From 7bbb4178229f049f83fe0582bbc741623d60c4a5 Mon Sep 17 00:00:00 2001 From: Andreas Fernandez <a.fernandez@scripting-base.de> Date: Sun, 6 Mar 2016 13:18:46 +0100 Subject: [PATCH] [TASK] The Number of The Workspace This change ports EXT:workspace to Bootstrap and jQuery. The workspace interface is simplified now, similar actions are tied together: - The record history is now part of ``getRowDetails()`` to get rid of the extra button and popup. - The "Send to stage" buttons are now also in the record information modal as separate buttons. The JavaScript has some wrapper methods to simplify the remaining ExtDirect calls. ExtDirectServer::getDifferenceHandler() now instantiates the DiffUtility and does not use the internal diff library directly anymore. Resolves: #74359 Releases: master Change-Id: Id706ae8a886f05aafeb402cdc2352068f1021dbe Reviewed-on: https://review.typo3.org/46573 Reviewed-by: Oliver Hader <oliver.hader@typo3.org> Tested-by: Oliver Hader <oliver.hader@typo3.org> Reviewed-by: Susanne Moog <typo3@susannemoog.de> Tested-by: Susanne Moog <typo3@susannemoog.de> --- Build/Gruntfile.js | 9 + .../Public/Less/TYPO3/_module_workspaces.less | 320 +---- .../t3skin/Resources/Public/Css/backend.css | 247 +--- .../Classes/Controller/AbstractController.php | 47 +- .../Classes/Controller/PreviewController.php | 232 ++-- .../Classes/Controller/ReviewController.php | 166 ++- .../Classes/ExtDirect/ActionHandler.php | 140 +- .../Classes/ExtDirect/ExtDirectServer.php | 144 +- .../Classes/Service/GridDataService.php | 2 + .../Classes/Service/HistoryService.php | 8 + .../Classes/Service/StagesService.php | 1 - .../Resources/Private/Language/locallang.xlf | 46 +- .../Resources/Private/Layouts/Empty.html | 1 + .../Resources/Private/Layouts/Module.html | 5 +- .../Resources/Private/Layouts/Nodoc.html | 9 + .../Resources/Private/Layouts/Popup.html | 17 - .../Resources/Private/Less/preview.less | 69 + .../Resources/Private/Partials/Legend.html | 4 +- .../Private/Partials/Navigation.html | 2 + .../Partials/Preview/StageButtons.html | 11 + .../Private/Partials/WorkingTable.html | 94 ++ .../Templates/Preview/Ajax/StageButtons.html | 3 + .../Private/Templates/Preview/Help.html | 4 +- .../Private/Templates/Preview/Index.html | 65 +- .../Private/Templates/Preview/NewPage.html | 2 + .../Private/Templates/Preview/Preview.html | 47 +- .../Private/Templates/Review/FullIndex.html | 8 +- .../Private/Templates/Review/Index.html | 29 +- .../Private/Templates/Review/SingleIndex.html | 8 +- .../Resources/Public/Css/module.css | 290 ---- .../Resources/Public/Css/preview.css | 389 +----- .../Resources/Public/JavaScript/Backend.js | 1191 +++++++++++++++++ .../JavaScript/Component/RowDetailTemplate.js | 10 - .../JavaScript/Component/RowExpander.js | 318 ----- .../Public/JavaScript/Component/TabPanel.js | 159 --- .../Ext.ux.plugins.TabStripContainer.js | 99 -- .../Resources/Public/JavaScript/Preview.js | 305 +++++ .../Public/JavaScript/Store/mainstore.js | 85 -- .../Resources/Public/JavaScript/Workspaces.js | 214 +++ .../Resources/Public/JavaScript/actions.js | 403 ------ .../Resources/Public/JavaScript/component.js | 44 - .../Public/JavaScript/configuration.js | 400 ------ .../Resources/Public/JavaScript/grid.js | 153 --- .../JavaScript/gridfilters/GridFilters.js | 740 ---------- .../gridfilters/css/GridFilters.css | 53 - .../JavaScript/gridfilters/css/RangeMenu.css | 20 - .../gridfilters/filter/BooleanFilter.js | 103 -- .../gridfilters/filter/DateFilter.js | 313 ----- .../JavaScript/gridfilters/filter/Filter.js | 185 --- .../gridfilters/filter/ListFilter.js | 176 --- .../gridfilters/filter/NumericFilter.js | 202 --- .../gridfilters/filter/StringFilter.js | 132 -- .../JavaScript/gridfilters/images/equals.png | Bin 106 -> 0 bytes .../JavaScript/gridfilters/images/find.png | Bin 638 -> 0 bytes .../gridfilters/images/greater_than.png | Bin 213 -> 0 bytes .../gridfilters/images/less_than.png | Bin 215 -> 0 bytes .../gridfilters/images/sort_filtered_asc.gif | Bin 209 -> 0 bytes .../gridfilters/images/sort_filtered_desc.gif | Bin 209 -> 0 bytes .../JavaScript/gridfilters/menu/ListMenu.js | 177 --- .../JavaScript/gridfilters/menu/RangeMenu.js | 128 -- .../Resources/Public/JavaScript/helpers.js | 212 --- .../Resources/Public/JavaScript/preview.js | 391 ------ .../Resources/Public/JavaScript/toolbar.js | 407 ------ .../Resources/Public/JavaScript/workspaces.js | 90 -- .../ActionHandler/ActionHandlerTest.php | 2 +- 65 files changed, 2560 insertions(+), 6571 deletions(-) create mode 100644 typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html create mode 100644 typo3/sysext/workspaces/Resources/Private/Less/preview.less create mode 100644 typo3/sysext/workspaces/Resources/Private/Partials/Preview/StageButtons.html create mode 100644 typo3/sysext/workspaces/Resources/Private/Partials/WorkingTable.html create mode 100644 typo3/sysext/workspaces/Resources/Private/Templates/Preview/Ajax/StageButtons.html delete mode 100644 typo3/sysext/workspaces/Resources/Public/Css/module.css create mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Backend.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowDetailTemplate.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowExpander.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Component/TabPanel.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Ext.ux.plugins.TabStripContainer.js create mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Preview.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Store/mainstore.js create mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/Workspaces.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/component.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/configuration.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/grid.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/GridFilters.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/GridFilters.css delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/RangeMenu.css delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/BooleanFilter.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/DateFilter.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/Filter.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/ListFilter.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/NumericFilter.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/StringFilter.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/equals.png delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/find.png delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/greater_than.png delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/less_than.png delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/sort_filtered_asc.gif delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/sort_filtered_desc.gif delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/ListMenu.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/RangeMenu.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/helpers.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/preview.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar.js delete mode 100644 typo3/sysext/workspaces/Resources/Public/JavaScript/workspaces.js diff --git a/Build/Gruntfile.js b/Build/Gruntfile.js index 7ee9654cf756..1dcf43a8b87f 100644 --- a/Build/Gruntfile.js +++ b/Build/Gruntfile.js @@ -39,6 +39,7 @@ module.exports = function(grunt) { install : '<%= paths.sysext %>install/Resources/', linkvalidator : '<%= paths.sysext %>linkvalidator/Resources/', backend : '<%= paths.sysext %>backend/Resources/', + workspaces: '<%= paths.sysext %>workspaces/Resources/', core : '<%= paths.sysext %>core/Resources/', flags : 'bower_components/region-flags/svg/', t3icons : 'bower_components/wmdbsystems-typo3-icons/dist/' @@ -77,6 +78,11 @@ module.exports = function(grunt) { files: { "<%= paths.linkvalidator %>Public/Css/linkvalidator.css": "<%= paths.less %>linkvalidator.less" } + }, + workspaces: { + files: { + "<%= paths.workspaces %>Public/Css/preview.css": "<%= paths.workspaces %>Private/Less/preview.less" + } } }, postcss: { @@ -109,6 +115,9 @@ module.exports = function(grunt) { }, linkvalidator: { src: '<%= paths.linkvalidator %>Public/Css/*.css' + }, + workspaces: { + src: '<%= paths.workspaces %>Public/Css/*.css' } }, watch: { diff --git a/Build/Resources/Public/Less/TYPO3/_module_workspaces.less b/Build/Resources/Public/Less/TYPO3/_module_workspaces.less index b79e023b96be..162ae0aa46b1 100644 --- a/Build/Resources/Public/Less/TYPO3/_module_workspaces.less +++ b/Build/Resources/Public/Less/TYPO3/_module_workspaces.less @@ -1,290 +1,54 @@ -ul.x-tab-strip.x-tab-strip-top { - margin-bottom: 0; - margin-top: 0; - padding-left: 0; -} -ul.x-tab-strip.x-tab-strip-top li.last { - float: right; -} -span.item-state-modified { - color: #f78f25; -} -span.item-state-moved { - color: #457fb8; -} -span.item-state-new { - color: #3c9934; -} -span.item-state-hidden { - color: #abaaaa; -} -span.item-state-deleted { - color: #000000; - text-decoration: line-through; -} -.legend { - margin: 5px; - height: 18px; - color: #888888; -} -.legend dd, .legend dt { - display: inline; - overflow: hidden; -} -.legend dd span { - display: inline-block; - padding: 4px 4px; -} -.x-toolbar { - background:none !important; -} -.x-grid3 { - background: none !important; -} -.x-grid3-row-expanded { - background-color: #ececec; -} -.x-grid3-row-selected { - color: #4D4D4D; -} -#typo3-mod-php div.typo3-noDoc { - margin: 0px 0px; -} -#typo3-mod-php div.typo3-noDoc #typo3-docbody { - padding: 0px 0px; - top: 0px; -} -.icon-hidden { - display: none; - visibility: hidden; -} -div.x-grid3-row img.x-action-col-icon { - display:none; -} +#workspace-panel { + tr.collapsing { + transition: none; + } + tr.collapse { + display: none; -div.x-grid3-row-over img.x-action-col-icon, -div.x-grid3-row img.x-action-col-icon.t3-visible { - display:inline-block; + &.in { + display: table-row; + } + } } -div.t3-workspaces-foldoutWrapper { - padding: 10px; - margin-left: 40px; - background-color: #FFFFFF; - background-image: linear-gradient(center top, #ececec 0px, #f7f7f7 200px); - background-repeat: repeat-x; - color: #606060; -} -.x-grid3-row-selected div.t3-workspaces-foldoutWrapper { - background-image: linear-gradient(center top, #dedede 0px, #f7f7f7 200px); - background-repeat: repeat-x; -} -div.t3-workspaces-foldoutWrapper table { - border-collapse: collapse; - width: 100%; -} -div.t3-workspaces-foldoutWrapper tr.header { - background-color: #B5B5B5; - background-image: linear-gradient(center top, #7f7f7f 10%, #5b5b5b 100%); - background-repeat: repeat-x; -} -div.t3-workspaces-foldoutWrapper tr.header th { - padding: 4px 8px; - color: #FFFFFF; - line-height: 15px; -} +span.item-state- { + &modified { + color: #f78f25; + } -.t3-workspaces-foldout-subheaderLeft, -.t3-workspaces-foldout-subheaderRight { - padding: 15px 0 10px 0; - margin: 10px; -} -.t3-workspaces-foldout-td-contentDiffLeft .t3-workspaces-foldout-contentDiff-container, -.t3-workspaces-foldout-td-contentDiffRight .t3-workspaces-foldout-contentDiff-container { - padding-top: 10px; - border-top: 1px solid #cdcdcd; -} -.x-grid3-row .t3-workspaces-foldout-td-contentDiffLeft, -.x-grid3-row .t3-workspaces-foldout-subheaderLeft { - padding-right: 10px; -} -.x-grid3-row .t3-workspaces-foldout-td-contentDiffRight, -.x-grid3-row .t3-workspaces-foldout-subheaderRight { - padding-left: 10px; -} -.x-grid3-row .t3-workspaces-foldout-subheaderLeft .t3-workspaces-foldout-subheader-container { - padding-bottom: 10px; - border-bottom: 1px solid #cdcdcd; -} -table.t3-workspaces-foldout-contentDiff { - padding: 8px 8px; - table-layout: auto; - background-color: #ffffff; -} -table.t3-workspaces-foldout-contentDiff th { - padding: 8px 0 8px 8px; - width: 80px; -} -table.t3-workspaces-foldout-contentDiff td { - padding: 8px 8px 8px 0; - line-height: 1.3em; -} -table.t3-workspaces-foldout-contentDiff .diff-r { - text-decoration: line-through; -} -.t3-workspaces-foldout-contentDiff .content ins > img { - padding: 1px; - margin-right: 2px; - border: 2px solid green; -} -.t3-workspaces-foldout-contentDiff .content del > img { - padding: 1px; - margin-right: 2px; - border: 2px solid red; -} -div.t3-workspaces-foldoutWrapper td.char_select_profile_stats { - padding-right: 10px; -} -div.t3-workspaces-comments { - background-color: #dedede; - padding: 10px 10px 10px 10px; -} -div.t3-workspaces-comments-singleComment { - overflow: hidden; - margin-bottom: 10px; - position: relative; -} -div.t3-workspaces-comments-singleComment:last-child { - margin: 0; -} -div .t3-workspaces-comments-singleComment-author { - width: 60px; - margin: 10px 0; - position: absolute; - left: 0px; - top: 0px; - font-weight: bold; - overflow: hidden; -} -div .t3-workspaces-comments-singleComment-content-wrapper { - background: url(../Images/workspaces-comments-arrow.gif) left 10px no-repeat; - margin-left: 70px; -} -div .t3-workspaces-comments-singleComment-content-date { - font-size: 10px; -} -div .t3-workspaces-comments-singleComment-content { - background-color: #ffffff; - padding: 10px 10px; - margin-left: 10px; -} -div .t3-workspaces-comments-singleComment-content-title { - padding: 8px 0 8px 0; - font-weight: bold; -} - -.typo3-workspaces-row-disabled .x-grid3-td-checker { - visibility: hidden; -} - -div.x-grid3-row img.t3-icon-extensions-workspaces { - display: inline-block !important; - width: 17px; - visibility: hidden; -} - -div.x-grid3-row-over img.t3-icon-extensions-workspaces { - visibility: visible; -} - -img.t3-icon-workspaces-sendtonextstage { - background: url(../Images/version-workspace-sendtonextstage.png) no-repeat; -} + &moved { + color: #457fb8; + } -img.t3-icon-workspaces-sendtoprevstage { - background: url(../Images/version-workspace-sendtoprevstage.png) no-repeat; -} + &new { + color: #3c9934; + } -table.t3-workspaces-foldout-contentDiff td.content { - word-break: break-all; -} + &hidden { + color: #abaaaa; + } -.typo3-workspaces-collection-level-node, -.typo3-workspaces-collection-level-leaf, -.typo3-workspaces-collection-level-none { - float: left; - width: 18px; + &deleted { + color: #000000; + text-decoration: line-through; + } } -.x-grid3-row-expander { - background-position: left top; - float: left; -} -.x-grid3-row-collapsed .x-grid3-row-expander { - background-position: left top; - background-image: url('../Images/zoom_in.png'); -} -.x-grid3-row-expanded .x-grid3-row-expander { - background-position: left top; - background-image: url('../Images/zoom_out.png'); -} - -.typo3-workspaces-collection-child-collapsed { - display: none; -} - -.typo3-workspaces-collection-child-expanded { - display: block; -} - -.typo3-workspaces-collection-level-node { - width: 18px; +.legend { + margin: 5px; height: 18px; - background-position: 4px 2px; - background-color: transparent; - background-repeat: no-repeat; - background-image: url('../../../../t3skin/extjs/images/grid/row-expand-sprite.png'); -} -.typo3-workspaces-collection-parent-collapsed .typo3-workspaces-collection-level-node { - background-position: 4px 2px; -} -.typo3-workspaces-collection-parent-expanded .typo3-workspaces-collection-level-node { - background-position: -21px 2px; -} - -.x-menu.typo3-workspaces-menu { - background-image: none; -} -.x-menu.typo3-workspaces-menu a.x-menu-item { - padding: 3px 14px; -} - -.x-tab-menu-right { - background: #dadada; - border-color: #adadad; - border-style: solid; - border-width: 1px; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - color: #666666; - background-position: center 6px; - padding: 4px; - - background-image: url('../Images/menu.png'); - background-repeat: no-repeat; - width: 16px; - position: absolute; - right: 0; - top: 0; - z-index: 10; - cursor:pointer; -} - -.t3-workspaces-foldoutWrapper .char_select_profile_titleLeft .icon, -.t3-workspaces-foldoutWrapper .char_select_profile_titleRight .icon{ - float: left; -} + color: #888888; -.t3-workspaces-foldoutWrapper .t3-workspaces-foldout-contentDiff { - text-align: left; + dd, + dt { + display: inline; + overflow: hidden; + } + + dd { + span { + display: inline-block; + padding: 4px 4px; + } + } } diff --git a/typo3/sysext/t3skin/Resources/Public/Css/backend.css b/typo3/sysext/t3skin/Resources/Public/Css/backend.css index 539d833f6e27..bd5af74d9784 100644 --- a/typo3/sysext/t3skin/Resources/Public/Css/backend.css +++ b/typo3/sysext/t3skin/Resources/Public/Css/backend.css @@ -12621,13 +12621,14 @@ iframe { background-position: -80px -224px; border-width: 0; } -ul.x-tab-strip.x-tab-strip-top { - margin-bottom: 0; - margin-top: 0; - padding-left: 0; +#workspace-panel tr.collapsing { + transition: none; } -ul.x-tab-strip.x-tab-strip-top li.last { - float: right; +#workspace-panel tr.collapse { + display: none; +} +#workspace-panel tr.collapse.in { + display: table-row; } span.item-state-modified { color: #f78f25; @@ -12659,240 +12660,6 @@ span.item-state-deleted { display: inline-block; padding: 4px 4px; } -.x-toolbar { - background: none !important; -} -.x-grid3 { - background: none !important; -} -.x-grid3-row-expanded { - background-color: #ececec; -} -.x-grid3-row-selected { - color: #4D4D4D; -} -#typo3-mod-php div.typo3-noDoc { - margin: 0px 0px; -} -#typo3-mod-php div.typo3-noDoc #typo3-docbody { - padding: 0px 0px; - top: 0px; -} -.icon-hidden { - display: none; - visibility: hidden; -} -div.x-grid3-row img.x-action-col-icon { - display: none; -} -div.x-grid3-row-over img.x-action-col-icon, -div.x-grid3-row img.x-action-col-icon.t3-visible { - display: inline-block; -} -div.t3-workspaces-foldoutWrapper { - padding: 10px; - margin-left: 40px; - background-color: #FFFFFF; - background-image: linear-gradient(center top, #ececec 0px, #f7f7f7 200px); - background-repeat: repeat-x; - color: #606060; -} -.x-grid3-row-selected div.t3-workspaces-foldoutWrapper { - background-image: linear-gradient(center top, #dedede 0px, #f7f7f7 200px); - background-repeat: repeat-x; -} -div.t3-workspaces-foldoutWrapper table { - border-collapse: collapse; - width: 100%; -} -div.t3-workspaces-foldoutWrapper tr.header { - background-color: #B5B5B5; - background-image: linear-gradient(center top, #7f7f7f 10%, #5b5b5b 100%); - background-repeat: repeat-x; -} -div.t3-workspaces-foldoutWrapper tr.header th { - padding: 4px 8px; - color: #FFFFFF; - line-height: 15px; -} -.t3-workspaces-foldout-subheaderLeft, -.t3-workspaces-foldout-subheaderRight { - padding: 15px 0 10px 0; - margin: 10px; -} -.t3-workspaces-foldout-td-contentDiffLeft .t3-workspaces-foldout-contentDiff-container, -.t3-workspaces-foldout-td-contentDiffRight .t3-workspaces-foldout-contentDiff-container { - padding-top: 10px; - border-top: 1px solid #cdcdcd; -} -.x-grid3-row .t3-workspaces-foldout-td-contentDiffLeft, -.x-grid3-row .t3-workspaces-foldout-subheaderLeft { - padding-right: 10px; -} -.x-grid3-row .t3-workspaces-foldout-td-contentDiffRight, -.x-grid3-row .t3-workspaces-foldout-subheaderRight { - padding-left: 10px; -} -.x-grid3-row .t3-workspaces-foldout-subheaderLeft .t3-workspaces-foldout-subheader-container { - padding-bottom: 10px; - border-bottom: 1px solid #cdcdcd; -} -table.t3-workspaces-foldout-contentDiff { - padding: 8px 8px; - table-layout: auto; - background-color: #ffffff; -} -table.t3-workspaces-foldout-contentDiff th { - padding: 8px 0 8px 8px; - width: 80px; -} -table.t3-workspaces-foldout-contentDiff td { - padding: 8px 8px 8px 0; - line-height: 1.3em; -} -table.t3-workspaces-foldout-contentDiff .diff-r { - text-decoration: line-through; -} -.t3-workspaces-foldout-contentDiff .content ins > img { - padding: 1px; - margin-right: 2px; - border: 2px solid green; -} -.t3-workspaces-foldout-contentDiff .content del > img { - padding: 1px; - margin-right: 2px; - border: 2px solid red; -} -div.t3-workspaces-foldoutWrapper td.char_select_profile_stats { - padding-right: 10px; -} -div.t3-workspaces-comments { - background-color: #dedede; - padding: 10px 10px 10px 10px; -} -div.t3-workspaces-comments-singleComment { - overflow: hidden; - margin-bottom: 10px; - position: relative; -} -div.t3-workspaces-comments-singleComment:last-child { - margin: 0; -} -div .t3-workspaces-comments-singleComment-author { - width: 60px; - margin: 10px 0; - position: absolute; - left: 0px; - top: 0px; - font-weight: bold; - overflow: hidden; -} -div .t3-workspaces-comments-singleComment-content-wrapper { - background: url(../Images/workspaces-comments-arrow.gif) left 10px no-repeat; - margin-left: 70px; -} -div .t3-workspaces-comments-singleComment-content-date { - font-size: 10px; -} -div .t3-workspaces-comments-singleComment-content { - background-color: #ffffff; - padding: 10px 10px; - margin-left: 10px; -} -div .t3-workspaces-comments-singleComment-content-title { - padding: 8px 0 8px 0; - font-weight: bold; -} -.typo3-workspaces-row-disabled .x-grid3-td-checker { - visibility: hidden; -} -div.x-grid3-row img.t3-icon-extensions-workspaces { - display: inline-block !important; - width: 17px; - visibility: hidden; -} -div.x-grid3-row-over img.t3-icon-extensions-workspaces { - visibility: visible; -} -img.t3-icon-workspaces-sendtonextstage { - background: url(../Images/version-workspace-sendtonextstage.png) no-repeat; -} -img.t3-icon-workspaces-sendtoprevstage { - background: url(../Images/version-workspace-sendtoprevstage.png) no-repeat; -} -table.t3-workspaces-foldout-contentDiff td.content { - word-break: break-all; -} -.typo3-workspaces-collection-level-node, -.typo3-workspaces-collection-level-leaf, -.typo3-workspaces-collection-level-none { - float: left; - width: 18px; -} -.x-grid3-row-expander { - background-position: left top; - float: left; -} -.x-grid3-row-collapsed .x-grid3-row-expander { - background-position: left top; - background-image: url('../Images/zoom_in.png'); -} -.x-grid3-row-expanded .x-grid3-row-expander { - background-position: left top; - background-image: url('../Images/zoom_out.png'); -} -.typo3-workspaces-collection-child-collapsed { - display: none; -} -.typo3-workspaces-collection-child-expanded { - display: block; -} -.typo3-workspaces-collection-level-node { - width: 18px; - height: 18px; - background-position: 4px 2px; - background-color: transparent; - background-repeat: no-repeat; - background-image: url('../../../../t3skin/extjs/images/grid/row-expand-sprite.png'); -} -.typo3-workspaces-collection-parent-collapsed .typo3-workspaces-collection-level-node { - background-position: 4px 2px; -} -.typo3-workspaces-collection-parent-expanded .typo3-workspaces-collection-level-node { - background-position: -21px 2px; -} -.x-menu.typo3-workspaces-menu { - background-image: none; -} -.x-menu.typo3-workspaces-menu a.x-menu-item { - padding: 3px 14px; -} -.x-tab-menu-right { - background: #dadada; - border-color: #adadad; - border-style: solid; - border-width: 1px; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - color: #666666; - background-position: center 6px; - padding: 4px; - background-image: url('../Images/menu.png'); - background-repeat: no-repeat; - width: 16px; - position: absolute; - right: 0; - top: 0; - z-index: 10; - cursor: pointer; -} -.t3-workspaces-foldoutWrapper .char_select_profile_titleLeft .icon, -.t3-workspaces-foldoutWrapper .char_select_profile_titleRight .icon { - float: left; -} -.t3-workspaces-foldoutWrapper .t3-workspaces-foldout-contentDiff { - text-align: left; -} [id="typo3-topbar"], [id="typo3-topbar"] .x-panel-body { min-width: 1000px; diff --git a/typo3/sysext/workspaces/Classes/Controller/AbstractController.php b/typo3/sysext/workspaces/Classes/Controller/AbstractController.php index 6235cee61c35..ad173223ed9e 100644 --- a/typo3/sysext/workspaces/Classes/Controller/AbstractController.php +++ b/typo3/sysext/workspaces/Classes/Controller/AbstractController.php @@ -19,12 +19,14 @@ use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\PathUtility; +use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; +use TYPO3\CMS\Workspaces\Service\AdditionalColumnService; +use TYPO3\CMS\Workspaces\Service\AdditionalResourceService; /** * Abstract action controller. */ -class AbstractController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController +class AbstractController extends ActionController { /** * @var string @@ -62,6 +64,7 @@ class AbstractController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControl // @todo Evaluate how the intval() call can be used with Extbase validators/filters $this->pageId = (int)GeneralUtility::_GP('id'); $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $lang = $this->getLanguageService(); $icons = array( 'language' => $iconFactory->getIcon('flags-multiple', Icon::SIZE_SMALL)->render(), 'integrity' => $iconFactory->getIcon('status-dialog-information', Icon::SIZE_SMALL)->render(), @@ -74,18 +77,18 @@ class AbstractController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControl $this->pageRenderer->addInlineSetting('Workspaces', 'id', $this->pageId); $this->pageRenderer->addInlineSetting('Workspaces', 'depth', $this->pageId === 0 ? 999 : 1); $this->pageRenderer->addInlineSetting('Workspaces', 'language', $this->getLanguageSelection()); - $this->pageRenderer->addInlineLanguageLabelArray(array( - 'title' => $GLOBALS['LANG']->getLL('title'), - 'path' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.path'), - 'table' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.table'), - 'depth' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_mod_web_perm.xlf:Depth'), - 'depth_0' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_0'), - 'depth_1' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_1'), - 'depth_2' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_2'), - 'depth_3' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_3'), - 'depth_4' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_4'), - 'depth_infi' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_infi') - )); + $this->pageRenderer->addInlineLanguageLabelArray([ + 'title' => $lang->getLL('title'), + 'path' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.path'), + 'table' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.table'), + 'depth' => $lang->sL('LLL:EXT:lang/locallang_mod_web_perm.xlf:Depth'), + 'depth_0' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_0'), + 'depth_1' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_1'), + 'depth_2' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_2'), + 'depth_3' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_3'), + 'depth_4' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_4'), + 'depth_infi' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_infi') + ]); $this->pageRenderer->addInlineLanguageLabelFile('EXT:workspaces/Resources/Private/Language/locallang.xlf'); $this->assignExtensionSettings(); } @@ -125,19 +128,19 @@ class AbstractController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControl } /** - * @return \TYPO3\CMS\Workspaces\Service\AdditionalColumnService + * @return AdditionalColumnService */ protected function getAdditionalColumnService() { - return $this->objectManager->get(\TYPO3\CMS\Workspaces\Service\AdditionalColumnService::class); + return $this->objectManager->get(AdditionalColumnService::class); } /** - * @return \TYPO3\CMS\Workspaces\Service\AdditionalResourceService + * @return AdditionalResourceService */ protected function getAdditionalResourceService() { - return $this->objectManager->get(\TYPO3\CMS\Workspaces\Service\AdditionalResourceService::class); + return $this->objectManager->get(AdditionalResourceService::class); } /** @@ -148,6 +151,14 @@ class AbstractController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControl return $GLOBALS['BE_USER']; } + /** + * @return \TYPO3\CMS\Lang\LanguageService + */ + protected function getLanguageService() + { + return $GLOBALS['LANG']; + } + /** * @return PageRenderer */ diff --git a/typo3/sysext/workspaces/Classes/Controller/PreviewController.php b/typo3/sysext/workspaces/Classes/Controller/PreviewController.php index 41e668535209..5885fa5cee64 100644 --- a/typo3/sysext/workspaces/Classes/Controller/PreviewController.php +++ b/typo3/sysext/workspaces/Classes/Controller/PreviewController.php @@ -16,9 +16,16 @@ namespace TYPO3\CMS\Workspaces\Controller; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Backend\View\BackendTemplateView; +use TYPO3\CMS\Core\FormProtection\FormProtectionFactory; +use TYPO3\CMS\Core\Messaging\FlashMessage; +use TYPO3\CMS\Core\Messaging\FlashMessageService; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; +use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; +use TYPO3\CMS\Workspaces\Service\StagesService; +use TYPO3\CMS\Workspaces\Service\WorkspaceService; /** * Implements the preview controller of the workspace module. @@ -26,12 +33,12 @@ use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; class PreviewController extends AbstractController { /** - * @var \TYPO3\CMS\Workspaces\Service\StagesService + * @var StagesService */ protected $stageService; /** - * @var \TYPO3\CMS\Workspaces\Service\WorkspaceService + * @var WorkspaceService */ protected $workspaceService; @@ -59,32 +66,17 @@ class PreviewController extends AbstractController { parent::initializeAction(); $backendRelPath = ExtensionManagementUtility::extRelPath('backend'); - $workspacesRelPath = ExtensionManagementUtility::extRelPath('workspaces'); - $this->stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); - $this->workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + $this->stageService = GeneralUtility::makeInstance(StagesService::class); + $this->workspaceService = GeneralUtility::makeInstance(WorkspaceService::class); $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/ExtDirect.StateProvider.js'); - $resourcePath = $workspacesRelPath . 'Resources/Public/Css/preview.css'; - $GLOBALS['TBE_STYLES']['extJS']['theme'] = $resourcePath; - $this->pageRenderer->loadExtJS(); + $this->pageRenderer->loadExtJS(false, false); // Load JavaScript: $this->pageRenderer->addExtDirectCode(array( 'TYPO3.Workspaces', 'TYPO3.ExtDirectStateProvider' )); - $states = $GLOBALS['BE_USER']->uc['moduleData']['Workspaces']['States']; + $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States']; $this->pageRenderer->addInlineSetting('Workspaces', 'States', $states); - $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/notifications.js'); - $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/iframepanel.js'); - $resourcePathJavaScript = $workspacesRelPath . 'Resources/Public/JavaScript/'; - $jsFiles = array( - 'Ext.ux.plugins.TabStripContainer.js', - 'Store/mainstore.js', - 'helpers.js', - 'actions.js' - ); - foreach ($jsFiles as $jsFile) { - $this->pageRenderer->addJsFile($resourcePathJavaScript . $jsFile); - } $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', BackendUtility::getModuleUrl('record_edit')); $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', BackendUtility::getModuleUrl('record_history')); // @todo this part should be done with inlineLocallanglabels @@ -101,81 +93,103 @@ class PreviewController extends AbstractController */ public function indexAction($previewWS = null) { + $backendUser = $this->getBackendUser(); + // Get all the GET parameters to pass them on to the frames $queryParameters = GeneralUtility::_GET(); - // Remove the GET parameters related to the workspaces module and the page id + + // Remove the GET parameters related to the workspaces module and the page id unset($queryParameters['tx_workspaces_web_workspacesworkspaces']); unset($queryParameters['M']); unset($queryParameters['id']); - // Assemble a query string from the retrieved parameters + + // Assemble a query string from the retrieved parameters $queryString = GeneralUtility::implodeArrayForUrl('', $queryParameters); // fetch the next and previous stage $workspaceItemsArray = $this->workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $this->pageId, ($recursionLevel = 0), ($selectionType = 'tables_modify')); list(, $nextStage) = $this->stageService->getNextStageForElementCollection($workspaceItemsArray); list(, $previousStage) = $this->stageService->getPreviousStageForElementCollection($workspaceItemsArray); - /** @var $wsService \TYPO3\CMS\Workspaces\Service\WorkspaceService */ - $wsService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + /** @var $wsService WorkspaceService */ + $wsService = GeneralUtility::makeInstance(WorkspaceService::class); $wsList = $wsService->getAvailableWorkspaces(); - $activeWorkspace = $GLOBALS['BE_USER']->workspace; + $activeWorkspace = $backendUser->workspace; if (!is_null($previewWS)) { if (in_array($previewWS, array_keys($wsList)) && $activeWorkspace != $previewWS) { $activeWorkspace = $previewWS; - $GLOBALS['BE_USER']->setWorkspace($activeWorkspace); + $backendUser->setWorkspace($activeWorkspace); BackendUtility::setUpdateSignal('updatePageTree'); } } - /** @var $uriBuilder \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder */ - $uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class); + /** @var $uriBuilder UriBuilder */ + $uriBuilder = $this->objectManager->get(UriBuilder::class); $wsSettingsPath = GeneralUtility::getIndpEnv('TYPO3_SITE_URL'); - $wsSettingsUri = $uriBuilder->uriFor('singleIndex', array(), \TYPO3\CMS\Workspaces\Controller\ReviewController::class, 'workspaces', 'web_workspacesworkspaces'); + $wsSettingsUri = $uriBuilder->uriFor('singleIndex', array(), ReviewController::class, 'workspaces', 'web_workspacesworkspaces'); $wsSettingsParams = '&tx_workspaces_web_workspacesworkspaces[controller]=Review'; $wsSettingsUrl = $wsSettingsPath . $wsSettingsUri . $wsSettingsParams; $viewDomain = BackendUtility::getViewDomain($this->pageId); $wsBaseUrl = $viewDomain . '/index.php?id=' . $this->pageId . $queryString; // @todo - handle new pages here // branchpoints are not handled anymore because this feature is not supposed anymore - if (\TYPO3\CMS\Workspaces\Service\WorkspaceService::isNewPage($this->pageId)) { - $wsNewPageUri = $uriBuilder->uriFor('newPage', array(), \TYPO3\CMS\Workspaces\Controller\PreviewController::class, 'workspaces', 'web_workspacesworkspaces'); + if (WorkspaceService::isNewPage($this->pageId)) { + $wsNewPageUri = $uriBuilder->uriFor('newPage', array(), PreviewController::class, 'workspaces', 'web_workspacesworkspaces'); $wsNewPageParams = '&tx_workspaces_web_workspacesworkspaces[controller]=Preview'; - $this->view->assign('liveUrl', $wsSettingsPath . $wsNewPageUri . $wsNewPageParams . '&ADMCMD_prev=IGNORE'); + $liveUrl = $wsSettingsPath . $wsNewPageUri . $wsNewPageParams . '&ADMCMD_prev=IGNORE'; } else { - $this->view->assign('liveUrl', $wsBaseUrl . '&ADMCMD_noBeUser=1&ADMCMD_prev=IGNORE'); + $liveUrl = $wsBaseUrl . '&ADMCMD_noBeUser=1&ADMCMD_prev=IGNORE'; } - $this->view->assign('wsUrl', $wsBaseUrl . '&ADMCMD_prev=IGNORE&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS=' . $GLOBALS['BE_USER']->workspace); - $this->view->assign('wsSettingsUrl', $wsSettingsUrl); - $this->view->assign('backendDomain', GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY')); + $wsUrl = $wsBaseUrl . '&ADMCMD_prev=IGNORE&ADMCMD_view=1&ADMCMD_editIcons=1&ADMCMD_previewWS=' . $backendUser->workspace; + $backendDomain = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'); $splitPreviewTsConfig = BackendUtility::getModTSconfig($this->pageId, 'workspaces.splitPreviewModes'); $splitPreviewModes = GeneralUtility::trimExplode(',', $splitPreviewTsConfig['value']); $allPreviewModes = array('slider', 'vbox', 'hbox'); if (!array_intersect($splitPreviewModes, $allPreviewModes)) { $splitPreviewModes = $allPreviewModes; } + + $wsList = $wsService->getAvailableWorkspaces(); + $activeWorkspace = $backendUser->workspace; + + $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Workspaces/Preview'); $this->pageRenderer->addInlineSetting('Workspaces', 'SplitPreviewModes', $splitPreviewModes); - $GLOBALS['BE_USER']->setAndSaveSessionData('workspaces.backend_domain', GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY')); - $this->pageRenderer->addInlineSetting('Workspaces', 'disableNextStageButton', $this->isInvalidStage($nextStage)); - $this->pageRenderer->addInlineSetting('Workspaces', 'disablePreviousStageButton', $this->isInvalidStage($previousStage)); - $this->pageRenderer->addInlineSetting('Workspaces', 'disableDiscardStageButton', $this->isInvalidStage($nextStage) && $this->isInvalidStage($previousStage)); - $resourcePath = ExtensionManagementUtility::extRelPath('lang') . 'Resources/Public/JavaScript/'; - $this->pageRenderer->addJsFile($resourcePath . 'Typo3Lang.js'); - $this->pageRenderer->addJsInlineCode('workspaces.preview.lll', ' - TYPO3.lang = { - visualPreview: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.visualPreview', true)) . ', - listView: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.listView', true)) . ', - livePreview: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.livePreview', true)) . ', - livePreviewDetail: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.livePreviewDetail', true)) . ', - workspacePreview: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.workspacePreview', true)) . ', - workspacePreviewDetail: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.workspacePreviewDetail', true)) . ', - modeSlider: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.modeSlider', true)) . ', - modeVbox: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.modeVbox', true)) . ', - modeHbox: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:preview.modeHbox', true)) . ', - discard: ' . GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_discard', true)) . ', - nextStage: ' . GeneralUtility::quoteJSvalue($nextStage['title']) . ', - previousStage: ' . GeneralUtility::quoteJSvalue($previousStage['title']) . ' - };TYPO3.l10n.initialize(); -'); - $resourcePath = ExtensionManagementUtility::extRelPath('workspaces') . 'Resources/Public/'; - $this->pageRenderer->addJsFile($resourcePath . 'JavaScript/preview.js'); + $this->pageRenderer->addInlineSetting('Workspaces', 'token', FormProtectionFactory::get('backend')->generateToken('extDirect')); + + $cssFile = 'EXT:workspaces/Resources/Public/Css/preview.css'; + $cssFile = GeneralUtility::getFileAbsFileName($cssFile); + $this->pageRenderer->addCssFile(PathUtility::getAbsoluteWebPath($cssFile)); + + $backendUser->setAndSaveSessionData('workspaces.backend_domain', GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY')); + + $logoPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/typo3-topbar@2x.png'); + list($logoWidth, $logoHeight) = @getimagesize($logoPath); + + // High-resolution? + $logoWidth = $logoWidth/2; + $logoHeight = $logoHeight/2; + + $this->view->assignMultiple([ + 'logoUrl' => PathUtility::getAbsoluteWebPath($logoPath), + 'logoLink' => TYPO3_URL_GENERAL, + 'logoWidth' => $logoWidth, + 'logoHeight' => $logoHeight, + 'liveUrl' => $liveUrl, + 'wsUrl' => $wsUrl, + 'wsSettingsUrl' => $wsSettingsUrl, + 'backendDomain' => $backendDomain, + 'activeWorkspace' => $wsList[$activeWorkspace], + 'splitPreviewModes' => $splitPreviewModes, + 'firstPreviewMode' => current($splitPreviewModes), + 'enablePreviousStageButton' => !$this->isInvalidStage($previousStage), + 'enableNextStageButton' => !$this->isInvalidStage($nextStage), + 'enableDiscardStageButton' => !$this->isInvalidStage($nextStage) || !$this->isInvalidStage($previousStage), + 'nextStage' => $nextStage['title'], + 'nextStageId' => $nextStage['uid'], + 'prevStage' => $previousStage['title'], + 'prevStageId' => $previousStage['uid'], + ]); + foreach ($this->getAdditionalResourceService()->getLocalizationResources() as $localizationResource) { + $this->pageRenderer->addInlineLanguageLabelFile($localizationResource); + } } /** @@ -194,9 +208,10 @@ class PreviewController extends AbstractController */ public function newPageAction() { - $flashMessage = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessage::class, $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.newpage.detail'), $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.newpage'), \TYPO3\CMS\Core\Messaging\FlashMessage::INFO); - /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */ - $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class); + /** @var FlashMessage $flashMessage */ + $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.newpage.detail'), $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:info.newpage'), FlashMessage::INFO); + /** @var $flashMessageService FlashMessageService */ + $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */ $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); @@ -211,58 +226,59 @@ class PreviewController extends AbstractController */ protected function generateJavascript() { + $backendUser = $this->getBackendUser(); + $lang = $this->getLanguageService(); // If another page module was specified, replace the default Page module with the new one - $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule')); + $newPageModule = trim($backendUser->getTSConfigVal('options.overridePageModule')); $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout'; - if (!$GLOBALS['BE_USER']->check('modules', $pageModule)) { + if (!$backendUser->check('modules', $pageModule)) { $pageModule = ''; } $t3Configuration = array( 'siteUrl' => GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), - 'username' => htmlspecialchars($GLOBALS['BE_USER']->user['username']), + 'username' => htmlspecialchars($backendUser->user['username']), 'uniqueID' => GeneralUtility::shortMD5(uniqid('', true)), 'pageModule' => $pageModule, - 'inWorkspace' => $GLOBALS['BE_USER']->workspace !== 0, - 'workspaceFrontendPreviewEnabled' => $GLOBALS['BE_USER']->user['workspace_preview'] ? 1 : 0, - 'moduleMenuWidth' => $this->menuWidth - 1, + 'inWorkspace' => $backendUser->workspace !== 0, + 'workspaceFrontendPreviewEnabled' => $backendUser->user['workspace_preview'] ? 1 : 0, 'topBarHeight' => isset($GLOBALS['TBE_STYLES']['dims']['topFrameH']) ? (int)$GLOBALS['TBE_STYLES']['dims']['topFrameH'] : 30, 'showRefreshLoginPopup' => isset($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup']) ? (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] : false, - 'debugInWindow' => $GLOBALS['BE_USER']->uc['debugInWindow'] ? 1 : 0, + 'debugInWindow' => $backendUser->uc['debugInWindow'] ? 1 : 0, 'ContextHelpWindows' => array( 'width' => 600, 'height' => 400 ) ); $t3LLLcore = array( - 'waitTitle' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_logging_in'), - 'refresh_login_failed' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed'), - 'refresh_login_failed_message' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed_message'), - 'refresh_login_title' => sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_title'), htmlspecialchars($GLOBALS['BE_USER']->user['username'])), - 'login_expired' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_expired'), - 'refresh_login_username' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_username'), - 'refresh_login_password' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_password'), - 'refresh_login_emptyPassword' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_emptyPassword'), - 'refresh_login_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_button'), - 'refresh_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_logout_button'), - 'refresh_exit_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_exit_button'), - 'please_wait' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.please_wait'), - 'loadingIndicator' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:loadingIndicator'), - 'be_locked' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.be_locked'), - 'refresh_login_countdown_singular' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown_singular'), - 'refresh_login_countdown' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown'), - 'login_about_to_expire' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire'), - 'login_about_to_expire_title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire_title'), - 'refresh_login_refresh_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_refresh_button'), - 'refresh_direct_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_direct_logout_button'), - 'tabs_closeAll' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeAll'), - 'tabs_closeOther' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeOther'), - 'tabs_close' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.close'), - 'tabs_openInBrowserWindow' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.openInBrowserWindow'), - 'donateWindow_title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.title'), - 'donateWindow_message' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.message'), - 'donateWindow_button_donate' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_donate'), - 'donateWindow_button_disable' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_disable'), - 'donateWindow_button_postpone' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_postpone') + 'waitTitle' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_logging_in'), + 'refresh_login_failed' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed'), + 'refresh_login_failed_message' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed_message'), + 'refresh_login_title' => sprintf($lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_title'), htmlspecialchars($backendUser->user['username'])), + 'login_expired' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_expired'), + 'refresh_login_username' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_username'), + 'refresh_login_password' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_password'), + 'refresh_login_emptyPassword' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_emptyPassword'), + 'refresh_login_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_button'), + 'refresh_logout_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_logout_button'), + 'refresh_exit_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_exit_button'), + 'please_wait' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.please_wait'), + 'loadingIndicator' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:loadingIndicator'), + 'be_locked' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.be_locked'), + 'refresh_login_countdown_singular' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown_singular'), + 'refresh_login_countdown' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown'), + 'login_about_to_expire' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire'), + 'login_about_to_expire_title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire_title'), + 'refresh_login_refresh_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_refresh_button'), + 'refresh_direct_logout_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_direct_logout_button'), + 'tabs_closeAll' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeAll'), + 'tabs_closeOther' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeOther'), + 'tabs_close' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.close'), + 'tabs_openInBrowserWindow' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.openInBrowserWindow'), + 'donateWindow_title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.title'), + 'donateWindow_message' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.message'), + 'donateWindow_button_donate' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_donate'), + 'donateWindow_button_disable' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_disable'), + 'donateWindow_button_postpone' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:donateWindow.button_postpone') ); return ' TYPO3.configuration = ' . json_encode($t3Configuration) . '; @@ -281,4 +297,20 @@ class PreviewController extends AbstractController //backwards compatibility '; } + + /** + * @return \TYPO3\CMS\Lang\LanguageService + */ + protected function getLanguageService() + { + return $GLOBALS['LANG']; + } + + /** + * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication + */ + protected function getBackendUser() + { + return $GLOBALS['BE_USER']; + } } diff --git a/typo3/sysext/workspaces/Classes/Controller/ReviewController.php b/typo3/sysext/workspaces/Classes/Controller/ReviewController.php index 3b6c3cb1b6d4..f93c2b341ef6 100644 --- a/typo3/sysext/workspaces/Classes/Controller/ReviewController.php +++ b/typo3/sysext/workspaces/Classes/Controller/ReviewController.php @@ -15,7 +15,11 @@ namespace TYPO3\CMS\Workspaces\Controller; */ use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\FormProtection\FormProtectionFactory; use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Messaging\FlashMessage; +use TYPO3\CMS\Core\Messaging\FlashMessageService; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; @@ -66,26 +70,25 @@ class ReviewController extends AbstractController */ public function indexAction() { + $backendUser = $this->getBackendUser(); + $moduleTemplate = $this->view->getModuleTemplate(); + /** @var WorkspaceService $wsService */ $wsService = GeneralUtility::makeInstance(WorkspaceService::class); - $this->view->assign('showGrid', !($GLOBALS['BE_USER']->workspace === 0 && !$GLOBALS['BE_USER']->isAdmin())); - $this->view->assign('showAllWorkspaceTab', true); - $this->view->assign('pageUid', GeneralUtility::_GP('id')); if (GeneralUtility::_GP('id')) { $pageRecord = BackendUtility::getRecord('pages', GeneralUtility::_GP('id')); if ($pageRecord) { - $this->view->getModuleTemplate()->getDocHeaderComponent()->setMetaInformation($pageRecord); + $moduleTemplate->getDocHeaderComponent()->setMetaInformation($pageRecord); $this->view->assign('pageTitle', BackendUtility::getRecordTitle('pages', $pageRecord)); } } - $this->view->assign('showLegend', !($GLOBALS['BE_USER']->workspace === 0 && !$GLOBALS['BE_USER']->isAdmin())); $wsList = $wsService->getAvailableWorkspaces(); - $activeWorkspace = $GLOBALS['BE_USER']->workspace; + $activeWorkspace = $backendUser->workspace; $performWorkspaceSwitch = false; // Only admins see multiple tabs, we decided to use it this // way for usability reasons. Regular users might be confused // by switching workspaces with the tabs in a module. - if (!$GLOBALS['BE_USER']->isAdmin()) { + if (!$backendUser->isAdmin()) { $wsCur = array($activeWorkspace => true); $wsList = array_intersect_key($wsList, $wsCur); } else { @@ -93,7 +96,7 @@ class ReviewController extends AbstractController $switchWs = (int)GeneralUtility::_GP('workspace'); if (in_array($switchWs, array_keys($wsList)) && $activeWorkspace != $switchWs) { $activeWorkspace = $switchWs; - $GLOBALS['BE_USER']->setWorkspace($activeWorkspace); + $backendUser->setWorkspace($activeWorkspace); $performWorkspaceSwitch = true; BackendUtility::setUpdateSignal('updatePageTree'); } elseif ($switchWs == WorkspaceService::SELECT_ALL_WORKSPACES) { @@ -101,26 +104,33 @@ class ReviewController extends AbstractController } } } - $this->pageRenderer->addInlineSetting('Workspaces', 'isLiveWorkspace', (int)$GLOBALS['BE_USER']->workspace === 0); + $this->pageRenderer->addInlineSetting('Workspaces', 'isLiveWorkspace', (int)$backendUser->workspace === 0); $this->pageRenderer->addInlineSetting('Workspaces', 'workspaceTabs', $this->prepareWorkspaceTabs($wsList, $activeWorkspace)); $this->pageRenderer->addInlineSetting('Workspaces', 'activeWorkspaceId', $activeWorkspace); $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', BackendUtility::getModuleUrl('record_edit')); - $this->view->assign('performWorkspaceSwitch', $performWorkspaceSwitch); - $this->view->assign('workspaceList', $wsList); - $this->view->assign('activeWorkspaceUid', $activeWorkspace); - $this->view->assign('activeWorkspaceTitle', WorkspaceService::getWorkspaceTitle($activeWorkspace)); + $workspaceIsAccessible = !($backendUser->workspace === 0 && !$backendUser->isAdmin()); + $this->view->assignMultiple([ + 'showGrid' => $workspaceIsAccessible, + 'showLegend' => $workspaceIsAccessible, + 'pageUid' => (int)GeneralUtility::_GP('id'), + 'performWorkspaceSwitch' => $performWorkspaceSwitch, + 'workspaceList' => $this->prepareWorkspaceTabs($wsList, $activeWorkspace), + 'activeWorkspaceUid' => $activeWorkspace, + 'activeWorkspaceTitle' => WorkspaceService::getWorkspaceTitle($activeWorkspace), + 'showPreviewLink' => $wsService->canCreatePreviewLink(GeneralUtility::_GP('id'), $activeWorkspace) + ]); + if ($wsService->canCreatePreviewLink(GeneralUtility::_GP('id'), $activeWorkspace)) { - $buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar(); - $iconFactory = $this->view->getModuleTemplate()->getIconFactory(); + $buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar(); + $iconFactory = $moduleTemplate->getIconFactory(); $showButton = $buttonBar->makeLinkButton() ->setHref('#') - ->setOnClick('TYPO3.Workspaces.Actions.generateWorkspacePreviewLinksForAllLanguages();return false;') + ->setClasses('t3js-preview-link') ->setTitle($this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:tooltip.generatePagePreview')) ->setIcon($iconFactory->getIcon('module-workspaces-action-preview-link', Icon::SIZE_SMALL)); $buttonBar->addButton($showButton); } - $this->view->assign('showPreviewLink', $wsService->canCreatePreviewLink(GeneralUtility::_GP('id'), $activeWorkspace)); - $GLOBALS['BE_USER']->setAndSaveSessionData('tx_workspace_activeWorkspace', $activeWorkspace); + $backendUser->setAndSaveSessionData('tx_workspace_activeWorkspace', $activeWorkspace); } /** @@ -131,25 +141,26 @@ class ReviewController extends AbstractController */ public function fullIndexAction() { - $wsService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + $wsService = GeneralUtility::makeInstance(WorkspaceService::class); $wsList = $wsService->getAvailableWorkspaces(); - if (!$GLOBALS['BE_USER']->isAdmin()) { - $activeWorkspace = $GLOBALS['BE_USER']->workspace; + $activeWorkspace = $this->getBackendUser()->workspace; + if (!$this->getBackendUser()->isAdmin()) { $wsCur = array($activeWorkspace => true); $wsList = array_intersect_key($wsList, $wsCur); } $this->pageRenderer->addInlineSetting('Workspaces', 'workspaceTabs', $this->prepareWorkspaceTabs($wsList, WorkspaceService::SELECT_ALL_WORKSPACES)); $this->pageRenderer->addInlineSetting('Workspaces', 'activeWorkspaceId', WorkspaceService::SELECT_ALL_WORKSPACES); - $this->view->assign('pageUid', GeneralUtility::_GP('id')); - $this->view->assign('showGrid', true); - $this->view->assign('showLegend', true); - $this->view->assign('showAllWorkspaceTab', true); - $this->view->assign('workspaceList', $wsList); - $this->view->assign('activeWorkspaceUid', WorkspaceService::SELECT_ALL_WORKSPACES); - $this->view->assign('showPreviewLink', false); - $GLOBALS['BE_USER']->setAndSaveSessionData('tx_workspace_activeWorkspace', WorkspaceService::SELECT_ALL_WORKSPACES); + $this->view->assignMultiple([ + 'pageUid' => (int)GeneralUtility::_GP('id'), + 'showGrid' => true, + 'showLegend' => true, + 'workspaceList' => $this->prepareWorkspaceTabs($wsList, $activeWorkspace), + 'activeWorkspaceUid' => WorkspaceService::SELECT_ALL_WORKSPACES, + 'showPreviewLink', false + ]); + $this->getBackendUser()->setAndSaveSessionData('tx_workspace_activeWorkspace', WorkspaceService::SELECT_ALL_WORKSPACES); // set flag for javascript $this->pageRenderer->addInlineSetting('Workspaces', 'allView', '1'); } @@ -162,17 +173,19 @@ class ReviewController extends AbstractController */ public function singleIndexAction() { - $wsService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + $wsService = GeneralUtility::makeInstance(WorkspaceService::class); $wsList = $wsService->getAvailableWorkspaces(); - $activeWorkspace = $GLOBALS['BE_USER']->workspace; + $activeWorkspace = $this->getBackendUser()->workspace; $wsCur = array($activeWorkspace => true); $wsList = array_intersect_key($wsList, $wsCur); $backendDomain = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'); - $this->view->assign('pageUid', GeneralUtility::_GP('id')); - $this->view->assign('showGrid', true); - $this->view->assign('showAllWorkspaceTab', false); - $this->view->assign('workspaceList', $wsList); - $this->view->assign('backendDomain', $backendDomain); + $this->view->assignMultiple([ + 'pageUid' => (int)GeneralUtility::_GP('id'), + 'showGrid' => true, + 'workspaceList' => $this->prepareWorkspaceTabs($wsList, $activeWorkspace, false), + 'activeWorkspaceUid' => $activeWorkspace, + 'backendDomain' => $backendDomain + ]); // Setting the document.domain early before JavScript // libraries are loaded, try to access top frame reference // and possibly run into some CORS issue @@ -194,68 +207,30 @@ class ReviewController extends AbstractController $backendRelPath = ExtensionManagementUtility::extRelPath('backend'); $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/ExtDirect.StateProvider.js'); if (WorkspaceService::isOldStyleWorkspaceUsed()) { - $flashMessage = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessage::class, $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:warning.oldStyleWorkspaceInUser'), '', \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING); - /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */ - $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class); + /** @var FlashMessage $flashMessage */ + $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $this->getLanguageService()->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:warning.oldStyleWorkspaceInUser'), '', FlashMessage::WARNING); + /** @var $flashMessageService FlashMessageService */ + $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */ $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); $defaultFlashMessageQueue->enqueue($flashMessage); } - $this->pageRenderer->loadExtJS(); - $states = $GLOBALS['BE_USER']->uc['moduleData']['Workspaces']['States']; + $this->pageRenderer->loadExtJS(false, false); + $states = $this->getBackendUser()->uc['moduleData']['Workspaces']['States']; $this->pageRenderer->addInlineSetting('Workspaces', 'States', $states); // Load JavaScript: $this->pageRenderer->addExtDirectCode(array( 'TYPO3.Workspaces' )); - $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/extjs/ux/Ext.grid.RowExpander.js'); - $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/extjs/ux/Ext.app.SearchField.js'); - $this->pageRenderer->addJsFile($backendRelPath . 'Resources/Public/JavaScript/extjs/ux/Ext.ux.FitToParent.js'); - $resourcePath = ExtensionManagementUtility::extRelPath('workspaces') . 'Resources/Public/JavaScript/'; - - // @todo Integrate additional stylesheet resources - $this->pageRenderer->addCssFile($resourcePath . 'gridfilters/css/GridFilters.css'); - $this->pageRenderer->addCssFile($resourcePath . 'gridfilters/css/RangeMenu.css'); - - $filters = array( - $resourcePath . 'gridfilters/menu/RangeMenu.js', - $resourcePath . 'gridfilters/menu/ListMenu.js', - $resourcePath . 'gridfilters/GridFilters.js', - $resourcePath . 'gridfilters/filter/Filter.js', - $resourcePath . 'gridfilters/filter/StringFilter.js', - $resourcePath . 'gridfilters/filter/DateFilter.js', - $resourcePath . 'gridfilters/filter/ListFilter.js', - $resourcePath . 'gridfilters/filter/NumericFilter.js', - $resourcePath . 'gridfilters/filter/BooleanFilter.js', - $resourcePath . 'gridfilters/filter/BooleanFilter.js', - ); - - $custom = $this->getAdditionalResourceService()->getJavaScriptResources(); - - $resources = array( - $resourcePath . 'Component/RowDetailTemplate.js', - $resourcePath . 'Component/RowExpander.js', - $resourcePath . 'Component/TabPanel.js', - $resourcePath . 'Store/mainstore.js', - $resourcePath . 'configuration.js', - $resourcePath . 'helpers.js', - $resourcePath . 'actions.js', - $resourcePath . 'component.js', - $resourcePath . 'toolbar.js', - $resourcePath . 'grid.js', - $resourcePath . 'workspaces.js' - ); - - $javaScriptFiles = array_merge($filters, $custom, $resources); - foreach ($javaScriptFiles as $javaScriptFile) { - $this->pageRenderer->addJsFile($javaScriptFile); - } foreach ($this->getAdditionalResourceService()->getLocalizationResources() as $localizationResource) { $this->pageRenderer->addInlineLanguageLabelFile($localizationResource); } + $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Workspaces/Backend'); $this->pageRenderer->addInlineSetting('FormEngine', 'moduleUrl', BackendUtility::getModuleUrl('record_edit')); $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', BackendUtility::getModuleUrl('record_history')); + $this->pageRenderer->addInlineSetting('Workspaces', 'token', FormProtectionFactory::get('backend')->generateToken('extDirect')); + $this->pageRenderer->addInlineSetting('Workspaces', 'id', (int)GeneralUtility::_GP('id')); } /** @@ -263,9 +238,10 @@ class ReviewController extends AbstractController * * @param array $workspaceList * @param int $activeWorkspace + * @param bool $showAllWorkspaceTab * @return array */ - protected function prepareWorkspaceTabs(array $workspaceList, $activeWorkspace) + protected function prepareWorkspaceTabs(array $workspaceList, $activeWorkspace, $showAllWorkspaceTab = true) { $tabs = array(); @@ -278,12 +254,14 @@ class ReviewController extends AbstractController ); } - $tabs[] = array( - 'title' => 'All workspaces', - 'itemId' => 'workspace-' . WorkspaceService::SELECT_ALL_WORKSPACES, - 'workspaceId' => WorkspaceService::SELECT_ALL_WORKSPACES, - 'triggerUrl' => $this->getModuleUri(WorkspaceService::SELECT_ALL_WORKSPACES), - ); + if ($showAllWorkspaceTab) { + $tabs[] = array( + 'title' => 'All workspaces', + 'itemId' => 'workspace-' . WorkspaceService::SELECT_ALL_WORKSPACES, + 'workspaceId' => WorkspaceService::SELECT_ALL_WORKSPACES, + 'triggerUrl' => $this->getModuleUri(WorkspaceService::SELECT_ALL_WORKSPACES), + ); + } foreach ($workspaceList as $workspaceId => $workspaceTitle) { if ($workspaceId === $activeWorkspace) { @@ -328,4 +306,12 @@ class ReviewController extends AbstractController { return $GLOBALS['LANG']; } + + /** + * @return BackendUserAuthentication + */ + protected function getBackendUser() + { + return $GLOBALS['BE_USER']; + } } diff --git a/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php b/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php index 4a880625204b..9d1f0a95fd8c 100644 --- a/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php +++ b/typo3/sysext/workspaces/Classes/ExtDirect/ActionHandler.php @@ -15,10 +15,13 @@ namespace TYPO3\CMS\Workspaces\ExtDirect; */ use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Fluid\View\StandaloneView; use TYPO3\CMS\Workspaces\Domain\Record\StageRecord; use TYPO3\CMS\Workspaces\Domain\Record\WorkspaceRecord; use TYPO3\CMS\Workspaces\Service\StagesService; +use TYPO3\CMS\Workspaces\Service\WorkspaceService; /** * ExtDirect action handler @@ -35,7 +38,7 @@ class ActionHandler extends AbstractHandler */ public function __construct() { - $this->stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); + $this->stageService = GeneralUtility::makeInstance(StagesService::class); } /** @@ -114,7 +117,7 @@ class ActionHandler extends AbstractHandler */ public function viewSingleRecord($table, $uid) { - return \TYPO3\CMS\Workspaces\Service\WorkspaceService::viewSingleRecord($table, $uid); + return WorkspaceService::viewSingleRecord($table, $uid); } /** @@ -410,10 +413,10 @@ class ActionHandler extends AbstractHandler public function discardStagesFromPage($pageId) { $cmdMapArray = array(); - /** @var $workspaceService \TYPO3\CMS\Workspaces\Service\WorkspaceService */ - $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + /** @var $workspaceService WorkspaceService */ + $workspaceService = GeneralUtility::makeInstance(WorkspaceService::class); /** @var $stageService StagesService */ - $stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); + $stageService = GeneralUtility::makeInstance(StagesService::class); $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $pageId, ($recursionLevel = 0), ($selectionType = 'tables_modify')); foreach ($workspaceItemsArray as $tableName => $items) { foreach ($items as $item) { @@ -437,7 +440,7 @@ class ActionHandler extends AbstractHandler * $parameters->stageId * </code> * - * @param stdClass $parameters + * @param \stdClass $parameters * @return array */ public function sentCollectionToStage(\stdClass $parameters) @@ -451,7 +454,7 @@ class ActionHandler extends AbstractHandler if (!is_object($parameters->affects) || empty($parameters->affects)) { throw new \InvalidArgumentException('Missing "affected items" in $parameters array.', 1319488195); } - $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $stageId); + $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $stageId); foreach ($parameters->affects as $tableName => $items) { foreach ($items as $item) { // Publishing uses live id in command map @@ -516,7 +519,7 @@ class ActionHandler extends AbstractHandler * additional: string * comments: string * - * @param stdClass $parameters + * @param \stdClass $parameters * @return array */ public function sendToNextStageExecute(\stdClass $parameters) @@ -531,7 +534,7 @@ class ActionHandler extends AbstractHandler $elementRecord = BackendUtility::getRecord($table, $uid); $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']); - $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $setStageId); + $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $setStageId); if ($setStageId == StagesService::STAGE_PUBLISH_EXECUTE_ID) { $cmdArray[$table][$t3ver_oid]['version']['action'] = 'swap'; $cmdArray[$table][$t3ver_oid]['version']['swapWith'] = $uid; @@ -563,7 +566,7 @@ class ActionHandler extends AbstractHandler * additional: string * comments: string * - * @param stdClass $parameters + * @param \stdClass $parameters * @return array */ public function sendToPrevStageExecute(\stdClass $parameters) @@ -577,7 +580,7 @@ class ActionHandler extends AbstractHandler $elementRecord = BackendUtility::getRecord($table, $uid); $currentWorkspace = $this->setTemporaryWorkspace($elementRecord['t3ver_wsid']); - $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $setStageId); + $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $setStageId); $cmdArray[$table][$uid]['version']['action'] = 'setStage'; $cmdArray[$table][$uid]['version']['stageId'] = $setStageId; $cmdArray[$table][$uid]['version']['comment'] = $comments; @@ -618,7 +621,7 @@ class ActionHandler extends AbstractHandler $setStageId = $parameters->affects->nextStage; $comments = $parameters->comments; $elements = $parameters->affects->elements; - $recipients = $this->getRecipientList($parameters->receipients, $parameters->additional, $setStageId); + $recipients = $this->getRecipientList((array)$parameters->recipients, $parameters->additional, $setStageId); foreach ($elements as $element) { // Avoid any action on records that have already been published to live $elementRecord = BackendUtility::getRecord($element->table, $element->uid); @@ -648,7 +651,7 @@ class ActionHandler extends AbstractHandler /** * Gets the dialog window to be displayed before a record can be sent to a stage. * - * @param StageRecord|int $nextStageId + * @param StageRecord|int $nextStage * @return array */ protected function getSentToStageWindow($nextStage) @@ -657,43 +660,18 @@ class ActionHandler extends AbstractHandler $nextStage = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($nextStage); } - $result = array( - 'title' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:actionSendToStage'), - 'items' => array( - array( - 'xtype' => 'panel', - 'bodyStyle' => 'margin-bottom: 7px; border: none;', - 'html' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.itemsWillBeSentTo') . ' ' . $nextStage->getTitle() - ) - ) - ); - + $result = []; if ($nextStage->isDialogEnabled()) { - $result['items'][] = array( - 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.sendMailTo'), - 'xtype' => 'checkboxgroup', - 'itemCls' => 'x-check-group-alt', - 'columns' => 1, - 'style' => 'max-height: 200px', - 'autoScroll' => true, - 'items' => array( - $this->getReceipientsOfStage($nextStage->getUid()) - ) - ); - $result['items'][] = array( - 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.additionalRecipients'), - 'name' => 'additional', - 'xtype' => 'textarea', - 'width' => 250 - ); + $result['sendMailTo'] = $this->getRecipientsOfStage($nextStage->getUid()); + $result['additional'] = [ + 'type' => 'textarea', + 'value' => '' + ]; } - $result['items'][] = array( - 'fieldLabel' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:window.sendToNextStageWindow.comments'), - 'name' => 'comments', - 'xtype' => 'textarea', - 'width' => 250, + $result['comments'] = [ + 'type' => 'textarea', 'value' => ($nextStage->isInternal() ? '' : $nextStage->getDefaultComment()) - ); + ]; return $result; } @@ -704,7 +682,7 @@ class ActionHandler extends AbstractHandler * @param StageRecord|int $stageRecord * @return array */ - protected function getReceipientsOfStage($stageRecord) + protected function getRecipientsOfStage($stageRecord) { if (!$stageRecord instanceof StageRecord) { $stageRecord = WorkspaceRecord::get($this->getCurrentWorkspace())->getStage($stageRecord); @@ -725,8 +703,9 @@ class ActionHandler extends AbstractHandler $disabled = ($checked && !$isPreselectionChangeable); $result[] = array( - 'boxLabel' => sprintf('%s (%s)', $name, $backendUser['email']), - 'name' => 'receipients-' . $backendUserId, + 'label' => sprintf('%s (%s)', $name, $backendUser['email']), + 'value' => $backendUserId, + 'name' => 'recipients-' . $backendUserId, 'checked' => $checked, 'disabled' => $disabled ); @@ -755,7 +734,7 @@ class ActionHandler extends AbstractHandler protected function getStageService() { if (!isset($this->stageService)) { - $this->stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); + $this->stageService = GeneralUtility::makeInstance(StagesService::class); } return $this->stageService; } @@ -768,17 +747,19 @@ class ActionHandler extends AbstractHandler */ public function sendPageToPreviousStage($id) { - $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + $workspaceService = GeneralUtility::makeInstance(WorkspaceService::class); $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $id, ($recursionLevel = 0), ($selectionType = 'tables_modify')); list($currentStage, $previousStage) = $this->getStageService()->getPreviousStageForElementCollection($workspaceItemsArray); // get only the relevant items for processing $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), $currentStage['uid'], $id, ($recursionLevel = 0), ($selectionType = 'tables_modify')); - return array( + $stageFormFields = $this->getSentToStageWindow($previousStage['uid']); + $result = array_merge($stageFormFields, [ 'title' => 'Status message: Page send to next stage - ID: ' . $id . ' - Next stage title: ' . $previousStage['title'], 'items' => $this->getSentToStageWindow($previousStage['uid']), 'affects' => $workspaceItemsArray, 'stageId' => $previousStage['uid'] - ); + ]); + return $result; } /** @@ -787,48 +768,55 @@ class ActionHandler extends AbstractHandler */ public function sendPageToNextStage($id) { - $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + $workspaceService = GeneralUtility::makeInstance(WorkspaceService::class); $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $id, ($recursionLevel = 0), ($selectionType = 'tables_modify')); list($currentStage, $nextStage) = $this->getStageService()->getNextStageForElementCollection($workspaceItemsArray); // get only the relevant items for processing $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($this->stageService->getWorkspaceId(), ($filter = 1), $currentStage['uid'], $id, ($recursionLevel = 0), ($selectionType = 'tables_modify')); - return array( + $stageFormFields = $this->getSentToStageWindow($nextStage['uid']); + $result = array_merge($stageFormFields, [ 'title' => 'Status message: Page send to next stage - ID: ' . $id . ' - Next stage title: ' . $nextStage['title'], - 'items' => $this->getSentToStageWindow($nextStage['uid']), 'affects' => $workspaceItemsArray, 'stageId' => $nextStage['uid'] - ); + ]); + return $result; } /** * Fetch the current label and visible state of the buttons. * * @param int $id - * @return array Contains the visibility state and label of the stage change buttons. + * @return string The pre-rendered HTML for the stage buttons */ public function updateStageChangeButtons($id) { - $stageService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); - $workspaceService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\WorkspaceService::class); + /** @var StagesService $stageService */ + $stageService = GeneralUtility::makeInstance(StagesService::class); + /** @var WorkspaceService $workspaceService */ + $workspaceService = GeneralUtility::makeInstance(WorkspaceService::class); // fetch the next and previous stage $workspaceItemsArray = $workspaceService->selectVersionsInWorkspace($stageService->getWorkspaceId(), ($filter = 1), ($stage = -99), $id, ($recursionLevel = 0), ($selectionType = 'tables_modify')); list(, $nextStage) = $stageService->getNextStageForElementCollection($workspaceItemsArray); list(, $previousStage) = $stageService->getPreviousStageForElementCollection($workspaceItemsArray); - $toolbarButtons = array( - 'feToolbarButtonNextStage' => array( - 'visible' => is_array($nextStage) && !empty($nextStage), - 'text' => $nextStage['title'] - ), - 'feToolbarButtonPreviousStage' => array( - 'visible' => is_array($previousStage) && !empty($previousStage), - 'text' => $previousStage['title'] - ), - 'feToolbarButtonDiscardStage' => array( - 'visible' => is_array($nextStage) && !empty($nextStage) || is_array($previousStage) && !empty($previousStage), - 'text' => $GLOBALS['LANG']->sL('LLL:EXT:workspaces/Resources/Private/Language/locallang.xlf:label_doaction_discard', true) - ) - ); - return $toolbarButtons; + + /** @var StandaloneView $view */ + $view = GeneralUtility::makeInstance(StandaloneView::class); + $extensionPath = ExtensionManagementUtility::extPath('workspaces'); + $view->setPartialRootPaths(['default' => $extensionPath . 'Resources/Private/Partials']); + $view->setTemplatePathAndFilename($extensionPath . 'Resources/Private/Templates/Preview/Ajax/StageButtons.html'); + $request = $view->getRequest(); + $request->setControllerExtensionName('workspaces'); + $view->assignMultiple([ + 'enablePreviousStageButton' => is_array($previousStage) && !empty($previousStage), + 'enableNextStageButton' => is_array($nextStage) && !empty($nextStage), + 'enableDiscardStageButton' => is_array($nextStage) && !empty($nextStage) || is_array($previousStage) && !empty($previousStage), + 'nextStage' => $nextStage['title'], + 'nextStageId' => $nextStage['uid'], + 'prevStage' => $previousStage['title'], + 'prevStageId' => $previousStage['uid'], + ]); + $renderedView = $view->render(); + return $renderedView; } /** diff --git a/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php b/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php index edcb1279f1be..dde139e87964 100644 --- a/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php +++ b/typo3/sysext/workspaces/Classes/ExtDirect/ExtDirectServer.php @@ -14,12 +14,25 @@ namespace TYPO3\CMS\Workspaces\ExtDirect; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Backend\Backend\Avatar\Avatar; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Database\DatabaseConnection; +use TYPO3\CMS\Core\Html\RteHtmlParser; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Resource\FileReference; +use TYPO3\CMS\Core\Resource\ProcessedFile; +use TYPO3\CMS\Core\Utility\DiffUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Utility\LocalizationUtility; +use TYPO3\CMS\Lang\LanguageService; +use TYPO3\CMS\Workspaces\Service\GridDataService; +use TYPO3\CMS\Workspaces\Service\HistoryService; +use TYPO3\CMS\Workspaces\Service\StagesService; +use TYPO3\CMS\Workspaces\Service\WorkspaceService; /** * ExtDirect server @@ -27,17 +40,17 @@ use TYPO3\CMS\Extbase\Object\ObjectManager; class ExtDirectServer extends AbstractHandler { /** - * @var \TYPO3\CMS\Workspaces\Service\GridDataService + * @var GridDataService */ protected $gridDataService; /** - * @var \TYPO3\CMS\Workspaces\Service\StagesService + * @var StagesService */ protected $stagesService; /** - * @var \cogpowered\FineDiff\Diff + * @var DiffUtility */ protected $differenceHandler; @@ -67,7 +80,7 @@ class ExtDirectServer extends AbstractHandler { // To avoid too much work we use -1 to indicate that every page is relevant $pageId = $parameter->id > 0 ? $parameter->id : -1; - if (!isset($parameter->language) || !\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($parameter->language)) { + if (!isset($parameter->language) || !MathUtility::canBeInterpretedAsInteger($parameter->language)) { $parameter->language = null; } $versions = $this->getWorkspaceService()->selectVersionsInWorkspace($this->getCurrentWorkspace(), 0, -99, $pageId, $parameter->depth, 'tables_select', $parameter->language); @@ -75,23 +88,6 @@ class ExtDirectServer extends AbstractHandler return $data; } - /** - * Gets the editing history of a record. - * - * @param stdClass $parameters - * @return array - */ - public function getHistory($parameters) - { - /** @var $historyService \TYPO3\CMS\Workspaces\Service\HistoryService */ - $historyService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\HistoryService::class); - $history = $historyService->getHistory($parameters->table, $parameters->liveId); - return array( - 'data' => $history, - 'total' => count($history) - ); - } - /** * Get List of available workspace actions * @@ -102,7 +98,7 @@ class ExtDirectServer extends AbstractHandler { $currentWorkspace = $this->getCurrentWorkspace(); $stages = array(); - if ($currentWorkspace != \TYPO3\CMS\Workspaces\Service\WorkspaceService::SELECT_ALL_WORKSPACES) { + if ($currentWorkspace != WorkspaceService::SELECT_ALL_WORKSPACES) { $stages = $this->getStagesService()->getStagesForWSUser(); } $data = array( @@ -122,16 +118,16 @@ class ExtDirectServer extends AbstractHandler { $diffReturnArray = array(); $liveReturnArray = array(); - /** @var $diffUtility \TYPO3\CMS\Core\Utility\DiffUtility */ - $diffUtility = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Utility\DiffUtility::class); - /** @var $parseObj \TYPO3\CMS\Core\Html\RteHtmlParser */ - $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Html\RteHtmlParser::class); + $diffUtility = $this->getDifferenceHandler(); + /** @var $parseObj RteHtmlParser */ + $parseObj = GeneralUtility::makeInstance(RteHtmlParser::class); $liveRecord = BackendUtility::getRecord($parameter->table, $parameter->t3ver_oid); $versionRecord = BackendUtility::getRecord($parameter->table, $parameter->uid); $iconFactory = GeneralUtility::makeInstance(IconFactory::class); $icon_Live = $iconFactory->getIconForRecord($parameter->table, $liveRecord, Icon::SIZE_SMALL)->render(); $icon_Workspace = $iconFactory->getIconForRecord($parameter->table, $versionRecord, Icon::SIZE_SMALL)->render(); - $stagePosition = $this->getStagesService()->getPositionOfCurrentStage($parameter->stage); + $stagesService = $this->getStagesService(); + $stagePosition = $stagesService->getPositionOfCurrentStage($parameter->stage); $fieldsOfRecords = array_keys($liveRecord); if ($GLOBALS['TCA'][$parameter->table]) { if ($GLOBALS['TCA'][$parameter->table]['interface']['showRecordFieldList']) { @@ -144,14 +140,14 @@ class ExtDirectServer extends AbstractHandler continue; } // Get the field's label. If not available, use the field name - $fieldTitle = $GLOBALS['LANG']->sL(BackendUtility::getItemLabel($parameter->table, $fieldName)); + $fieldTitle = $this->getLanguageService()->sL(BackendUtility::getItemLabel($parameter->table, $fieldName)); if (empty($fieldTitle)) { $fieldTitle = $fieldName; } // Gets the TCA configuration for the current field $configuration = $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config']; // check for exclude fields - if ($GLOBALS['BE_USER']->isAdmin() || $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['exclude'] == 0 || GeneralUtility::inList($GLOBALS['BE_USER']->groupData['non_exclude_fields'], $parameter->table . ':' . $fieldName)) { + if ($this->getBackendUser()->isAdmin() || $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['exclude'] == 0 || GeneralUtility::inList($this->getBackendUser()->groupData['non_exclude_fields'], $parameter->table . ':' . $fieldName)) { // call diff class only if there is a difference if ($configuration['type'] === 'inline' && $configuration['foreign_table'] === 'sys_file_reference') { $useThumbnails = false; @@ -216,8 +212,8 @@ class ExtDirectServer extends AbstractHandler ); if ($configuration['type'] == 'group' && $configuration['internal_type'] == 'file') { - $versionThumb = BackendUtility::thumbCode($versionRecord, $parameter->table, $fieldName); - $liveThumb = BackendUtility::thumbCode($liveRecord, $parameter->table, $fieldName); + $versionThumb = BackendUtility::thumbCode($versionRecord, $parameter->table, $fieldName, ''); + $liveThumb = BackendUtility::thumbCode($liveRecord, $parameter->table, $fieldName, ''); $diffReturnArray[] = array( 'field' => $fieldName, 'label' => $fieldTitle, @@ -254,6 +250,22 @@ class ExtDirectServer extends AbstractHandler } } $commentsForRecord = $this->getCommentsForRecord($parameter->uid, $parameter->table); + + /** @var $historyService HistoryService */ + $historyService = GeneralUtility::makeInstance(HistoryService::class); + $history = $historyService->getHistory($parameter->table, $parameter->t3ver_oid); + + $prevStage = $stagesService->getPrevStage($parameter->stage); + $nextStage = $stagesService->getNextStage($parameter->stage); + + if (isset($prevStage[0])) { + $prevStage = current($prevStage); + } + + if (isset($nextStage[0])) { + $nextStage = current($nextStage); + } + return array( 'total' => 1, 'data' => array( @@ -265,11 +277,21 @@ class ExtDirectServer extends AbstractHandler 'icon_Workspace' => $icon_Workspace, // this part is already escaped in getCommentsForRecord() 'comments' => $commentsForRecord, - // escape/santinize the others - 'path_Live' => htmlspecialchars($parameter->path_Live), - 'label_Stage' => htmlspecialchars($parameter->label_Stage), + // escape/sanitize the others + 'path_Live' => htmlspecialchars(BackendUtility::getRecordPath($liveRecord['pid'], '', 999)), + 'label_Stage' => htmlspecialchars($stagesService->getStageTitle($parameter->stage)), + 'label_PrevStage' => $prevStage, + 'label_NextStage' => $nextStage, 'stage_position' => (int)$stagePosition['position'], - 'stage_count' => (int)$stagePosition['count'] + 'stage_count' => (int)$stagePosition['count'], + 'parent' => [ + 'table' => htmlspecialchars($parameter->table), + 'uid' => (int)$parameter->uid + ], + 'history' => [ + 'data' => $history, + 'total' => count($history) + ] ) ) ); @@ -323,7 +345,7 @@ class ExtDirectServer extends AbstractHandler foreach ($candidates as $identifierWithRandomValue => $fileReference) { if ($useThumbnails) { $thumbnailFile = $fileReference->getOriginalFile()->process( - \TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, + ProcessedFile::CONTEXT_IMAGEPREVIEW, array('width' => 40, 'height' => 40) ); $thumbnailMarkup = '<img src="' . $thumbnailFile->getPublicUrl(true) . '" />'; @@ -333,7 +355,7 @@ class ExtDirectServer extends AbstractHandler } } - $differences = $this->getDifferenceHandler()->render($liveInformation, $versionInformation); + $differences = $this->getDifferenceHandler()->makeDiffDisplay($liveInformation, $versionInformation); $liveInformation = str_replace(array_keys($substitutes), array_values($substitutes), trim($liveInformation)); $differences = str_replace(array_keys($substitutes), array_values($substitutes), trim($differences)); @@ -353,14 +375,18 @@ class ExtDirectServer extends AbstractHandler public function getCommentsForRecord($uid, $table) { $sysLogReturnArray = array(); - $sysLogRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + $sysLogRows = $this->getDatabaseConnection()->exec_SELECTgetRows( 'log_data,tstamp,userid', 'sys_log', - 'action=6 and details_nr=30 AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_log') + 'action=6 and details_nr=30 AND tablename=' . $this->getDatabaseConnection()->fullQuoteStr($table, 'sys_log') . ' AND recuid=' . (int)$uid, '', 'tstamp DESC' ); + + /** @var Avatar $avatar */ + $avatar = GeneralUtility::makeInstance(Avatar::class); + foreach ($sysLogRows as $sysLogRow) { $sysLogEntry = array(); $data = unserialize($sysLogRow['log_data']); @@ -370,6 +396,7 @@ class ExtDirectServer extends AbstractHandler $sysLogEntry['user_username'] = is_array($beUserRecord) ? htmlspecialchars($beUserRecord['username']) : ''; $sysLogEntry['tstamp'] = htmlspecialchars(BackendUtility::datetime($sysLogRow['tstamp'])); $sysLogEntry['user_comment'] = nl2br(htmlspecialchars($data['comment'])); + $sysLogEntry['user_avatar'] = $avatar->render($beUserRecord); $sysLogReturnArray[] = $sysLogEntry; } return $sysLogReturnArray; @@ -386,7 +413,7 @@ class ExtDirectServer extends AbstractHandler $systemLanguages = array( array( 'uid' => 'all', - 'title' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate('language.allLanguages', 'workspaces'), + 'title' => LocalizationUtility::translate('language.allLanguages', 'workspaces'), 'icon' => $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() ) ); @@ -407,15 +434,39 @@ class ExtDirectServer extends AbstractHandler return $result; } + /** + * @return BackendUserAuthentication; + */ + protected function getBackendUser() + { + return $GLOBALS['BE_USER']; + } + + /** + * @return LanguageService; + */ + protected function getLanguageService() + { + return $GLOBALS['LANG']; + } + + /** + * @return DatabaseConnection; + */ + protected function getDatabaseConnection() + { + return $GLOBALS['TYPO3_DB']; + } + /** * Gets the Grid Data Service. * - * @return \TYPO3\CMS\Workspaces\Service\GridDataService + * @return GridDataService */ protected function getGridDataService() { if (!isset($this->gridDataService)) { - $this->gridDataService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\GridDataService::class); + $this->gridDataService = GeneralUtility::makeInstance(GridDataService::class); } return $this->gridDataService; } @@ -423,12 +474,12 @@ class ExtDirectServer extends AbstractHandler /** * Gets the Stages Service. * - * @return \TYPO3\CMS\Workspaces\Service\StagesService + * @return StagesService */ protected function getStagesService() { if (!isset($this->stagesService)) { - $this->stagesService = GeneralUtility::makeInstance(\TYPO3\CMS\Workspaces\Service\StagesService::class); + $this->stagesService = GeneralUtility::makeInstance(StagesService::class); } return $this->stagesService; } @@ -436,13 +487,12 @@ class ExtDirectServer extends AbstractHandler /** * Gets the difference handler, parsing differences based on sentences. * - * @return \cogpowered\FineDiff\Diff + * @return DiffUtility */ protected function getDifferenceHandler() { if (!isset($this->differenceHandler)) { - $granularity = new \cogpowered\FineDiff\Granularity\Word(); - $this->differenceHandler = new \cogpowered\FineDiff\Diff($granularity); + $this->differenceHandler = GeneralUtility::makeInstance(DiffUtility::class); } return $this->differenceHandler; } diff --git a/typo3/sysext/workspaces/Classes/Service/GridDataService.php b/typo3/sysext/workspaces/Classes/Service/GridDataService.php index 8edebad40099..bec3887ab855 100644 --- a/typo3/sysext/workspaces/Classes/Service/GridDataService.php +++ b/typo3/sysext/workspaces/Classes/Service/GridDataService.php @@ -163,8 +163,10 @@ class GridDataService $versionArray['label_Stage'] = htmlspecialchars($stagesObj->getStageTitle($versionRecord['t3ver_stage'])); $tempStage = $stagesObj->getNextStage($versionRecord['t3ver_stage']); $versionArray['label_nextStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid'])); + $versionArray['value_nextStage'] = (int)$tempStage['uid']; $tempStage = $stagesObj->getPrevStage($versionRecord['t3ver_stage']); $versionArray['label_prevStage'] = htmlspecialchars($stagesObj->getStageTitle($tempStage['uid'])); + $versionArray['value_prevStage'] = (int)$tempStage['uid']; $versionArray['path_Live'] = htmlspecialchars(BackendUtility::getRecordPath($record['livepid'], '', 999)); $versionArray['path_Workspace'] = htmlspecialchars(BackendUtility::getRecordPath($record['wspid'], '', 999)); $versionArray['workspace_Title'] = htmlspecialchars(\TYPO3\CMS\Workspaces\Service\WorkspaceService::getWorkspaceTitle($versionRecord['t3ver_wsid'])); diff --git a/typo3/sysext/workspaces/Classes/Service/HistoryService.php b/typo3/sysext/workspaces/Classes/Service/HistoryService.php index c872bb6489b3..4483a6d2d754 100644 --- a/typo3/sysext/workspaces/Classes/Service/HistoryService.php +++ b/typo3/sysext/workspaces/Classes/Service/HistoryService.php @@ -14,7 +14,9 @@ namespace TYPO3\CMS\Workspaces\Service; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Backend\Backend\Avatar\Avatar; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Service for history @@ -79,9 +81,15 @@ class HistoryService implements \TYPO3\CMS\Core\SingletonInterface } else { $differences = $this->getDifferences($entry); } + + /** @var Avatar $avatar */ + $avatar = GeneralUtility::makeInstance(Avatar::class); + $beUserRecord = BackendUtility::getRecord('be_users', $entry['user']); + return array( 'datetime' => htmlspecialchars(BackendUtility::datetime($entry['tstamp'])), 'user' => htmlspecialchars($this->getUserName($entry['user'])), + 'user_avatar' => $avatar->render($beUserRecord), 'differences' => $differences ); } diff --git a/typo3/sysext/workspaces/Classes/Service/StagesService.php b/typo3/sysext/workspaces/Classes/Service/StagesService.php index f54579bc283c..7c95c511bede 100644 --- a/typo3/sysext/workspaces/Classes/Service/StagesService.php +++ b/typo3/sysext/workspaces/Classes/Service/StagesService.php @@ -318,7 +318,6 @@ class StagesService implements \TYPO3\CMS\Core\SingletonInterface } next($workspaceStageRecs); } - } else { } if ($nextStage === false) { $nextStage[] = array( diff --git a/typo3/sysext/workspaces/Resources/Private/Language/locallang.xlf b/typo3/sysext/workspaces/Resources/Private/Language/locallang.xlf index 45037ccf9dd1..c888fe656f17 100644 --- a/typo3/sysext/workspaces/Resources/Private/Language/locallang.xlf +++ b/typo3/sysext/workspaces/Resources/Private/Language/locallang.xlf @@ -25,16 +25,19 @@ <source>Workspaces</source> </trans-unit> <trans-unit id="ok"> - <source>ok</source> + <source>Ok</source> </trans-unit> <trans-unit id="cancel"> - <source>cancel</source> + <source>Cancel</source> </trans-unit> <trans-unit id="chooseMassAction"> - <source>choose Mass Action</source> + <source>Choose mass action</source> + </trans-unit> + <trans-unit id="chooseSelectionAction"> + <source>Choose selection action</source> </trans-unit> <trans-unit id="chooseAction"> - <source>choose Action</source> + <source>Choose staging action</source> </trans-unit> <trans-unit id="item"> <source>item</source> @@ -97,6 +100,9 @@ <trans-unit id="tooltip.generatePagePreview"> <source>Generate page preview links</source> </trans-unit> + <trans-unit id="tooltip.showChanges"> + <source>Show changes</source> + </trans-unit> <trans-unit id="previewLink"> <source>Preview Link</source> </trans-unit> @@ -125,23 +131,26 @@ <source>Changed</source> </trans-unit> <trans-unit id="column.uid"> - <source>WS-Id</source> + <source>WS ID</source> </trans-unit> <trans-unit id="column.oid"> - <source>Live-Id</source> + <source>Live ID</source> </trans-unit> <trans-unit id="column.workspaceName"> <source>Workspace</source> </trans-unit> <trans-unit id="column.livePath"> - <source>Live-Path</source> + <source>Live path</source> </trans-unit> <trans-unit id="column.liveTitle"> - <source>Live-Title</source> + <source>Live title</source> </trans-unit> <trans-unit id="column.wsSwapColumn"> <source>Swap workspace</source> </trans-unit> + <trans-unit id="column.integrity"> + <source>Integrity</source> + </trans-unit> <trans-unit id="tooltip.viewElementAction"> <source>Preview element</source> </trans-unit> @@ -213,9 +222,30 @@ <trans-unit id="window.sendToNextStageWindow.additionalRecipients"> <source>Additional recipients</source> </trans-unit> + <trans-unit id="window.sendToNextStageWindow.additionalRecipients.hint"> + <source>One recipient per line</source> + </trans-unit> <trans-unit id="window.sendToNextStageWindow.comments"> <source>Comments</source> </trans-unit> + <trans-unit id="window.recordChanges"> + <source>Record changes</source> + </trans-unit> + <trans-unit id="window.recordInformation"> + <source>Information for "{0}"</source> + </trans-unit> + <trans-unit id="window.recordChanges.tabs.changeSummary"> + <source>Summary of changes</source> + </trans-unit> + <trans-unit id="window.recordChanges.tabs.comments"> + <source>Comments</source> + </trans-unit> + <trans-unit id="window.recordChanges.tabs.history"> + <source>History</source> + </trans-unit> + <trans-unit id="window.recordHistory"> + <source>History of record "{0}"</source> + </trans-unit> <trans-unit id="error.getStageTitle.stageNotFound"> <source>Stage not found</source> </trans-unit> diff --git a/typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html b/typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html new file mode 100644 index 000000000000..54d9aa0bb6bc --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Private/Layouts/Empty.html @@ -0,0 +1 @@ +<f:render section="main" /> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Layouts/Module.html b/typo3/sysext/workspaces/Resources/Private/Layouts/Module.html index d480d8d62d01..1cf117ca0350 100644 --- a/typo3/sysext/workspaces/Resources/Private/Layouts/Module.html +++ b/typo3/sysext/workspaces/Resources/Private/Layouts/Module.html @@ -1,4 +1,5 @@ <f:if condition="{pageTitle}"><h1>{pageTitle}</h1></f:if> -<div id="workspacetabs"></div> -<div class="well well-sm"><f:render section="main" /></div> + +<f:render section="main" /> + <f:if condition="{showLegend}"><f:render partial="legend" /></f:if> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Layouts/Nodoc.html b/typo3/sysext/workspaces/Resources/Private/Layouts/Nodoc.html index 18efefcd0bb6..71351b39fcaf 100644 --- a/typo3/sysext/workspaces/Resources/Private/Layouts/Nodoc.html +++ b/typo3/sysext/workspaces/Resources/Private/Layouts/Nodoc.html @@ -1,3 +1,12 @@ +<style type="text/css"> + .module-body { + padding: 0; + } + + div.typo3-noDoc { + margin: 0; + } +</style> <div class="typo3-noDoc"> <!-- Content of module, for instance listing, info or editing --> <div id="typo3-docbody"> diff --git a/typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html b/typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html index f935b702f8c5..8b997bb89e17 100644 --- a/typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html +++ b/typo3/sysext/workspaces/Resources/Private/Layouts/Popup.html @@ -1,21 +1,4 @@ -<!-- ###FULLDOC### begin --> <f:render section="main" /> <script type="text/javascript"> - var liveUrl = '{liveUrl}'; - var wsUrl = '{wsUrl}'; - var wsSettingsUrl = '{wsSettingsUrl}'; document.domain = '{backendDomain}'; - - function resize(height) { - // poor way to avoid that we require any scrollbars within the frames - if (Ext.getCmp('wsContainer')) { - var currentHeight = isNaN(Ext.getCmp('wsContainer').getHeight()) ? 0 : Ext.getCmp('wsContainer').getHeight(); - var finalHeight = Math.max(currentHeight, height * 1.1); - Ext.getCmp('visualPanel').setHeight(finalHeight); - Ext.getCmp('wsContainer').setHeight(finalHeight); - Ext.getCmp('wsPanel').setHeight(finalHeight); - Ext.getCmp('liveContainer').setHeight(finalHeight * (100 - Ext.getCmp('sizeSlider').getValue()) / 100); - Ext.getCmp('livePanel').setHeight(finalHeight); - } - } </script> diff --git a/typo3/sysext/workspaces/Resources/Private/Less/preview.less b/typo3/sysext/workspaces/Resources/Private/Less/preview.less new file mode 100644 index 000000000000..a10257abb866 --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Private/Less/preview.less @@ -0,0 +1,69 @@ +.module-body { + padding: 0; +} + +.typo3-topbar-workspace-actions { + float: right; + height: 100%; + display: table; +} + +.workspace-action { + display: table-cell; + vertical-align: middle; + padding: 0 4px; + + &:last-child { + padding-right: 16px; + } + + .active-preview-mode { + display: inline-block; + text-align: left; + } + + .slider-wrapper { + .slider { + float: left; + } + + b { + margin: 7px 10px; + display: block; + float: left; + } + } +} + +.workspaces { + position: relative; + + iframe { + border: 0; + } +} + +.preview-mode- { + &slider { + iframe { + position: absolute; + top: 0; + z-index: 100; + } + #live-view { + border-bottom: 2px solid #c83c3c; + z-index: 200; + } + } + + &vbox iframe { + width: 50%; + height: 100%; + float: left; + } + + &hbox iframe { + width: 100%; + height: 50%; + } +} \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Partials/Legend.html b/typo3/sysext/workspaces/Resources/Private/Partials/Legend.html index 9accc01b9e8a..fa91e9df808f 100644 --- a/typo3/sysext/workspaces/Resources/Private/Partials/Legend.html +++ b/typo3/sysext/workspaces/Resources/Private/Partials/Legend.html @@ -1,3 +1,4 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <dl class="legend"> <dt><f:translate key="legend.label" /></dt> <dd><span class="item-state-modified"><f:translate key="legend.edited" /></span> • </dd> @@ -5,4 +6,5 @@ <dd><span class="item-state-new"><f:translate key="legend.new" /></span> • </dd> <dd><span class="item-state-hidden"><f:translate key="legend.hidden" /></span> • </dd> <dd><span class="item-state-deleted"><f:translate key="legend.deleted" /></span></dd> -</dl> \ No newline at end of file +</dl> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Partials/Navigation.html b/typo3/sysext/workspaces/Resources/Private/Partials/Navigation.html index d7af39211429..cbc66a954812 100644 --- a/typo3/sysext/workspaces/Resources/Private/Partials/Navigation.html +++ b/typo3/sysext/workspaces/Resources/Private/Partials/Navigation.html @@ -1,3 +1,4 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <ul class="x-tab-strip x-tab-strip-top"> <f:for each="{workspaceList}" as="workspace" key="uid"> <f:if condition="{uid}=={activeWorkspaceUid}"> @@ -32,3 +33,4 @@ </f:if> <div class="x-clear"></div> </ul> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Partials/Preview/StageButtons.html b/typo3/sysext/workspaces/Resources/Private/Partials/Preview/StageButtons.html new file mode 100644 index 000000000000..5f74f59d2e44 --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Private/Partials/Preview/StageButtons.html @@ -0,0 +1,11 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> +<f:if condition="{enablePreviousStageButton}"> + <input type="button" class="btn btn-sm btn-default" value="{prevStage}" data-stage-id="{prevStageId}" data-action="send-to-stage" data-direction="prev"> +</f:if> +<f:if condition="{enableNextStageButton}"> + <input type="button" class="btn btn-sm btn-success" value="{nextStage}" data-stage-id="{nextStageId}" data-action="send-to-stage" data-direction="next"> +</f:if> +<f:if condition="{enableDiscardStageButton}"> + <input type="button" class="btn btn-sm btn-danger" value="{f:translate(key: 'label_doaction_discard')}" data-action="discard"> +</f:if> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Partials/WorkingTable.html b/typo3/sysext/workspaces/Resources/Private/Partials/WorkingTable.html new file mode 100644 index 000000000000..d8e0f00bfb3b --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Private/Partials/WorkingTable.html @@ -0,0 +1,94 @@ +<html + xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" + xmlns:core="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers" + data-namespace-typo3-fluid="true" +> +<div role="tabpanel"> + <!-- Nav tabs --> + <ul class="nav nav-tabs" role="tablist"> + <f:for each="{workspaceList}" as="workspace"> + <li role="presentation" {f:if(condition: '{activeWorkspaceUid} == {workspace.workspaceId}', then: 'class="active"')}><a href="{workspace.triggerUrl}" aria-controls="{workspace.itemId}" role="tab">{workspace.title}</a></li> + </f:for> + </ul> + + <!-- Tab panes --> + <div class="tab-content"> + <div role="tabpanel" class="tab-pane active"> + <div class="form-section" id="workspace-panel"> + <form id="workspace-settings-form" class="form-inline form-inline-spaced"> + <div class="form-group"> + <select name="depth" class="form-control" disabled> + <option value="0"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.depth_0" /></option> + <option value="1"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.depth_1" /></option> + <option value="2"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.depth_2" /></option> + <option value="3"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.depth_3" /></option> + <option value="4"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.depth_4" /></option> + <option value="999"><f:translate key="LLL:EXT:lang/locallang_core.xlf:labels.depth_infi" /></option> + </select> + </div> + <div class="form-group"> + <div class="input-group"> + <span class="input-group-addon input-group-icon"></span> + <select name="languages" class="form-control" disabled></select> + </div> + </div> + <div class="form-group"> + <div class="input-group"> + <input class="form-control t3js-clearable" type="text" name="search-text"> + <span class="input-group-btn"> + <button type="submit" class="btn btn-default disabled"><core:icon identifier="actions-search" /></button> + </span> + </div> + </div> + </form> + <div class="hidden" id="workspace-action-icons"> + <f:comment>We pre-render the required icons that are used in the Workspaces module until we're able to use StandaloneView of Fluid</f:comment> + <core:icon identifier="empty-empty" size="small" /> + <core:icon identifier="actions-version-workspace-preview" size="small" /> + <core:icon identifier="actions-version-document-remove" size="small" /> + <core:icon identifier="actions-version-page-open" size="small" /> + <core:icon identifier="actions-version-swap-version" size="small" /> + <core:icon identifier="actions-open" size="small" /> + <core:icon identifier="actions-document-info" size="small" /> + <core:icon identifier="apps-pagetree-expand" size="small" /> + <core:icon identifier="apps-pagetree-collapse" size="small" /> + </div> + <table class="table table-striped"> + <thead> + <tr> + <th><f:translate key="column.wsTitle" /></th> + <th><f:translate key="column.liveTitle" /></th> + <th><f:translate key="column.stage" /></th> + <th><f:translate key="column.integrity" /></th> + <th><core:icon identifier="flags-multiple" size="small" /></th> + <th class="text-right"> + <button type="button" class="btn btn-default t3js-toggle-all"><span class="t3-icon fa fa-check-square-o"></span></button> + </th> + </tr> + </thead> + <tbody> + </tbody> + </table> + <form id="workspace-actions-form" class="form-inline form-inline-spaced"> + <div class="form-group"> + <select name="stage-action" class="form-control" disabled> + <option value=""><f:translate key="chooseAction" /></option> + </select> + </div> + <div class="form-group"> + <select name="selection-action" class="form-control" disabled> + <option value=""><f:translate key="chooseSelectionAction" /></option> + </select> + </div> + <div class="form-group"> + <select name="mass-action" class="form-control" disabled> + <option value=""><f:translate key="chooseMassAction" /></option> + </select> + </div> + </form> + <nav id="workspace-pagination"></nav> + </div> + </div> + </div> +</div> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Ajax/StageButtons.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Ajax/StageButtons.html new file mode 100644 index 000000000000..fac1cc3fceb6 --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Ajax/StageButtons.html @@ -0,0 +1,3 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> +<f:render partial="Preview/StageButtons" arguments="{_all}" /> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html index 9b56cffcc2cc..1dd38b78d490 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Help.html @@ -1,3 +1,5 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <f:layout name="nodoc" /> -<f:section name="main">Help contents - not yet defined</f:section> \ No newline at end of file +<f:section name="main">Help contents - not yet defined</f:section> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Index.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Index.html index 29d353410a5d..60a1162d17b3 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Index.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Index.html @@ -1,3 +1,66 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <f:layout name="popup" /> -<f:section name="main"></f:section> \ No newline at end of file +<f:section name="main"> + <div id="typo3-topbar"> + <div class="typo3-topbar-container" role="navigation" id="typo3-top-container"> + <div class="typo3-topbar-site"> + <a class="typo3-topbar-site-logo" href="{logoLink}" target="_blank"> + <img src="{logoUrl}" width="{logoWidth}" height="{logoHeight}" title="TYPO3 Content Management System" alt=""> + </a> + <span class="typo3-topbar-site-name">{activeWorkspace}</span> + </div> + <div class="typo3-topbar-tabs t3js-workspace-tabs" style="float: left; height: 100%;"> + <ul class="nav nav-tabs" role="tablist" style="position: absolute; bottom: 0"> + <li role="presentation" class="active"><a href="#visual" aria-controls="visual" role="tab" data-toggle="tab" data-actions="true"><f:translate key="preview.visualPreview" /></a></li> + <li role="presentation"><a href="#list" aria-controls="list" role="tab" data-toggle="tab" data-actions="false"><f:translate key="preview.listView" /></a></li> + </ul> + </div> + <div class="typo3-topbar-workspace-actions t3js-workspace-actions"> + <div class="workspace-action"> + <div class="slider-wrapper"> + <b>Live</b><div + id="workspace-stage-slider" + data-slider-min="0" + data-slider-max="100" + data-slider-value="100" + style="width: 150px;" + ></div> + <b>Workspace</b> + </div> + </div> + <div class="workspace-action t3js-stage-buttons"> + <f:render partial="Preview/StageButtons" arguments="{_all}"/> + </div> + <div class="workspace-action t3js-preview-mode"> + <div class="btn-group"> + <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <span class="t3js-active-preview-mode active-preview-mode" data-active-preview-mode="{firstPreviewMode}"><f:translate key="preview.mode{firstPreviewMode -> f:format.case(mode: 'capital')}" /></span> <span class="caret"></span> + </button> + <ul class="dropdown-menu dropdown-menu-right"> + <f:for each="{splitPreviewModes}" as="mode"> + <li><a href="#" data-preview-mode="{mode}"><span><f:translate key="preview.mode{mode -> f:format.case(mode: 'capital')}" /></span></a></li> + </f:for> + </ul> + </div> + </div> + </div> + </div> + </div> + + <div role="tabpanel" class="workspace-panel"> + <div class="tab-content"> + <div role="tabpanel" class="tab-pane active workspaces preview-mode-slider" id="visual"> + <div class="t3js-workspace-preview"> + <iframe src="{liveUrl}" style="height: 0px;" id="live-view"></iframe> + <iframe src="{wsUrl}" id="workspace-view"></iframe> + </div> + </div> + <div role="tabpanel" class="tab-pane workspaces" id="list"> + <iframe src="{wsSettingsUrl}" id="workspace-list"></iframe> + </div> + </div> + + </div> +</f:section> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html index 41fac087cbf6..6207ef52ec84 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/NewPage.html @@ -1,3 +1,5 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <f:layout name="nodoc" /> <f:section name="main"></f:section> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html index 2629a2a34034..0c16f8d5965c 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Preview/Preview.html @@ -1,47 +1,4 @@ <script type="text/javascript"> - - // @todo redirect to split module if this is opened standalone - - // having this is very important, otherwise the parent.resize call will fail + // having this is very important, otherwise the parent.resize call will fail document.domain = '{backendDomain}'; - - var asNumber = function(val) { - return isNaN(val) ? 0 : parseInt(val, 10); - }; - var TYPO3 = TYPO3 || {}; - TYPO3.ready = function () { - // make sure we're in the workspace preview module - if (typeof parent.resize == 'function') { - // try to find the height of the document - var docHeight = Math.max( - asNumber(window.innerHeight), - asNumber(document.height), - asNumber(document.body.scrollHeight), - asNumber(document.body.offsetHeight), - asNumber(document.body.clientHeight), - asNumber(document.documentElement.scrollHeight), - asNumber(document.documentElement.offsetHeight), - asNumber(document.documentElement.clientHeight) - ); - parent.resize(docHeight); - // remove the ugly red box if we're in the ws-repview frames - var element = document.getElementById('typo3-previewInfo'); - if (element) { - element.parentNode.removeChild(element); - } - } - }; - // trigger this after content is loaded, inspired by jQuery - if (document.addEventListener && !/opera/.test(navigator.userAgent.toLowerCase())) { - document.addEventListener("DOMContentLoaded", TYPO3.ready, false); - } else { - (function() { - if (document.readyState != "loaded" && document.readyState != "complete") { - setTimeout(arguments.callee, 10); - } else { - TYPO3.ready(); - } - })(); - } - -</script> +</script> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Review/FullIndex.html b/typo3/sysext/workspaces/Resources/Private/Templates/Review/FullIndex.html index e80d90f926cf..8350fe2f258c 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Review/FullIndex.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Review/FullIndex.html @@ -1,5 +1,9 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <f:layout name="module" /> <f:section name="main"> -<div id="workspacegrid"></div> -</f:section> \ No newline at end of file + + <f:render partial="WorkingTable" arguments="{_all}" /> + +</f:section> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Review/Index.html b/typo3/sysext/workspaces/Resources/Private/Templates/Review/Index.html index a151608a0538..07863b1fc2bb 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Review/Index.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Review/Index.html @@ -1,18 +1,23 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <f:layout name="module" /> <f:section name="main"> -<f:if condition="{performWorkspaceSwitch}"> -<script type="text/javascript"> - top.TYPO3.ModuleMenu.App.refreshMenu(); - top.TYPO3.WorkspacesMenu.performWorkspaceSwitch({activeWorkspaceUid}, "{activeWorkspaceTitle}"); -</script> -</f:if> + <f:if condition="{performWorkspaceSwitch}"> + <script type="text/javascript"> + top.TYPO3.ModuleMenu.App.refreshMenu(); + top.TYPO3.WorkspacesMenu.performWorkspaceSwitch({activeWorkspaceUid}, "{activeWorkspaceTitle}"); + </script> + </f:if> -<f:if condition="{showGrid}"> - <f:then><div id="workspacegrid"></div> - </f:then> - <f:else><f:translate key="editorInLive" /></f:else> -</f:if> + <f:if condition="{showGrid}"> + <f:then> + <f:render partial="WorkingTable" arguments="{_all}" /> + </f:then> + <f:else> + <f:be.infobox message="{f:translate(key: 'editorInLive')}" state="-1" /> + </f:else> + </f:if> -</f:section> \ No newline at end of file +</f:section> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Private/Templates/Review/SingleIndex.html b/typo3/sysext/workspaces/Resources/Private/Templates/Review/SingleIndex.html index a76244a0c042..8a521e25ad9a 100644 --- a/typo3/sysext/workspaces/Resources/Private/Templates/Review/SingleIndex.html +++ b/typo3/sysext/workspaces/Resources/Private/Templates/Review/SingleIndex.html @@ -1,5 +1,9 @@ +<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true"> <f:layout name="nodoc" /> <f:section name="main"> - <div id="workspacegrid"></div> -</f:section> \ No newline at end of file + + <f:render partial="WorkingTable" arguments="{_all}" /> + +</f:section> +</html> \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/Css/module.css b/typo3/sysext/workspaces/Resources/Public/Css/module.css deleted file mode 100644 index b79e023b96be..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/Css/module.css +++ /dev/null @@ -1,290 +0,0 @@ -ul.x-tab-strip.x-tab-strip-top { - margin-bottom: 0; - margin-top: 0; - padding-left: 0; -} -ul.x-tab-strip.x-tab-strip-top li.last { - float: right; -} -span.item-state-modified { - color: #f78f25; -} -span.item-state-moved { - color: #457fb8; -} -span.item-state-new { - color: #3c9934; -} -span.item-state-hidden { - color: #abaaaa; -} -span.item-state-deleted { - color: #000000; - text-decoration: line-through; -} -.legend { - margin: 5px; - height: 18px; - color: #888888; -} -.legend dd, .legend dt { - display: inline; - overflow: hidden; -} -.legend dd span { - display: inline-block; - padding: 4px 4px; -} -.x-toolbar { - background:none !important; -} -.x-grid3 { - background: none !important; -} -.x-grid3-row-expanded { - background-color: #ececec; -} -.x-grid3-row-selected { - color: #4D4D4D; -} -#typo3-mod-php div.typo3-noDoc { - margin: 0px 0px; -} -#typo3-mod-php div.typo3-noDoc #typo3-docbody { - padding: 0px 0px; - top: 0px; -} -.icon-hidden { - display: none; - visibility: hidden; -} -div.x-grid3-row img.x-action-col-icon { - display:none; -} - -div.x-grid3-row-over img.x-action-col-icon, -div.x-grid3-row img.x-action-col-icon.t3-visible { - display:inline-block; -} - -div.t3-workspaces-foldoutWrapper { - padding: 10px; - margin-left: 40px; - background-color: #FFFFFF; - background-image: linear-gradient(center top, #ececec 0px, #f7f7f7 200px); - background-repeat: repeat-x; - color: #606060; -} -.x-grid3-row-selected div.t3-workspaces-foldoutWrapper { - background-image: linear-gradient(center top, #dedede 0px, #f7f7f7 200px); - background-repeat: repeat-x; -} -div.t3-workspaces-foldoutWrapper table { - border-collapse: collapse; - width: 100%; -} -div.t3-workspaces-foldoutWrapper tr.header { - background-color: #B5B5B5; - background-image: linear-gradient(center top, #7f7f7f 10%, #5b5b5b 100%); - background-repeat: repeat-x; -} -div.t3-workspaces-foldoutWrapper tr.header th { - padding: 4px 8px; - color: #FFFFFF; - line-height: 15px; -} - -.t3-workspaces-foldout-subheaderLeft, -.t3-workspaces-foldout-subheaderRight { - padding: 15px 0 10px 0; - margin: 10px; -} -.t3-workspaces-foldout-td-contentDiffLeft .t3-workspaces-foldout-contentDiff-container, -.t3-workspaces-foldout-td-contentDiffRight .t3-workspaces-foldout-contentDiff-container { - padding-top: 10px; - border-top: 1px solid #cdcdcd; -} -.x-grid3-row .t3-workspaces-foldout-td-contentDiffLeft, -.x-grid3-row .t3-workspaces-foldout-subheaderLeft { - padding-right: 10px; -} -.x-grid3-row .t3-workspaces-foldout-td-contentDiffRight, -.x-grid3-row .t3-workspaces-foldout-subheaderRight { - padding-left: 10px; -} -.x-grid3-row .t3-workspaces-foldout-subheaderLeft .t3-workspaces-foldout-subheader-container { - padding-bottom: 10px; - border-bottom: 1px solid #cdcdcd; -} -table.t3-workspaces-foldout-contentDiff { - padding: 8px 8px; - table-layout: auto; - background-color: #ffffff; -} -table.t3-workspaces-foldout-contentDiff th { - padding: 8px 0 8px 8px; - width: 80px; -} -table.t3-workspaces-foldout-contentDiff td { - padding: 8px 8px 8px 0; - line-height: 1.3em; -} -table.t3-workspaces-foldout-contentDiff .diff-r { - text-decoration: line-through; -} -.t3-workspaces-foldout-contentDiff .content ins > img { - padding: 1px; - margin-right: 2px; - border: 2px solid green; -} -.t3-workspaces-foldout-contentDiff .content del > img { - padding: 1px; - margin-right: 2px; - border: 2px solid red; -} -div.t3-workspaces-foldoutWrapper td.char_select_profile_stats { - padding-right: 10px; -} -div.t3-workspaces-comments { - background-color: #dedede; - padding: 10px 10px 10px 10px; -} -div.t3-workspaces-comments-singleComment { - overflow: hidden; - margin-bottom: 10px; - position: relative; -} -div.t3-workspaces-comments-singleComment:last-child { - margin: 0; -} -div .t3-workspaces-comments-singleComment-author { - width: 60px; - margin: 10px 0; - position: absolute; - left: 0px; - top: 0px; - font-weight: bold; - overflow: hidden; -} -div .t3-workspaces-comments-singleComment-content-wrapper { - background: url(../Images/workspaces-comments-arrow.gif) left 10px no-repeat; - margin-left: 70px; -} -div .t3-workspaces-comments-singleComment-content-date { - font-size: 10px; -} -div .t3-workspaces-comments-singleComment-content { - background-color: #ffffff; - padding: 10px 10px; - margin-left: 10px; -} -div .t3-workspaces-comments-singleComment-content-title { - padding: 8px 0 8px 0; - font-weight: bold; -} - -.typo3-workspaces-row-disabled .x-grid3-td-checker { - visibility: hidden; -} - -div.x-grid3-row img.t3-icon-extensions-workspaces { - display: inline-block !important; - width: 17px; - visibility: hidden; -} - -div.x-grid3-row-over img.t3-icon-extensions-workspaces { - visibility: visible; -} - -img.t3-icon-workspaces-sendtonextstage { - background: url(../Images/version-workspace-sendtonextstage.png) no-repeat; -} - -img.t3-icon-workspaces-sendtoprevstage { - background: url(../Images/version-workspace-sendtoprevstage.png) no-repeat; -} - -table.t3-workspaces-foldout-contentDiff td.content { - word-break: break-all; -} - -.typo3-workspaces-collection-level-node, -.typo3-workspaces-collection-level-leaf, -.typo3-workspaces-collection-level-none { - float: left; - width: 18px; -} - -.x-grid3-row-expander { - background-position: left top; - float: left; -} -.x-grid3-row-collapsed .x-grid3-row-expander { - background-position: left top; - background-image: url('../Images/zoom_in.png'); -} -.x-grid3-row-expanded .x-grid3-row-expander { - background-position: left top; - background-image: url('../Images/zoom_out.png'); -} - -.typo3-workspaces-collection-child-collapsed { - display: none; -} - -.typo3-workspaces-collection-child-expanded { - display: block; -} - -.typo3-workspaces-collection-level-node { - width: 18px; - height: 18px; - background-position: 4px 2px; - background-color: transparent; - background-repeat: no-repeat; - background-image: url('../../../../t3skin/extjs/images/grid/row-expand-sprite.png'); -} -.typo3-workspaces-collection-parent-collapsed .typo3-workspaces-collection-level-node { - background-position: 4px 2px; -} -.typo3-workspaces-collection-parent-expanded .typo3-workspaces-collection-level-node { - background-position: -21px 2px; -} - -.x-menu.typo3-workspaces-menu { - background-image: none; -} -.x-menu.typo3-workspaces-menu a.x-menu-item { - padding: 3px 14px; -} - -.x-tab-menu-right { - background: #dadada; - border-color: #adadad; - border-style: solid; - border-width: 1px; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - color: #666666; - background-position: center 6px; - padding: 4px; - - background-image: url('../Images/menu.png'); - background-repeat: no-repeat; - width: 16px; - position: absolute; - right: 0; - top: 0; - z-index: 10; - cursor:pointer; -} - -.t3-workspaces-foldoutWrapper .char_select_profile_titleLeft .icon, -.t3-workspaces-foldoutWrapper .char_select_profile_titleRight .icon{ - float: left; -} - -.t3-workspaces-foldoutWrapper .t3-workspaces-foldout-contentDiff { - text-align: left; -} diff --git a/typo3/sysext/workspaces/Resources/Public/Css/preview.css b/typo3/sysext/workspaces/Resources/Public/Css/preview.css index 76fcaddf3804..257bca294d20 100644 --- a/typo3/sysext/workspaces/Resources/Public/Css/preview.css +++ b/typo3/sysext/workspaces/Resources/Public/Css/preview.css @@ -1,353 +1,64 @@ -/** - * Top bar +/*! + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! */ - -/** - * Tabs - */ - -.x-btn button { - font-size: 12px; -} - -/* panel containing the tabs */ -.x-tab-panel-header { - background-color: #3f3f3f; - background-image: url('../../../../../../typo3/sysext/t3skin/extjs/images/backgrounds/topbar.png'); - background-image: linear-gradient(center top, #494949 0%, #373737 91%, #343434 92%, #2A2A2A 100%); - background-repeat: repeat-x; - border: none; - padding-bottom: 0; - padding-top: 9px; -} - -.x-tab-strip-wrap { - background: url('../Images/typo3-logo.png') no-repeat 16px 0; - padding-left: 140px; -} - -/* normal tab */ -ul.x-tab-strip li { - margin-left: 4px; -} - -ul.x-tab-strip-top { - border-bottom: 0; -} - -/* reset ExtJS "no skin" nonsense */ -.x-tab-strip-top .x-tab-right, -.x-tab-strip-top .x-tab-strip-over .x-tab-right { - background-position: 0 0; -} - -/* we don't need the active tab to be 1px below the inactive */ -.x-tab-strip-top .x-tab-strip-active .x-tab-right { - margin-bottom: 0; -} - -.x-tab-strip span.x-tab-strip-text, -.x-tab-strip-top .x-tab-strip-active .x-tab-right span.x-tab-strip-text { - padding-bottom: 4px; -} - -/* normal tab styling */ -.x-tab-strip .x-tab-right { - background-color: #707171; - background-image: linear-gradient(center top, #707171 0%, #474747 85%, #363636 100%); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#707171', EndColorStr='#474747')"; /* IE8 */ - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} - -/* container surrounding text */ -.x-tab-strip-inner { - padding: 4px; -} - -/* hover tab */ -.x-tab-strip-over .x-tab-right { - background-color: #707171; - background-image: linear-gradient(center top, #888888 0%, #474747 85%, #363636 100%); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#888888', EndColorStr='#474747')"; /* IE8 */ -} - -/* active tab */ -.x-tab-strip-active .x-tab-right { - background-color: #989898; - background-image: linear-gradient(center top, #989898 0%, #6c6c6c 85%, #474747 100%); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#989898', EndColorStr='#6c6c6c')"; /* IE8 */ -} - -/* tab label */ -.x-tab-strip span.x-tab-strip-text { - color: #ffffff; -} - -/* text in tabs and buttons should not be italic.. why should it be? */ -.x-tab-strip em, -.x-btn-text { - font-style: normal; -} - -/** - * Slider - */ -#controls { - padding-top: 5px; -} -#slider { - padding-top: 5px; -} - -/* remove default ExtJS border */ -.x-panel-body { - border: none; -} - -.x-slider-horz .x-slider-thumb { - background-image: url('../Images/slider-thumb.png'); - height: 21px; - padding: 0 2px; - top: 0; - width: 7px; -} - -.x-slider-horz .x-slider-thumb-over, .x-slider-horz .x-slider-thumb-drag { - background-position: -7px -21px; -} - -.x-slider-horz .x-slider-inner { - background-image: url('../Images/slider-bg.png'); -} - -#visual-mode-selector { - list-style: none; - background-color: #f9f9f9; - border: 1px solid #abb2bc; - border-top: none; -} - -#visual-mode-selector td { - text-align: left; -} - -#visual-mode-selector td button { - text-decoration: none; -} - -#visual-mode-options { - display: block; - height: 20px; - margin: 0px 0 0 10px; -} -#visual-mode-options.x-btn-menu-active { - background-color: #f9f9f9; - border: 1px solid #abb2bc; - border-bottom: none; -} -#visual-mode-options .x-btn-arrow { - padding-right: 2px; -} - -#visual-mode-options.x-btn-menu-active .x-btn-text { - color: black; -} - -#visual-mode-toolbar { - border:none; -} - -/** - * Preview panel - */ -.x-panel-body-noheader { - border-top: 0; -} - -.x-tip { - background-color: #ffffc7; - border: 1px solid #cccccc; - box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.3); -} - -/* the mask for dialogs */ -.ext-el-mask { - background-color: #000000; - opacity: 0.75; - filter:alpha(opacity=75); -} - -.x-mask-loading { - border: none; -} - -.x-mask-loading div { - background-image: url("../../../../t3skin/images/spinner/big-f0f0f0.gif"); - background-position: top center; - border: none; - color: #828282; - padding-top: 40px; -} - -.x-window-tc { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/top-bottom.png); -} -.x-window-tl { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/left-corners.png); -} -.x-window-tr { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/right-corners.png); -} -.x-window-bc { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/top-bottom.png); -} -.x-window-bl { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/left-corners.png); -} -.x-window-br { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/right-corners.png); -} - -.x-window-ml { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/left-right.png); -} -.x-window-mr { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/window/left-right.png); -} - -.x-window-mc { - border:1px solid #A2AAB8; - background:#e8e8e8; -} - -.x-window-tl .x-window-header { - color: #FFFFFF; - font: bold 10px verdana,arial,tahoma,verdana,sans-serif; - padding: 4px 0; -} -.x-window-draggable, .x-window-draggable .x-window-header-text { - cursor: move; -} - -.x-window-tl .x-window-header { - color:#fff; - font:bold 10px verdana, arial,tahoma,verdana,sans-serif; - padding:4px 0 4px 0; -} - -.x-window-mc { - border-color:#A2AAB8; - font: normal 10px verdana, arial,tahoma,helvetica,sans-serif; - background-color:#e7e7e7; -} - -.x-window-maximized .x-window-tc { - background-color:#fff; -} - -.x-window-bbar .x-toolbar { - border-top-color:#A2AAB8; -} - -.x-form-text, textarea.x-form-field { - background-color: #FFFFFF; - background-image: none; - border-color: #B5B8C8; -} - -.x-btn { - color: #FFF; -} -.t3-icon-system-options-view { - float: right; -} -#feToolbarButtonNextStage.x-btn, #feToolbarButtonPreviousStage.x-btn, #feToolbarButtonDiscardStage.x-btn { - background: url('../Images/button_approve.png') #55A245 repeat-x; - border: 1px solid #7c7c7c; - border-radius: 1px; - margin-right: 10px; - margin-top:0px; -} -#feToolbarButtonNextStage.x-btn .x-btn-text, #feToolbarButtonPreviousStage.x-btn .x-btn-text, #feToolbarButtonDiscardStage.x-btn .x-btn-text { - color: #FFF; - line-height: 8px; - height: 13px; - padding: 0 3px 0 3px; -} -#feToolbarButtonPreviousStage.x-btn .x-btn-text { - color:#7c7c7c; +.module-body { + padding: 0; } -#feToolbarButtonPreviousStage.x-btn { - background-color: #D5D5D5; - background-image: url('../Images/button_reject.png'); +.typo3-topbar-workspace-actions { + float: right; + height: 100%; + display: table; } -#feToolbarButtonDiscardStage.x-btn { - background-color: #CD0505; - background-image: url('../Images/button_discard.png'); +.workspace-action { + display: table-cell; + vertical-align: middle; + padding: 0 4px; } -#sendToStageWindow .x-btn { - background-color: #d5d5d5; - background-image: url('../../../../../../typo3/sysext/t3skin/extjs/images/backgrounds/button.png'); - background-repeat: repeat-x; - background-image: linear-gradient(center top, #f6f6f6 10%, #d5d5d5 90%); - border: 1px solid #7c7c7c; - border-radius: 1px; - color: #434343; +.workspace-action:last-child { + padding-right: 16px; } -#sendToStageWindow .x-btn-pressed, -#sendToStageWindow .x-btn-over, -#sendToStageWindow .x-btn-icon.x-btn-over { - background-color: #bdbcbc; - background-image: url('../../../../../../typo3/sysext/t3skin/extjs/images/backgrounds/button-hover.png'); - background-image: linear-gradient(center top, #f6f6f6 10%, #bdbcbc 90%); - border-color: #737f91; - color: #1e1e1e; +.workspace-action .active-preview-mode { + display: inline-block; + text-align: left; } - -#sendToStageWindow .x-btn-over .x-btn-mc em.x-btn-split, -#sendToStageWindow .x-btn-click .x-btn-mc em.x-btn-split, -#sendToStageWindow .x-btn-menu-active .x-btn-mc em.x-btn-split, -#sendToStageWindow .x-btn-pressed .x-btn-mc em.x-btn-split { - background-image:url(../../../../../../typo3/sysext/t3skin/extjs/images/button/s-arrow-o.gif); +.workspace-action .slider-wrapper .slider { + float: left; } - -.x-tool { - background-image: url("../../../../../../typo3/sysext/t3skin/extjs/images/panel/tool-sprites.gif"); +.workspace-action .slider-wrapper b { + margin: 7px 10px; + display: block; + float: left; } - -.x-tool-close { - background-position: 0 0; +.workspaces { + position: relative; } -.x-tool-close-over { - background-position: -15px 0; +.workspaces iframe { + border: 0; } - -/* text */ -.x-btn.sliderButton .x-btn-text { - color: #A0A0A0; - font-style: normal; +.preview-mode-slider iframe { + position: absolute; + top: 0; + z-index: 100; } - -/* alignment of text in Button "Live" */ -#sizeSliderButtonLive .x-btn-mc { - text-align: right; +.preview-mode-slider #live-view { + border-bottom: 2px solid #c83c3c; + z-index: 200; } - -/* alignment of text in Button "Workspace" */ -#sizeSliderButtonWorkspace .x-btn-mc { - text-align: left; +.preview-mode-vbox iframe { + width: 50%; + height: 100%; + float: left; } -.x-panel-header { - border: none; - font-weight: bold; - padding-left:0px; +.preview-mode-hbox iframe { + width: 100%; + height: 50%; } - -.x-window-dlg .x-btn { - background-color: #D5D5D5; - background-image: linear-gradient(center top , #F6F6F6 10%, #D5D5D5 90%); - border-radius: 1px 1px 1px 1px; - border: 1px solid #7C7C7C; - color: #434343; -} \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Backend.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Backend.js new file mode 100644 index 000000000000..bbea867b8328 --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/Backend.js @@ -0,0 +1,1191 @@ +/* +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * RequireJS module for the workspace backend module + */ +define([ + 'jquery', + 'TYPO3/CMS/Workspaces/Workspaces', + 'TYPO3/CMS/Backend/Tooltip', + 'TYPO3/CMS/Backend/Severity', + 'TYPO3/CMS/Backend/Modal', + 'TYPO3/CMS/Backend/Wizard', + 'nprogress', + 'TYPO3/CMS/Backend/jquery.clearable' +], function($, Workspaces, Tooltip, Severity, Modal, Wizard, NProgress) { + 'use strict'; + + var Backend = { + workspaceTitle: '', + identifiers: { + searchForm: '#workspace-settings-form', + searchTextField: '#workspace-settings-form input[name="search-text"]', + searchSubmitBtn: '#workspace-settings-form button[type="submit"]', + depthSelector: '#workspace-settings-form [name="depth"]', + languageSelector: '#workspace-settings-form select[name="languages"]', + actionForm: '#workspace-actions-form', + chooseStageAction: '#workspace-actions-form [name="stage-action"]', + chooseSelectionAction: '#workspace-actions-form [name="selection-action"]', + chooseMassAction: '#workspace-actions-form [name="mass-action"]', + container: '#workspace-panel', + actionIcons: '#workspace-action-icons', + toggleAll: '.t3js-toggle-all', + previewLinksButton: '.t3js-preview-link', + pagination: '#workspace-pagination' + }, + settings: { + depth: TYPO3.settings.Workspaces.depth, + dir: 'ASC', + id: TYPO3.settings.Workspaces.id, + language: TYPO3.settings.Workspaces.language, + limit: 30, + query: '', + sort: 'label_Live', + start: 0, + filterTxt: '' + }, + paging: { + currentPage: 1, + totalPages: 1, + totalItems: 0 + }, + allToggled: false, + elements: {}, // filled in Backend.getElements() + latestPath: '', + markedRecordsForMassAction: [] + }; + + Backend.initialize = function() { + Backend.getElements(); + Backend.registerEvents(); + + if (TYPO3.settings.Workspaces.depth > 0) { + Backend.elements.$depthSelector.val(TYPO3.settings.Workspaces.depth); + } + + Backend.loadWorkspaceComponents(); + }; + + Backend.getElements = function() { + Backend.elements.$searchForm = $(Backend.identifiers.searchForm); + Backend.elements.$searchTextField = $(Backend.identifiers.searchTextField); + Backend.elements.$searchSubmitBtn = $(Backend.identifiers.searchSubmitBtn); + Backend.elements.$depthSelector = $(Backend.identifiers.depthSelector); + Backend.elements.$languageSelector = $(Backend.identifiers.languageSelector); + Backend.elements.$container = $(Backend.identifiers.container); + Backend.elements.$tableBody = Backend.elements.$container.find('tbody'); + Backend.elements.$actionIcons = $(Backend.identifiers.actionIcons); + Backend.elements.$toggleAll = $(Backend.identifiers.toggleAll); + Backend.elements.$chooseStageAction = $(Backend.identifiers.chooseStageAction); + Backend.elements.$chooseSelectionAction = $(Backend.identifiers.chooseSelectionAction); + Backend.elements.$chooseMassAction = $(Backend.identifiers.chooseMassAction); + Backend.elements.$previewLinksButton = $(Backend.identifiers.previewLinksButton); + Backend.elements.$pagination = $(Backend.identifiers.pagination); + }; + + Backend.registerEvents = function() { + $(document).on('click', '[data-action="swap"]', function(e) { + var $tr = $(e.target).closest('tr'); + Workspaces.checkIntegrity( + { + selection: [ + { + liveId: $tr.data('uid'), + versionId: $tr.data('t3ver_oid'), + table: $tr.data('table') + } + ], + type: 'selection' + } + ).done(function(response) { + if (response[0].result.result === 'warning') { + Backend.addIntegrityCheckWarningToWizard(); + } + + Wizard.setup.forceSelection = false; + Wizard.addSlide( + 'swap-confirm', + 'Swap', + TYPO3.lang['window.swap.message'], + Severity.info + ); + Wizard.addFinalProcessingSlide(function() { + // We passed this slide, swap the record now + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload('swapSingleRecord', [ + $tr.data('table'), + $tr.data('t3ver_oid'), + $tr.data('uid') + ]) + ).done(function() { + Wizard.dismiss(); + Backend.getWorkspaceInfos(); + Backend.refreshPageTree(); + }); + }).done(function() { + Wizard.show(); + }); + }); + }).on('click', '[data-action="prevstage"]', function(e) { + Backend.sendToStage($(e.target).closest('tr'), 'prev'); + }).on('click', '[data-action="nextstage"]', function(e) { + Backend.sendToStage($(e.target).closest('tr'), 'next'); + }).on('click', '[data-action="changes"]', Backend.viewChanges + ).on('click', '[data-action="preview"]', Backend.openPreview + ).on('click', '[data-action="open"]', function(e) { + var $tr = $(e.target).closest('tr'), + newUrl = TYPO3.settings.FormEngine.moduleUrl + '&returnUrl=' + encodeURIComponent(document.location.href) + '&id=' + TYPO3.settings.Workspaces.id + '&edit[' + $tr.data('table') + '][' + $tr.data('uid') + ']=edit'; + + // Append workspace of record in all-workspaces view + if (TYPO3.settings.Workspaces.allView) { + newUrl += '&workspace=' + $tr.data('t3ver_wsid'); + } + window.location.href = newUrl; + }).on('click', '[data-action="version"]', function(e) { + var $tr = $(e.target).closest('tr'); + if ($tr.data('table') === 'pages') { + top.loadEditId($tr.data('t3ver_oid')); + } else { + top.loadEditId($tr.data('pid')); + } + }).on('click', '[data-action="remove"]', Backend.confirmDeleteRecordFromWorkspace + ).on('click', '[data-action="expand"]', function(e) { + var $me = $(this), + $target = Backend.elements.$tableBody.find($me.data('target')), + iconIdentifier; + + if ($target.first().attr('aria-expanded') === 'true') { + iconIdentifier = 'apps-pagetree-expand'; + } else { + iconIdentifier = 'apps-pagetree-collapse'; + } + + $me.html(Backend.getPreRenderedIcon(iconIdentifier)); + }); + + Backend.elements.$searchForm.on('submit', function(e) { + e.preventDefault(); + Backend.settings.filterTxt = Backend.elements.$searchTextField.val(); + Backend.getWorkspaceInfos(); + }); + + Backend.elements.$searchTextField.on('keyup', function() { + var $me = $(this); + + if ($me.val() !== '') { + Backend.elements.$searchSubmitBtn.removeClass('disabled'); + } else { + Backend.elements.$searchSubmitBtn.addClass('disabled'); + Backend.getWorkspaceInfos(); + } + }).clearable( + { + onClear: function() { + Backend.elements.$searchSubmitBtn.addClass('disabled'); + Backend.settings.filterTxt = ''; + Backend.getWorkspaceInfos(); + } + } + ); + + // checkboxes in the table + Backend.elements.$toggleAll.on('click', function() { + Backend.allToggled = !Backend.allToggled; + Backend.elements.$tableBody.find('input[type="checkbox"]').prop('checked', Backend.allToggled).trigger('change'); + }); + Backend.elements.$tableBody.on('change', 'tr input[type=checkbox]', Backend.handleCheckboxChange); + + // Listen for depth changes + Backend.elements.$depthSelector.on('change', function(e) { + var $me = $(this); + Backend.settings.depth = $me.val(); + + Backend.getWorkspaceInfos(); + }); + + // Generate preview links + Backend.elements.$previewLinksButton.on('click', Backend.generatePreviewLinks); + + // Listen for language changes + Backend.elements.$languageSelector.on('change', function(e) { + var $me = $(this); + Backend.settings.language = $me.val(); + + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectActionsPayload('saveLanguageSelection', [$me.val()]), + Workspaces.generateExtDirectPayload('getWorkspaceInfos', Backend.settings) + ]).done(function(response) { + Backend.elements.$languageSelector.prev().html($me.find(':selected').data('icon')); + Backend.renderWorkspaceInfos(response[1].result); + }); + }); + + // Listen for actions + Backend.elements.$chooseStageAction.on('change', Backend.sendToSpecificStageAction); + Backend.elements.$chooseSelectionAction.on('change', Backend.runSelectionAction); + Backend.elements.$chooseMassAction.on('change', Backend.runMassAction); + + // clicking an action in the paginator + Backend.elements.$pagination.on('click', 'a[data-action]', function(e) { + e.preventDefault(); + + var $el = $(this), + reload = false; + + switch ($el.data('action')) { + case 'previous': + if (Backend.paging.currentPage > 1) { + Backend.paging.currentPage--; + reload = true; + } + break; + case 'next': + if (Backend.paging.currentPage < Backend.paging.totalPages) { + Backend.paging.currentPage++; + reload = true; + } + break; + case 'page': + Backend.paging.currentPage = parseInt($el.data('page')); + reload = true; + break; + } + + if (reload) { + // Adjust settings + Backend.settings.start = Backend.settings.limit * (Backend.paging.currentPage - 1); + Backend.getWorkspaceInfos(); + } + }); + }; + + Backend.handleCheckboxChange = function(e) { + var $checkbox = $(this), + $tr = $checkbox.parents('tr'), + table = $tr.data('table'), + uid = $tr.data('uid'), + t3ver_oid = $tr.data('t3ver_oid'), + record = table + ':' + uid + ':' + t3ver_oid; + + if ($checkbox.prop('checked')) { + Backend.markedRecordsForMassAction.push(record); + $tr.addClass('warning'); + } else { + var index = Backend.markedRecordsForMassAction.indexOf(record); + if (index > -1) { + Backend.markedRecordsForMassAction.splice(index, 1); + } + $tr.removeClass('warning'); + } + + Backend.elements.$chooseStageAction.prop('disabled', Backend.markedRecordsForMassAction.length === 0); + Backend.elements.$chooseSelectionAction.prop('disabled', Backend.markedRecordsForMassAction.length === 0); + Backend.elements.$chooseMassAction.prop('disabled', Backend.markedRecordsForMassAction.length > 0); + }; + + /** + * Generates the diff view of a record + * + * @param {Object} diff + * @return {$} + */ + Backend.generateDiffView = function(diff) { + var $diff = $('<div />', {class: 'diff'}); + + for (var i = 0; i < diff.length; ++i) { + $diff.append( + $('<div />', {class: 'diff-item'}).append( + $('<div />', {class: 'diff-item-title'}).text(diff[i].label), + $('<div />', {class: 'diff-item-result diff-item-result-inline'}).html(diff[i].content) + ) + ); + } + return $diff; + }; + + /** + * Generates the comments view of a record + * + * @param {Object} comments + * @return {$} + */ + Backend.generateCommentView = function(comments) { + var $comments = $('<div />'); + + for (var i = 0; i < comments.length; ++i) { + var $panel = $('<div />', {class: 'panel panel-default'}); + + if (comments[i].user_comment.length > 0) { + $panel.append( + $('<div />', {class: 'panel-body'}).html(comments[i].user_comment) + ); + } + + $panel.append( + $('<div />', {class: 'panel-footer'}).append( + $('<span />', {class: 'label label-success'}).text(comments[i].stage_title), + $('<span />', {class: 'label label-info'}).text(comments[i].tstamp) + ) + ); + + $comments.append( + $('<div />', {class: 'media'}).append( + $('<div />', {class: 'media-left text-center'}).text(comments[i].user_username).prepend( + $('<div />').html(comments[i].user_avatar) + ), + $('<div />', {class: 'media-body'}).append($panel) + ) + ); + } + + return $comments; + }; + + /** + * Sends a record to a stage + * + * @param {Object} $row + * @param {String} direction + */ + Backend.sendToStage = function($row, direction) { + var nextStage, + stageWindowAction, + stageExecuteAction; + + if (direction === 'next') { + nextStage = $row.data('nextStage'); + stageWindowAction = 'sendToNextStageWindow'; + stageExecuteAction = 'sendToNextStageExecute'; + } else if (direction === 'prev') { + nextStage = $row.data('prevStage'); + stageWindowAction = 'sendToPrevStageWindow'; + stageExecuteAction = 'sendToPrevStageExecute'; + } else { + throw 'Invalid direction given.'; + } + + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload(stageWindowAction, [ + $row.data('uid'), $row.data('table'), $row.data('t3ver_oid') + ]) + ).done(function(response) { + var $modal = Workspaces.renderSendToStageWindow(response); + $modal.on('button.clicked', function(e) { + if (e.target.name === 'ok') { + var $form = $(e.currentTarget).find('form'), + serializedForm = $form.serializeObject(); + + serializedForm.affects = { + table: $row.data('table'), + nextStage: nextStage, + t3ver_oid: $row.data('t3ver_oid'), + uid: $row.data('uid'), + elements: [] + }; + + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectActionsPayload(stageExecuteAction, [serializedForm]), + Workspaces.generateExtDirectPayload('getWorkspaceInfos', Backend.settings) + ]).done(function(response) { + $modal.modal('hide'); + Backend.renderWorkspaceInfos(response[1].result); + Backend.refreshPageTree(); + }); + } + }); + }); + }; + + /** + * Loads the workspace components, like available stage actions and items of the workspace + */ + Backend.loadWorkspaceComponents = function() { + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectPayload('getWorkspaceInfos', Backend.settings), + Workspaces.generateExtDirectPayload('getStageActions', {}), + Workspaces.generateExtDirectMassActionsPayload('getMassStageActions', {}), + Workspaces.generateExtDirectPayload('getSystemLanguages', {}) + ]).done(function(response) { + Backend.elements.$depthSelector.prop('disabled', false); + + // Records + Backend.renderWorkspaceInfos(response[0].result); + + // Stage actions + var stageActions = response[1].result.data, + i; + for (i = 0; i < stageActions.length; ++i) { + Backend.elements.$chooseStageAction.append( + $('<option />').val(stageActions[i].uid).text(stageActions[i].title) + ); + } + + // Mass actions + var massActions = response[2].result.data; + for (i = 0; i < massActions.length; ++i) { + Backend.elements.$chooseSelectionAction.append( + $('<option />').val(massActions[i].action).text(massActions[i].title) + ); + + Backend.elements.$chooseMassAction.append( + $('<option />').val(massActions[i].action).text(massActions[i].title) + ); + } + + // Languages + var languages = response[3].result.data; + for (i = 0; i < languages.length; ++i) { + var $option = $('<option />').val(languages[i].uid).text(languages[i].title).data('icon', languages[i].icon); + if (String(languages[i].uid) === String(TYPO3.settings.Workspaces.language)) { + $option.prop('selected', true); + Backend.elements.$languageSelector.prev().html(languages[i].icon); + } + Backend.elements.$languageSelector.append($option); + } + Backend.elements.$languageSelector.prop('disabled', false); + }); + }; + + /** + * Gets the workspace infos + * + * @return {Promise} + * @protected + */ + Backend.getWorkspaceInfos = function() { + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectPayload('getWorkspaceInfos', Backend.settings) + ).done(function(response) { + Backend.renderWorkspaceInfos(response[0].result); + }); + }; + + /** + * Renders fetched workspace informations + * + * @param {Object} result + */ + Backend.renderWorkspaceInfos = function(result) { + Backend.elements.$tableBody.children().remove(); + Backend.allToggled = false; + Backend.elements.$chooseStageAction.prop('disabled', true); + Backend.elements.$chooseSelectionAction.prop('disabled', true); + Backend.elements.$chooseMassAction.prop('disabled', result.data.length === 0); + + Backend.buildPagination(result.total); + + for (var i = 0; i < result.data.length; ++i) { + var item = result.data[i], + $actions = $('<div />', {class: 'btn-group'}), + $integrityIcon = ''; + + $actions.append( + Backend.getAction(item.Workspaces_CollectionChildren > 0 && item.Workspaces_CollectionCurrent !== '', 'expand', 'apps-pagetree-collapse').attr('title', TYPO3.lang['tooltip.swap']).attr('data-target', '[data-collection="' + item.Workspaces_CollectionCurrent + '"]').attr('data-toggle', 'collapse'), + $('<button />', {class: 'btn btn-default', 'data-action': 'changes', 'data-toggle': 'tooltip', title: TYPO3.lang['tooltip.showChanges']}).append(Backend.getPreRenderedIcon('actions-document-info')), + Backend.getAction(item.allowedAction_swap && item.Workspaces_CollectionParent === '', 'swap', 'actions-version-swap-version').attr('title', TYPO3.lang['tooltip.swap']), + Backend.getAction(item.allowedAction_view, 'preview', 'actions-version-workspace-preview').attr('title', TYPO3.lang['tooltip.viewElementAction']), + $('<button />', {class: 'btn btn-default', 'data-action': 'open', 'data-toggle': 'tooltip', title: TYPO3.lang['tooltip.editElementAction']}).append(Backend.getPreRenderedIcon('actions-open')), + $('<button />', {class: 'btn btn-default', 'data-action': 'version', 'data-toggle': 'tooltip', title: TYPO3.lang['tooltip.openPage']}).append(Backend.getPreRenderedIcon('actions-version-page-open')), + Backend.getAction(item.allowedAction_delete, 'remove', 'actions-version-document-remove').attr('title', TYPO3.lang['tooltip.discardVersion']), + $('<label />', {class: 'btn btn-default btn-checkbox'}).append( + $('<input />', {type: 'checkbox'}), + $('<span />', {class: 't3-icon fa'}) + ) + ); + + if (item.integrity.messages !== '') { + $integrityIcon = $(TYPO3.settings.Workspaces.icons[item.integrity.status]); + $integrityIcon + .attr('data-toggle', 'tooltip') + .attr('data-placement', 'top') + .attr('data-html', true) + .attr('title', item.integrity.messages); + } + + if (Backend.latestPath !== item.path_Workspace) { + Backend.latestPath = item.path_Workspace; + Backend.elements.$tableBody.append( + $('<tr />').append( + $('<th />', {colspan: 6}).text(Backend.latestPath) + ) + ); + } + + var rowConfiguration = { + 'data-uid': item.uid, + 'data-pid': item.livepid, + 'data-t3ver_oid': item.t3ver_oid, + 'data-t3ver_wsid': item.t3ver_wsid, + 'data-table': item.table, + 'data-next-stage': item.value_nextStage, + 'data-prev-stage': item.value_prevStage, + 'data-stage': item.stage + }; + + if (item.Workspaces_CollectionParent !== '') { + rowConfiguration['data-collection'] = item.Workspaces_CollectionParent; + rowConfiguration['class'] = 'collapse'; + } + + Backend.elements.$tableBody.append( + $('<tr />', rowConfiguration).append( + $('<td />', {class: 't3js-title-workspace'}).html(item.icon_Workspace + ' ' + '<a href="#" data-action="changes"><span class="item-state-' + item.state_Workspace + '">' + item.label_Workspace + '</span></a>'), + $('<td />', {class: 't3js-title-live'}).html(item.icon_Live + ' ' + item.label_Live), + $('<td />').text(item.label_Stage), + $('<td />').html($integrityIcon), + $('<td />').html(item.language.icon), + $('<td />', {class: 'text-right', nowrap: 'nowrap'}).append($actions) + ) + ); + + Tooltip.initialize('[data-toggle="tooltip"]', { + delay: { + show: 500, + hide: 100 + }, + trigger: 'hover', + container: 'body' + }); + } + }; + + /** + * Renders the pagination + * + * @param {Number} totalItems + */ + Backend.buildPagination = function(totalItems) { + if (totalItems === 0) { + Backend.elements.$pagination.contents().remove(); + return; + } + + Backend.paging.totalItems = totalItems; + Backend.paging.totalPages = Math.ceil(totalItems / Backend.settings.limit); + + if (Backend.paging.totalPages === 1) { + // early abort if only one page is available + Backend.elements.$pagination.contents().remove(); + return; + } + + var $ul = $('<ul />', {class: 'pagination pagination-block'}), + liElements = [], + $controlFirstPage = $('<li />').append( + $('<a />', {'data-action': 'previous'}).append( + $('<span />', {class: 't3-icon fa fa-arrow-left'}) + ) + ), + $controlLastPage = $('<li />').append( + $('<a />', {'data-action': 'next'}).append( + $('<span />', {class: 't3-icon fa fa-arrow-right'}) + ) + ); + + if (Backend.paging.currentPage === 1) { + $controlFirstPage.disablePagingAction(); + } + + if (Backend.paging.currentPage === Backend.paging.totalPages) { + $controlLastPage.disablePagingAction(); + } + + for (var i = 1; i <= Backend.paging.totalPages; i++) { + var $li = $('<li />', {class: Backend.paging.currentPage === i ? 'active' : ''}); + $li.append( + $('<a />', {'data-action': 'page', 'data-page': i}).append( + $('<span />').text(i) + ) + ); + liElements.push($li); + } + + $ul.append($controlFirstPage, liElements, $controlLastPage); + Backend.elements.$pagination.html($ul); + }; + + /** + * View changes of a record + * + * @param {Event} e + */ + Backend.viewChanges = function(e) { + e.preventDefault(); + + var $tr = $(e.target).closest('tr'); + + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectPayload('getRowDetails', { + stage: $tr.data('stage'), + t3ver_oid: $tr.data('t3ver_oid'), + table: $tr.data('table'), + uid: $tr.data('uid') + }) + ).done(function(response) { + var item = response[0].result.data[0], + $content = $('<div />'), + $tabsNav = $('<ul />', {class: 'nav nav-tabs', role: 'tablist'}), + $tabsContent = $('<div />', {class: 'tab-content'}), + modalButtons = []; + + $content.append( + $('<p />').html(TYPO3.lang['path'].replace('{0}', item.path_Live)), + $('<p />').html(TYPO3.lang['current_step'].replace('{0}', item.label_Stage).replace('{1}', item.stage_position).replace('{2}', item.stage_count)) + ); + + if (item.diff.length > 0) { + $tabsNav.append( + $('<li />', {role: 'presentation'}).append( + $('<a />', {href: '#workspace-changes', 'aria-controls': 'workspace-changes', role: 'tab', 'data-toggle': 'tab'}).text(TYPO3.lang['window.recordChanges.tabs.changeSummary']) + ) + ); + $tabsContent.append( + $('<div />', {role: 'tabpanel', class: 'tab-pane', id: 'workspace-changes'}).append( + $('<div />', {class: 'form-section'}).append( + Backend.generateDiffView(item.diff) + ) + ) + ); + } + + if (item.comments.length > 0) { + $tabsNav.append( + $('<li />', {role: 'presentation'}).append( + $('<a />', {href: '#workspace-comments', 'aria-controls': 'workspace-comments', role: 'tab', 'data-toggle': 'tab'}).html(TYPO3.lang['window.recordChanges.tabs.comments'] + ' ').append( + $('<span />', {class: 'badge'}).text(item.comments.length) + ) + ) + ); + $tabsContent.append( + $('<div />', {role: 'tabpanel', class: 'tab-pane', id: 'workspace-comments'}).append( + $('<div />', {class: 'form-section'}).append( + Backend.generateCommentView(item.comments) + ) + ) + ); + } + + if (item.history.total > 0) { + $tabsNav.append( + $('<li />', {role: 'presentation'}).append( + $('<a />', {href: '#workspace-history', 'aria-controls': 'workspace-history', role: 'tab', 'data-toggle': 'tab'}).text(TYPO3.lang['window.recordChanges.tabs.history']) + ) + ); + + $tabsContent.append( + $('<div />', {role: 'tabpanel', class: 'tab-pane', id: 'workspace-history'}).append( + $('<div />', {class: 'form-section'}).append( + Backend.generateHistoryView(item.history.data) + ) + ) + ); + } + + // Mark the first tab and pane as active + $tabsNav.find('li').first().addClass('active'); + $tabsContent.find('.tab-pane').first().addClass('active'); + + // Attach tabs + $content.append( + $('<div />').append( + $tabsNav, + $tabsContent + ) + ); + + if ($tr.data('stage') !== $tr.data('prevStage')) { + modalButtons.push({ + text: item.label_PrevStage.title, + active: true, + btnClass: 'btn-default', + name: 'prevstage', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + Backend.sendToStage($(e.target).closest('tr'), 'prev'); + } + }); + } + + modalButtons.push({ + text: item.label_NextStage.title, + active: true, + btnClass: 'btn-default', + name: 'nextstage', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + Backend.sendToStage($(e.target).closest('tr'), 'next'); + } + }); + modalButtons.push({ + text: TYPO3.lang['close'], + active: true, + btnClass: 'btn-info', + name: 'cancel', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + Modal.show( + TYPO3.lang['window.recordInformation'].replace('{0}', $.trim($tr.find('.t3js-title-live').text())), + $content, + Severity.info, + modalButtons + ); + }); + }; + + /** + * Opens a record in a preview window + * + * @param {Event} e + */ + Backend.openPreview = function(e) { + var $tr = $(e.target).closest('tr'); + + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload('viewSingleRecord', [ + $tr.data('table'), $tr.data('uid') + ]) + ).done(function(response) { + eval(response[0].result); + }); + }; + + /** + * Renders the record's history + * + * @param {Object} data + */ + Backend.generateHistoryView = function(data) { + var $history = $('<div />'); + + for (var i = 0; i < data.length; ++i) { + var $panel = $('<div />', {class: 'panel panel-default'}), + $diff; + + if (typeof data[i].differences === 'object') { + if (data[i].differences.length === 0) { + // Somehow here are no differences. What a pity, skip that record + continue; + } + $diff = $('<div />', {class: 'diff'}); + + for (var j = 0; j < data[i].differences.length; ++j) { + $diff.append( + $('<div />', {class: 'diff-item'}).append( + $('<div />', {class: 'diff-item-title'}).text(data[i].differences[j].label), + $('<div />', {class: 'diff-item-result diff-item-result-inline'}).html(data[i].differences[j].html) + ) + ); + } + + $panel.append( + $('<div />').append($diff) + ); + } else { + $panel.append( + $('<div />', {class: 'panel-body'}).text(data[i].differences) + ); + } + $panel.append( + $('<div />', {class: 'panel-footer'}).append( + $('<span />', {class: 'label label-info'}).text(data[i].datetime) + ) + ); + + $history.append( + $('<div />', {class: 'media'}).append( + $('<div />', {class: 'media-left text-center'}).text(data[i].user).prepend( + $('<div />').html(data[i].user_avatar) + ), + $('<div />', {class: 'media-body'}).append($panel) + ) + ); + } + + return $history; + }; + + /** + * Shows a confirmation modal and deletes the selected record from workspace. + * + * @param {Event} e + */ + Backend.confirmDeleteRecordFromWorkspace = function(e) { + var $tr = $(e.target).closest('tr'); + var $modal = Modal.confirm( + TYPO3.lang['window.discard.title'], + TYPO3.lang['window.discard.message'], + Severity.warning, + [ + { + text: TYPO3.lang['cancel'], + active: true, + btnClass: 'btn-default', + name: 'cancel', + trigger: function() { + $modal.modal('hide'); + } + }, { + text: TYPO3.lang['ok'], + btnClass: 'btn-warning', + name: 'ok' + } + ] + ); + $modal.on('button.clicked', function(e) { + if (e.target.name === 'ok') { + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectActionsPayload('deleteSingleRecord', [ + $tr.data('table'), + $tr.data('uid') + ]) + ]).done(function() { + $modal.modal('hide'); + Backend.getWorkspaceInfos(); + Backend.refreshPageTree(); + }); + } + }); + }; + + /** + * Runs a mass action + */ + Backend.runSelectionAction = function() { + var selectedAction = Backend.elements.$chooseSelectionAction.val(), + integrityCheckRequired = selectedAction !== 'discard'; + + if (selectedAction.length === 0) { + // Don't do anything if that value is empty + return; + } + + var affectedRecords = []; + for (var i = 0; i < Backend.markedRecordsForMassAction.length; ++i) { + var affected = Backend.markedRecordsForMassAction[i].split(':'); + affectedRecords.push({ + table: affected[0], + liveId: affected[2], + versionId: affected[1] + }); + } + + if (!integrityCheckRequired) { + Wizard.setup.forceSelection = false; + Backend.renderSelectionActionWizard(selectedAction, affectedRecords); + } else { + Workspaces.checkIntegrity( + { + selection: affectedRecords, + type: 'selection' + } + ).done(function(response) { + Wizard.setup.forceSelection = false; + if (response[0].result.result === 'warning') { + Backend.addIntegrityCheckWarningToWizard(); + } + Backend.renderSelectionActionWizard(selectedAction, affectedRecords); + }); + } + }; + + /** + * Adds a slide to the wizard concerning an integrity check warning. + */ + Backend.addIntegrityCheckWarningToWizard = function() { + Wizard.addSlide( + 'integrity-warning', + 'Warning', + TYPO3.lang['integrity.hasIssuesDescription'] + '<br>' + TYPO3.lang['integrity.hasIssuesQuestion'], + Severity.warning + ); + }; + + /** + * Renders the wizard for selection actions + * + * @param {String} selectedAction + * @param {Object} affectedRecords + */ + Backend.renderSelectionActionWizard = function(selectedAction, affectedRecords) { + Wizard.addSlide( + 'mass-action-confirmation', + TYPO3.lang['window.selectionAction.title'], + $('<p />').text(TYPO3.lang['tooltip.' + selectedAction + 'Selected']), + Severity.warning + ); + Wizard.addFinalProcessingSlide(function() { + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload('executeSelectionAction', { + action: selectedAction, + selection: affectedRecords + }) + ).done(function() { + Backend.getWorkspaceInfos(); + Wizard.dismiss(); + Backend.refreshPageTree(); + }); + }).done(function() { + Wizard.show(); + + Wizard.getComponent().on('wizard-dismissed', function() { + Backend.elements.$chooseSelectionAction.val(''); + }); + }); + }; + + /** + * Runs a mass action + */ + Backend.runMassAction = function() { + var selectedAction = Backend.elements.$chooseMassAction.val(), + integrityCheckRequired = selectedAction !== 'discard'; + + if (selectedAction.length === 0) { + // Don't do anything if that value is empty + return; + } + + if (!integrityCheckRequired) { + Wizard.setup.forceSelection = false; + Backend.renderMassActionWizard(selectedAction); + } else { + Workspaces.checkIntegrity( + { + language: Backend.settings.language, + type: selectedAction + } + ).done(function(response) { + Wizard.setup.forceSelection = false; + if (response[0].result.result === 'warning') { + Backend.addIntegrityCheckWarningToWizard(); + } + Backend.renderMassActionWizard(selectedAction); + }); + } + }; + + /** + * Renders the wizard for mass actions + * + * @param {String} selectedAction + */ + Backend.renderMassActionWizard = function(selectedAction) { + var massAction, + doSwap = false; + + switch (selectedAction) { + case 'publish': + massAction = 'publishWorkspace'; + break; + case 'swap': + massAction = 'publishWorkspace'; + doSwap = true; + break; + case 'discard': + massAction = 'flushWorkspace'; + break; + } + + if (massAction === null) { + throw 'Invalid mass action ' + selectedAction + ' called.'; + } + + Wizard.setup.forceSelection = false; + Wizard.addSlide( + 'mass-action-confirmation', + TYPO3.lang['window.massAction.title'], + $('<p />').html(TYPO3.lang['tooltip.' + selectedAction + 'All'] + '<br><br>' + TYPO3.lang['tooltip.affectWholeWorkspace']), + Severity.warning + ); + Wizard.addFinalProcessingSlide(function() { + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectMassActionsPayload(massAction, { + init: true, + total: 0, + processed: 0, + language: Backend.settings.language, + swap: doSwap + }) + ).done(function(response) { + var payload = response[0].result; + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectMassActionsPayload(massAction, payload) + ).done(function() { + Backend.getWorkspaceInfos(); + Wizard.dismiss(); + }); + }); + }).done(function() { + Wizard.show(); + + Wizard.getComponent().on('wizard-dismissed', function() { + Backend.elements.$chooseMassAction.val(''); + }); + }); + }; + + /** + * Sends marked records to a stage + * + * @param {Event} e + */ + Backend.sendToSpecificStageAction = function(e) { + var affectedRecords = [], + stage = $(e.currentTarget).val(); + for (var i = 0; i < Backend.markedRecordsForMassAction.length; ++i) { + var affected = Backend.markedRecordsForMassAction[i].split(':'); + affectedRecords.push({ + table: affected[0], + uid: affected[1], + t3ver_oid: affected[2] + }); + } + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload('sendToSpecificStageWindow', [ + stage, affectedRecords + ]) + ).done(function(response) { + var $modal = Workspaces.renderSendToStageWindow(response); + $modal.on('button.clicked', function(e) { + if (e.target.name === 'ok') { + var $form = $(e.currentTarget).find('form'), + serializedForm = $form.serializeObject(); + + serializedForm.affects = { + elements: affectedRecords, + nextStage: stage + }; + + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectActionsPayload('sendToSpecificStageExecute', [serializedForm]), + Workspaces.generateExtDirectPayload('getWorkspaceInfos', Backend.settings) + ]).done(function(response) { + $modal.modal('hide'); + Backend.renderWorkspaceInfos(response[1].result); + Backend.refreshPageTree(); + }); + } + }).on('modal-destroyed', function() { + Backend.elements.$chooseStageAction.val(''); + }); + }); + }; + + /** + * Reloads the page tree + */ + Backend.refreshPageTree = function() { + if (top.TYPO3 && top.TYPO3.Backend && top.TYPO3.Backend.NavigationContainer && top.TYPO3.Backend.NavigationContainer.PageTree) { + top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree(); + } + }; + + /** + * Renders the action button based on the user's permission. + * This method is intended to be dropped once we don't the ExtDirect stuff anymore. + * + * @returns {$} + * @private + */ + Backend.getAction = function(condition, action, iconIdentifier) { + if (condition) { + return $('<button />', {class: 'btn btn-default', 'data-action': action, 'data-toggle': 'tooltip'}).append(Backend.getPreRenderedIcon(iconIdentifier)) + } + return $('<span />', {class: 'btn btn-default disabled'}).append(Backend.getPreRenderedIcon('empty-empty')); + }; + + /** + * Fetches and renders available preview links + */ + Backend.generatePreviewLinks = function() { + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload('generateWorkspacePreviewLinksForAllLanguages', [ + Backend.settings.id + ]) + ).done(function(response) { + var result = response[0].result, + $list = $('<dl />'); + + $.each(result, function(language, url) { + $list.append( + $('<dt />').text(language), + $('<dd />').append( + $('<a />', {href: url, target: '_blank'}).text(url) + ) + ); + }); + + Modal.show( + TYPO3.lang['previewLink'], + $list, + Severity.info, + [{ + text: TYPO3.lang['ok'], + active: true, + btnClass: 'btn-info', + name: 'ok', + trigger: function() { + Modal.currentModal.trigger('modal-dismiss'); + } + }] + ); + }); + }; + + /** + * Gets the pre-rendered icon + * This method is intended to be dropped once we use Fluid's StandaloneView. + * + * @param {String} identifier + * @returns {$} + */ + Backend.getPreRenderedIcon = function(identifier) { + return Backend.elements.$actionIcons.find('[data-identifier="' + identifier + '"]').clone(); + }; + + /** + * Serialize a form to a JavaScript object + * + * @see http://stackoverflow.com/a/1186309/4828813 + * @return {Object} + */ + $.fn.serializeObject = function() { + var o = {}; + var a = this.serializeArray(); + $.each(a, function() { + if (typeof o[this.name] !== 'undefined') { + if (!o[this.name].push) { + o[this.name] = [o[this.name]]; + } + o[this.name].push(this.value || ''); + } else { + o[this.name] = this.value || ''; + } + }); + return o; + }; + + /** + * Changes the markup of a pagination action being disabled + */ + $.fn.disablePagingAction = function() { + $(this).addClass('disabled').find('.t3-icon').unwrap().wrap($('<span />')); + }; + + $(Backend.initialize); +}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowDetailTemplate.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowDetailTemplate.js deleted file mode 100644 index 0a33cc61500b..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowDetailTemplate.js +++ /dev/null @@ -1,10 +0,0 @@ -Ext.ns('TYPO3.Workspaces.Component'); - -TYPO3.Workspaces.Component.RowDetailTemplate = Ext.extend(Ext.XTemplate, { - exists: function(o, name) { - return typeof o != 'undefined' && o != null && o!=''; - }, - hasComments: function(comments){ - return comments.length>0; - } -}); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowExpander.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowExpander.js deleted file mode 100644 index 25d644ab0100..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/RowExpander.js +++ /dev/null @@ -1,318 +0,0 @@ -Ext.ns('TYPO3.Workspaces.Component'); - -TYPO3.Workspaces.Component.RowExpander = Ext.extend(Ext.grid.RowExpander, { - menuDisabled: true, - hideable: false, - - rowDetailTemplate: [ - '<div class="t3-workspaces-foldoutWrapper">', - '<tpl for=".">', - '<tpl>', - '<table class="char_select_template" width="100%">', - '<tr class="header">', - '<th class="char_select_profile_titleLeft">', - '{icon_Workspace} {[TYPO3.l10n.localize(\'workspace_version\')]}', - '</th>', - '<th class="char_select_profile_titleRight">', - '{icon_Live} {[TYPO3.l10n.localize(\'live_workspace\')]}', - '</th>', - '</tr>', - '<tr>', - '<td class="t3-workspaces-foldout-subheaderLeft">', - '{[String.format(TYPO3.l10n.localize(\'current_step\'), values.label_Stage, values.stage_position, values.stage_count)]}', - '</td>', - '<td class="t3-workspaces-foldout-subheaderRight">', - '{[String.format(TYPO3.l10n.localize(\'path\'), values.path_Live)]}', - '</td>', - '</tr>', - '<tr>', - '<td class="t3-workspaces-foldout-td-contentDiffLeft">', - '<div class="t3-workspaces-foldout-contentDiff-container">', - '<table class="t3-workspaces-foldout-contentDiff">', - '<tpl for="diff">', - '<tr><th>{label}</th><td class="content">', - '<tpl if="this.exists(content)">', - '{content}', - '</tpl>', - '</td></tr>', - '</tpl>', - '</table>', - '</div>', - '</td>', - '<td class="t3-workspaces-foldout-td-contentDiffRight">', - '<div class="t3-workspaces-foldout-contentDiff-container">', - '<table class="t3-workspaces-foldout-contentDiff">', - '<tpl for="live_record">', - '<tr><th>{label}</th><td class="content">', - '<tpl if="this.exists(content)">', - '{content}', - '</tpl>', - '</td></tr>', - '</tpl>', - '</table>', - '</div>', - '</td>', - '</tr>', - '<tpl if="this.hasComments(comments)">', - '<tr>', - '<td class="t3-workspaces-foldout-subheaderLeft">', - '<div class="t3-workspaces-foldout-subheader-container">', - '{[String.format(TYPO3.l10n.localize(\'comments\'), values.stage_position, values.label_Stage)]}', - '</div>', - '</td>', - '<td class="t3-workspaces-foldout-subheaderRight">', - ' ', - '</td>', - '</tr>', - '<tr>', - '<td class="char_select_profile_stats">', - '<div class="t3-workspaces-comments">', - '<tpl for="comments">', - '<div class="t3-workspaces-comments-singleComment">', - '<div class="t3-workspaces-comments-singleComment-author">', - '{user_username}', - '</div>', - '<div class="t3-workspaces-comments-singleComment-content-wrapper"><div class="t3-workspaces-comments-singleComment-content">', - '<span class="t3-workspaces-comments-singleComment-content-date">{tstamp}</span>', - '<div class="t3-workspaces-comments-singleComment-content-title">@ {[String.format(TYPO3.l10n.localize(\'stage\'), values.stage_title)]}</div>', - '<div class="t3-workspaces-comments-singleComment-content-text">{user_comment}</div>', - '</div></div>', - '</div>', - '</tpl>', - '</div>', - '</td>', - '<td class="char_select_profile_title">', - ' ', - '</td>', - '</tpl>', - '</tr>', - '</table>', - '</tpl>', - '</tpl>', - '</div>', - '<div class="x-clear"></div>' - ], - - detailStoreConfiguration: { - xtype : 'directstore', - storeId : 'rowDetailService', - root : 'data', - totalProperty : 'total', - idProperty : 'id', - fields : [ - {name : 'uid'}, - {name : 't3ver_oid'}, - {name : 'table'}, - {name : 'stage'}, - {name : 'diff'}, - {name : 'path_Live'}, - {name : 'label_Stage'}, - {name : 'stage_position'}, - {name : 'stage_count'}, - {name : 'live_record'}, - {name : 'comments'}, - {name : 'icon_Live'}, - {name : 'icon_Workspace'}, - {name : 'languageValue'}, - {name : 'integrity'} - ] - }, - - detailStore: null, - - init : function(grid) { - TYPO3.Workspaces.Component.RowExpander.superclass.init.call(this, grid); - this.detailStore = Ext.create(this.detailStoreConfiguration); - - this.addEvents({ - beforeExpandCollection: true, - beforeExpandCollectionChild: true, - beforeCollapseCollection: true, - beforeCollapseCollectionChild: true - }) - }, - - getRowClass : function(record, rowIndex, p, ds) { - var cls = []; - - cls.push(Ext.grid.RowExpander.prototype.getRowClass.call(this, record, rowIndex, p, ds)); - - if (record.json.Workspaces_CollectionChildren > 0) { - // @todo Extend by new nodeState check - cls.push('typo3-workspaces-collection-parent-collapsed'); - } - if (record.json.Workspaces_CollectionParent) { - // @todo Extend by new nodeState check - cls.push('typo3-workspaces-collection-child-collapsed'); - } - if (!record.json.allowedAction_nextStage && !record.json.allowedAction_prevStage && !record.json.allowedAction_swap) { - cls.push('typo3-workspaces-row-disabled'); - } - - return cls.join(' '); - }, - renderer : function(v, p, record) { - var html; - html = Ext.grid.RowExpander.prototype.renderer.call(this, v, p, record); - return html; - }, - remoteDataMethod : function (record, index) { - this.detailStore.baseParams = { - uid: record.json.uid, - table: record.json.table, - stage: record.json.stage, - t3ver_oid: record.json.t3ver_oid, - path_Live: record.json.path_Live, - label_Stage: record.json.label_Stage - }; - this.detailStore.load({ - callback: function(r, options, success) { - TYPO3.Workspaces.RowExpander.expandRow(index); - } - }); - new Ext.ux.TYPO3.Workspace.RowPanel({ - renderTo: 'remData' + index, - items: [{ - xtype: 'dataview', - store: this.detailStore, - tpl: new TYPO3.Workspaces.Component.RowDetailTemplate(this.rowDetailTemplate) - }] - }); - }, - onMouseDown : function(e, t) { - tObject = Ext.get(t); - if (tObject.hasClass('x-grid3-row-expander')) { - e.stopEvent(); - row = e.getTarget('.x-grid3-row'); - this.toggleRow(row); - } else if (tObject.hasClass('typo3-workspaces-collection-level-node')) { - e.stopEvent(); - row = e.getTarget('.x-grid3-row'); - this.toggleCollection(row); - } - }, - toggleRow : function(row) { - this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'beforeExpand' : 'collapseRow'](row); - }, - beforeExpand : function(row) { - if (typeof row == 'number') { - row = this.grid.view.getRow(row); - } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); - - if (this.fireEvent('beforexpand', this, record, body, row.rowIndex) !== false) { - this.tpl = new Ext.Template("<div id=\"remData" + row.rowIndex + "\" class=\"rem-data-expand\"><\div>"); - if (this.tpl && this.lazyRender) { - body.innerHTML = this.getBodyContent(record, row.rowIndex); - } - } - // toggle remoteData loading - this.remoteDataMethod(record, row.rowIndex); - return true; - }, - expandRow : function(row) { - if (typeof row == 'number') { - row = this.grid.view.getRow(row); - } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); - this.state[record.id] = true; - Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded'); - this.fireEvent('expand', this, record, body, row.rowIndex); - var i; - for(i = 0; i < this.grid.store.getCount(); i++) { - if(i != row.rowIndex) { - this.collapseRow(i); - } - } - }, - collapseRow : function(row) { - if (typeof row == 'number') { - row = this.grid.view.getRow(row); - } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); - if (this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false) { - this.state[record.id] = false; - Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed'); - this.fireEvent('collapse', this, record, body, row.rowIndex); - } - }, - - toggleCollection : function(row) { - if (Ext.fly(row).hasClass('typo3-workspaces-collection-parent-collapsed')) { - this.expandCollection(row); - } else { - this.collapseCollection(row); - } - }, - expandCollection : function(row) { - var record, body, child, i; - - if (typeof row === 'number') { - row = this.grid.view.getRow(row); - } - - record = this.grid.store.getAt(row.rowIndex); - body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); - if (this.fireEvent('beforeExpandCollection', this, record, body, row.rowIndex) !== false) { - for(i = 0; i < this.grid.store.getCount(); i++) { - child = this.grid.store.getAt(i); - if (child.json.Workspaces_CollectionParent === record.json.Workspaces_CollectionCurrent) { - this.expandCollectionChild(i); - } - } - Ext.fly(row).replaceClass('typo3-workspaces-collection-parent-collapsed', 'typo3-workspaces-collection-parent-expanded'); - } - }, - expandCollectionChild : function(row) { - var record, body; - - if (typeof row === 'number') { - row = this.grid.view.getRow(row); - } - - record = this.grid.store.getAt(row.rowIndex); - body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); - if (this.fireEvent('beforeCollapseCollectionChild', this, record, body, row.rowIndex) !== false) { - Ext.fly(row).replaceClass('typo3-workspaces-collection-child-collapsed', 'typo3-workspaces-collection-child-expanded'); - } - }, - collapseCollection : function(row) { - var record, body, child, i; - - if (typeof row === 'number') { - row = this.grid.view.getRow(row); - } - - record = this.grid.store.getAt(row.rowIndex); - body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); - if (this.fireEvent('beforeCollapseCollectionChild', this, record, body, row.rowIndex) !== false) { - for(i = 0; i < this.grid.store.getCount(); i++) { - child = this.grid.store.getAt(i); - if (child.json.Workspaces_CollectionParent === record.json.Workspaces_CollectionCurrent) { - // Delegate collapsing to child if it has children as well - if (child.json.Workspaces_CollectionChildren > 0) { - this.collapseCollection(i); - } - this.collapseCollectionChild(i); - } - } - Ext.fly(row).replaceClass('typo3-workspaces-collection-parent-expanded', 'typo3-workspaces-collection-parent-collapsed'); - } - }, - collapseCollectionChild : function(row) { - var record, body; - - if (typeof row === 'number') { - row = this.grid.view.getRow(row); - } - - record = this.grid.store.getAt(row.rowIndex); - body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); - if (this.fireEvent('beforeCollapseCollection', this, record, body, row.rowIndex) !== false) { - Ext.fly(row).replaceClass('typo3-workspaces-collection-child-expanded', 'typo3-workspaces-collection-child-collapsed'); - } - } -}); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/TabPanel.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/TabPanel.js deleted file mode 100644 index 635b47d1ed33..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/Component/TabPanel.js +++ /dev/null @@ -1,159 +0,0 @@ -Ext.ns('TYPO3.Workspaces.Component'); - -TYPO3.Workspaces.Component.TabPanel = Ext.extend(Ext.TabPanel, { - menuRight: null, - tabMenu: null, - - menuItems: [], - menuItemTemplate: null, - - listeners: { - beforetabchange: function(panel, newTab, currentTab) { - if (typeof currentTab !== 'undefined' && newTab.triggerUrl) { - this.handleTriggerUrl(newTab); - } - }, - afterrender: function() { - this.createMenu(); - this.arrangeTabsAfterRender(); - this.updateMenu(); - } - }, - - initComponent: function() { - TYPO3.Workspaces.Component.TabPanel.superclass.initComponent.call(this); - Ext.EventManager.onWindowResize(this.handleResize, this); - - this.menuItemTemplate = new Ext.XTemplate( - '<a id="{id}" class="{cls} x-unselectable" hidefocus="true" unselectable="on" href="{href}"', - '<tpl if="hrefTarget">', - ' target="{hrefTarget}"', - '</tpl>', - '>', - '<span class="x-menu-item-text">{text}</span>', - '</a>' - ); - }, - - getParentPanel: function() { - return this.findParentByType('panel'); - }, - - createMenu : function() { - var position = this.tabPosition=='bottom' ? this.footer : this.header; - var h = this.stripWrap.dom.offsetHeight; - var menuRight = position.insertFirst({ - cls:'x-tab-menu-right' - }); - menuRight.hide(); - menuRight.setHeight(h); - menuRight.addClassOnOver('x-tab-menu-right-over'); - menuRight.on('click', this.showMenu, this); - this.menuRight = menuRight; - }, - - updateMenu: function() { - if (this.menuItems.length) { - this.menuRight.show(); - } else { - this.menuRight.hide(); - } - }, - - showMenu: function(event) { - if (this.tabMenu) { - this.tabMenu.destroy(); - this.un('destroy', this.tabMenu.destroy, this.tabMenu); - this.tabMenu = null; - } - - this.tabMenu = new Ext.menu.Menu({ - cls: 'typo3-workspaces-menu' - }); - this.on('destroy', this.tabMenu.destroy, this.tabMenu); - - this.addMenuItems(); - - var target = Ext.get(event.getTarget()); - var xy = target.getXY(); - xy[1] += this.menuRight.getHeight() - 1; - - this.tabMenu.showAt(xy); - }, - - addMenuItems: function() { - Ext.each(this.menuItems, function(cmp) { - menuItem = new Ext.menu.Item({ - itemTpl: this.menuItemTemplate, - text : cmp.title, - handler : this.handleTriggerUrl, - scope : this, - triggerUrl: cmp.triggerUrl - //iconCls : item.iconCls - }); - this.tabMenu.add(menuItem); - }, this); - }, - - handleTriggerUrl: function(item) { - location.href = item.triggerUrl; - }, - - handleResize: function(width, height) { - this.setWidth(width); - this.arrangeTabsAfterResize(); - this.updateMenu(); - }, - - arrangeTabsAfterRender: function() { - var i, cmp, moveItems = [], width = 0; - var lastIndex = this.items.items.length; - var tabPanelWidth = this.getParentPanel().getWidth(); - - for (i = 0; i < lastIndex; i++) { - cmp = this.getComponent(i); - width += Ext.get(cmp.tabEl).getWidth() + this.tabMargin; - if (width > tabPanelWidth - this.menuRight.getWidth()) { - moveItems.push(cmp); - } - } - - Ext.each(moveItems, function(cmp) { - this.remove(cmp); - this.menuItems.push(cmp); - }, this); - }, - - arrangeTabsAfterResize: function() { - var i, cmp, moveItems = [], width = 0; - var lastIndex = this.items.items.length; - var tabPanelWidth = this.getParentPanel().getWidth(); - - for (i = 0; i < lastIndex; i++) { - cmp = this.getComponent(i); - width += Ext.get(cmp.tabEl).getWidth() + this.tabMargin; - if (width > tabPanelWidth - this.menuRight.getWidth()) { - moveItems.unshift(cmp); - } - } - - if (moveItems.length) { - Ext.each(moveItems, function(cmp) { - this.remove(cmp); - this.menuItems.unshift(cmp); - }, this); - } else { - while (this.menuItems.length) { - cmp = this.menuItems[0]; - this.add(cmp); - width += Ext.get(cmp.tabEl).getWidth() + this.tabMargin; - if (width > tabPanelWidth - this.menuRight.getWidth()) { - this.remove(cmp); - break; - } - this.menuItems.shift(); - } - } - } -}); -Ext.reg('WorkspacesTabPanel', TYPO3.Workspaces.Component.TabPanel); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Ext.ux.plugins.TabStripContainer.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Ext.ux.plugins.TabStripContainer.js deleted file mode 100644 index 67a3fe16268f..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/Ext.ux.plugins.TabStripContainer.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Ext.ux.plugins.TabStripContainer - * @date December 19, 2010 - * - * @class Ext.ux.plugins.TabStripContainer - * @extends Object - */ - -Ext.ns('Ext.ux.plugins'); - -Ext.ux.plugins.TabStripContainer = Ext.extend(Object, { - - /** - * @hide private - * - * Tab panel we are plugged in. - */ - tabPanel : null, - - /** - * @hide private - * - * items for the panel - */ - items: [], - - /** - * @hide private - * - * Cached tab panel's strip wrap element container, i.e. panel's header or footer element. - */ - headerFooterEl : null, - - - /** - * @constructor - */ - constructor : function(config) { - Ext.apply(this, config); - }, - - /** - * Initializes plugin - */ - init : function(tabPanel) { - this.tabPanel = tabPanel; - tabPanel.on( - 'afterrender', - this.onTabPanelAfterRender, - this, - { - delay: 10 - } - ); - }, - - /** - * Adds the panel to the tab header/footer - * - * @param tabPanel - */ - onTabPanelAfterRender: function(tabPanel) { - var height, panelDiv, stripTarget, config; - // Getting and caching strip wrap element parent, i.e. tab panel footer or header. - this.headerFooterEl = - this.tabPanel.tabPosition == 'bottom' - ? this.tabPanel.footer - : this.tabPanel.header; - height = this.headerFooterEl.getComputedHeight(); - stripTarget = tabPanel[tabPanel.stripTarget]; - stripTarget.applyStyles('position: relative;'); - - panelDiv = this.headerFooterEl.createChild({ - tag : 'div', - id: this.id || Ext.id(), - style : { - position : 'absolute', - right: 0, - top: '1px' - } - }); - panelDiv.setSize(this.width, height, false); - config = Ext.applyIf({ - layout: 'hbox', - height: height, - width: this.width, - renderTo: panelDiv - }, this.panelConfig); - this.panelContainer = new Ext.Panel(config); - this.panelContainer.add(this.items); - this.panelContainer.doLayout(); - }, - - doLayout: function () { - this.panelContainer.doLayout(); - } - -}); -Ext.preg('Ext.ux.plugins.TabStripContainer', Ext.ux.plugins.TabStripContainer); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Preview.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Preview.js new file mode 100644 index 000000000000..1c1113430bba --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/Preview.js @@ -0,0 +1,305 @@ +/* +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * RequireJS module for workspace preview + */ +define([ + 'jquery', + 'TYPO3/CMS/Workspaces/Workspaces', + 'TYPO3/CMS/Backend/Severity', + 'TYPO3/CMS/Backend/Modal', + 'twbs/bootstrap-slider' +], function($, Workspaces, Severity, Modal) { + 'use strict'; + + var Preview = { + identifiers: { + topbar: '#typo3-topbar', + workspacePanel: '.workspace-panel', + liveView: '#live-view', + workspaceTabs: '.t3js-workspace-tabs [data-toggle="tab"]', + workspaceActions: '.t3js-workspace-actions', + stageSlider: '#workspace-stage-slider', + workspaceView: '#workspace-view', + workspaceList: '#workspace-list', + sendToStageAction: '[data-action="send-to-stage"]', + discardAction: '[data-action="discard"]', + stageButtonsContainer: '.t3js-stage-buttons', + previewModeContainer: '.t3js-preview-mode', + activePreviewMode: '.t3js-active-preview-mode', + workspacePreview: '.t3js-workspace-preview' + }, + currentSlidePosition: 100, + elements: {} // filled in Preview.getElements() + }; + + /** + * Initializes the preview module + */ + Preview.initialize = function() { + Preview.getElements(); + Preview.resizeViews(); + + Preview.adjustPreviewModeSelectorWidth(); + Preview.elements.$stageSlider.slider(); + + Preview.registerEvents(); + }; + + /** + * Fetches and stores often required elements + */ + Preview.getElements = function() { + Preview.elements.$liveView = $(Preview.identifiers.liveView); + Preview.elements.$workspacePanel = $(Preview.identifiers.workspacePanel); + Preview.elements.$workspaceTabs = $(Preview.identifiers.workspaceTabs); + Preview.elements.$workspaceActions = $(Preview.identifiers.workspaceActions); + Preview.elements.$stageSlider = $(Preview.identifiers.stageSlider); + Preview.elements.$workspaceView = $(Preview.identifiers.workspaceView); + Preview.elements.$workspaceList = $(Preview.identifiers.workspaceList); + Preview.elements.$stageButtonsContainer = $(Preview.identifiers.stageButtonsContainer); + Preview.elements.$previewModeContainer = $(Preview.identifiers.previewModeContainer); + Preview.elements.$activePreviewMode = $(Preview.identifiers.activePreviewMode); + Preview.elements.$workspacePreview = $(Preview.identifiers.workspacePreview); + }; + + /** + * Registers the events + */ + Preview.registerEvents = function() { + $(window).on('resize', function() { + Preview.resizeViews(); + }); + $(document) + .on('click', Preview.identifiers.discardAction, Preview.renderDiscardWindow) + .on('click', Preview.identifiers.sendToStageAction, Preview.renderSendPageToStageWindow) + ; + + Preview.elements.$workspaceTabs.on('show.bs.tab', function() { + Preview.elements.$workspaceActions.toggle($(this).data('actions')); + }); + Preview.elements.$stageSlider.on('change', Preview.updateSlidePosition); + Preview.elements.$previewModeContainer.find('[data-preview-mode]').on('click', Preview.changePreviewMode); + }; + + /** + * Renders the staging buttons + * + * @param {String} buttons + */ + Preview.renderStageButtons = function(buttons) { + Preview.elements.$stageButtonsContainer.html(buttons); + }; + + /** + * Calculate the available space based on the viewport height + * + * @returns {Number} + */ + Preview.getAvailableSpace = function() { + var $viewportHeight = $(window).height(), + $topbarHeight = $(Preview.identifiers.topbar).outerHeight(); + + return $viewportHeight - $topbarHeight; + }; + + /** + * Updates the position of the comparison slider + * + * @param {Event} e + */ + Preview.updateSlidePosition = function(e) { + Preview.currentSlidePosition = e.value.newValue; + Preview.resizeViews(); + }; + + /** + * Resize the views based on the current viewport height and slider position + */ + Preview.resizeViews = function() { + var availableSpace = Preview.getAvailableSpace(), + relativeHeightOfLiveView = (Preview.currentSlidePosition - 100) * -1, + absoluteHeightOfLiveView = Math.round(Math.abs(availableSpace * relativeHeightOfLiveView / 100)), + outerHeightDifference = Preview.elements.$liveView.outerHeight() - Preview.elements.$liveView.height(); + + Preview.elements.$workspacePreview.height(availableSpace); + + if (Preview.elements.$activePreviewMode.data('activePreviewMode') === 'slider') { + Preview.elements.$liveView.height(absoluteHeightOfLiveView - outerHeightDifference); + } + Preview.elements.$workspaceList.height(availableSpace); + }; + + /** + * Renders the discard window + * + * @private + */ + Preview.renderDiscardWindow = function() { + var $modal = Modal.confirm( + TYPO3.lang['window.discardAll.title'], + TYPO3.lang['window.discardAll.message'], + Severity.warning, + [ + { + text: TYPO3.lang['cancel'], + active: true, + btnClass: 'btn-default', + name: 'cancel', + trigger: function() { + $modal.modal('hide'); + } + }, { + text: TYPO3.lang['ok'], + btnClass: 'btn-warning', + name: 'ok' + } + ] + ); + $modal.on('button.clicked', function(e) { + if (e.target.name === 'ok') { + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectActionsPayload('discardStagesFromPage', [TYPO3.settings.Workspaces.id]), + Workspaces.generateExtDirectActionsPayload('updateStageChangeButtons', [TYPO3.settings.Workspaces.id]) + ]).done(function(response) { + $modal.modal('hide'); + Preview.renderStageButtons(response[1].result); + // Reloading live view and and workspace list view IFRAME + Preview.elements.$workspaceView.attr('src', Preview.elements.$workspaceView.attr('src')); + Preview.elements.$workspaceList.attr('src', Preview.elements.$workspaceList.attr('src')); + }); + } + }); + }; + + /** + * Adjusts the width of the preview mode selector to avoid jumping around due to different widths of the labels + */ + Preview.adjustPreviewModeSelectorWidth = function() { + var $btnGroup = Preview.elements.$previewModeContainer.find('.btn-group'), + maximumWidth = 0; + + $btnGroup.addClass('open'); + Preview.elements.$previewModeContainer.find('li > a > span').each(function(_, el) { + var width = $(el).width(); + if (maximumWidth < width) { + maximumWidth = width; + } + }); + $btnGroup.removeClass('open'); + Preview.elements.$activePreviewMode.width(maximumWidth); + }; + + /** + * Renders the "send page to stage" window + * + * @private + */ + Preview.renderSendPageToStageWindow = function() { + var $me = $(this), + direction = $me.data('direction'), + actionName; + + if (direction === 'prev') { + actionName = 'sendPageToPreviousStage'; + } else if (direction === 'next') { + actionName = 'sendPageToNextStage'; + } else { + throw 'Invalid direction ' + direction + ' requested.'; + } + + Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectActionsPayload(actionName, [TYPO3.settings.Workspaces.id]) + ).done(function(response) { + var $modal = Workspaces.renderSendToStageWindow(response); + $modal.on('button.clicked', function (e) { + if (e.target.name === 'ok') { + var $form = $(e.currentTarget).find('form'), + serializedForm = $form.serializeObject(); + + serializedForm.affects = response[0].result.affects; + serializedForm.stageId = $me.data('stageId'); + + Workspaces.sendExtDirectRequest([ + Workspaces.generateExtDirectActionsPayload('sentCollectionToStage', [serializedForm]), + Workspaces.generateExtDirectActionsPayload('updateStageChangeButtons', [TYPO3.settings.Workspaces.id]) + ]).done(function(response) { + $modal.modal('hide'); + + Preview.renderStageButtons(response[1].result); + }); + } + }); + }); + }; + + /** + * Changes the preview mode + * + * @param {Event} e + */ + Preview.changePreviewMode = function(e) { + e.preventDefault(); + + var $trigger = $(this), + currentPreviewMode = Preview.elements.$activePreviewMode.data('activePreviewMode'), + newPreviewMode = $trigger.data('previewMode'); + + Preview.elements.$activePreviewMode.text($trigger.text()).data('activePreviewMode', newPreviewMode); + Preview.elements.$workspacePreview.parent() + .removeClass('preview-mode-' + currentPreviewMode) + .addClass('preview-mode-' + newPreviewMode); + + if (newPreviewMode === 'slider') { + Preview.elements.$stageSlider.parent().toggle(true); + Preview.resizeViews(); + } else { + Preview.elements.$stageSlider.parent().toggle(false); + + if (newPreviewMode === 'vbox') { + Preview.elements.$liveView.height('100%'); + } else { + Preview.elements.$liveView.height('50%'); + } + } + + }; + + /** + * Serialize a form to a JavaScript object + * + * @see http://stackoverflow.com/a/1186309/4828813 + * @return {Object} + */ + $.fn.serializeObject = function() { + var o = {}; + var a = this.serializeArray(); + $.each(a, function() { + if (typeof o[this.name] !== 'undefined') { + if (!o[this.name].push) { + o[this.name] = [o[this.name]]; + } + o[this.name].push(this.value || ''); + } else { + o[this.name] = this.value || ''; + } + }); + return o; + }; + + $(document).ready(function() { + Preview.initialize(); + }); +}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Store/mainstore.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Store/mainstore.js deleted file mode 100644 index b66d1200b521..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/Store/mainstore.js +++ /dev/null @@ -1,85 +0,0 @@ -Ext.ns('TYPO3.Workspaces.Configuration'); - -TYPO3.Workspaces.Configuration.StoreFieldArray = [ - {name : 'Workspaces_Collection', type : 'int'}, - {name : 'Workspaces_CollectionLevel', type : 'int'}, - {name : 'Workspaces_CollectionParent'}, - {name : 'Workspaces_CollectionCurrent'}, - {name : 'Workspaces_CollectionChildren', type : 'int'}, - {name : 'table'}, - {name : 'uid', type : 'int'}, - {name : 't3ver_oid', type : 'int'}, - {name : 't3ver_wsid', type : 'int'}, - {name : 'livepid', type : 'int'}, - {name : 'stage', type: 'int'}, - {name : 'change',type : 'int'}, - {name : 'languageValue'}, - {name : 'language'}, - {name : 'integrity'}, - {name : 'label_Live'}, - {name : 'label_Workspace'}, - {name : 'label_Stage'}, - {name : 'label_nextStage'}, - {name : 'label_prevStage'}, - {name : 'workspace_Title'}, - {name : 'actions'}, - {name : 'icon_Workspace'}, - {name : 'icon_Live'}, - {name : 'path_Live'}, - {name : 'path_Workspace'}, - {name : 'state_Workspace'}, - {name : 'workspace_Tstamp'}, - {name : 'workspace_Formated_Tstamp'}, - {name : 'allowedAction_nextStage'}, - {name : 'allowedAction_prevStage'}, - {name : 'allowedAction_swap'}, - {name : 'allowedAction_delete'}, - {name : 'allowedAction_edit'}, - {name : 'allowedAction_editVersionedPage'}, - {name : 'allowedAction_view'} -].concat(TYPO3.settings.Workspaces.extension.AdditionalColumn.Definition); - -TYPO3.Workspaces.MainStore = new Ext.data.GroupingStore({ - storeId : 'workspacesMainStore', - reader : new Ext.data.JsonReader({ - idProperty : 'id', - root : 'data', - totalProperty : 'total' - }, TYPO3.Workspaces.Configuration.StoreFieldArray), - groupField: 'path_Workspace', - paramsAsHash : true, - sortInfo : { - field : 'label_Live', - direction : "ASC" - }, - remoteSort : true, - baseParams: { - depth : 990, - id: TYPO3.settings.Workspaces.id, - language: TYPO3.settings.Workspaces.language, - query: '', - start: 0, - limit: 30 - }, - - showAction : false, - listeners : { - beforeload : function() {}, - load : function(store, records) { - var defaultColumn = TYPO3.Workspaces.WorkspaceGrid.colModel.getColumnById('label_Workspace'); - if (defaultColumn) { - defaultColumn.width = defaultColumn.defaultWidth + this.getMaximumCollectionLevel() * defaultColumn.levelWidth; - } - }, - datachanged : function(store) {} - }, - getMaximumCollectionLevel: function() { - var maximumCollectionLevel = 0; - Ext.each(this.data.items, function(item) { - if (item.json.Workspaces_CollectionLevel > maximumCollectionLevel) { - maximumCollectionLevel = item.json.Workspaces_CollectionLevel; - } - }); - return maximumCollectionLevel; - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Workspaces.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Workspaces.js new file mode 100644 index 000000000000..60e20ef72782 --- /dev/null +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/Workspaces.js @@ -0,0 +1,214 @@ +/* +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * RequireJS module for Workspaces + */ +define([ + 'jquery', + 'TYPO3/CMS/Backend/Severity', + 'TYPO3/CMS/Backend/Modal' +], function($, Severity, Modal) { + 'use strict'; + + var Workspaces = {}; + + /** + * Renders the send to stage window + * @param {Object} response + * @return {$} + */ + Workspaces.renderSendToStageWindow = function(response) { + var result = response[0].result, + $form = $('<form />'); + + if (typeof result.sendMailTo !== 'undefined' && result.sendMailTo.length > 0) { + $form.append( + $('<label />', {class: 'control-label'}).text(TYPO3.lang['window.sendToNextStageWindow.itemsWillBeSentTo']) + ); + + for (var i = 0; i < result.sendMailTo.length; ++i) { + var recipient = result.sendMailTo[i]; + + $form.append( + $('<div />', {class: 'checkbox'}).append( + $('<label />').text(recipient.label).prepend( + $('<input />', {type: 'checkbox', name: 'recipients', id: recipient.name, value: recipient.value}).prop('checked', recipient.checked).prop('disabled', recipient.disabled) + ) + ) + ); + } + } + + if (typeof result.additional !== 'undefined') { + $form.append( + $('<div />', {class: 'form-group'}).append( + $('<label />', {class: 'control-label', 'for': 'additional'}).text(TYPO3.lang['window.sendToNextStageWindow.additionalRecipients']), + $('<textarea />', {class: 'form-control', name: 'additional', id: 'additional'}).text(result.additional.value), + $('<span />', {class: 'help-block'}).text(TYPO3.lang['window.sendToNextStageWindow.additionalRecipients.hint']) + ) + ); + } + + $form.append( + $('<div />', {class: 'form-group'}).append( + $('<label />', {class: 'control-label', 'for': 'comments'}).text(TYPO3.lang['window.sendToNextStageWindow.comments']), + $('<textarea />', {class: 'form-control', name: 'comments', id: 'comments'}).text(result.comments.value) + ) + ); + + var $modal = Modal.show( + TYPO3.lang['actionSendToStage'], + $form, + Severity.info, + [ + { + text: TYPO3.lang['cancel'], + active: true, + btnClass: 'btn-default', + name: 'cancel', + trigger: function() { + $modal.modal('hide'); + } + }, { + text: TYPO3.lang['ok'], + btnClass: 'btn-info', + name: 'ok' + } + ] + ); + + return $modal; + }; + + /** + * Checks the integrity of a record + * + * @param {Array} payload + * @return {$} + */ + Workspaces.checkIntegrity = function(payload) { + return Workspaces.sendExtDirectRequest( + Workspaces.generateExtDirectPayload('checkIntegrity', payload) + ); + }; + + /** + * Sends an AJAX request compatible to ExtDirect + * This method is intended to be dropped once we don't the ExtDirect stuff anymore. + * + * @param {Object} payload + * @return {$} + */ + Workspaces.sendExtDirectRequest = function(payload) { + return $.ajax({ + url: TYPO3.settings.ajaxUrls['ext_direct_route'] + '&namespace=TYPO3.Workspaces', + method: 'POST', + contentType: 'application/json; charset=utf-8', + dataType: 'json', + data: JSON.stringify(payload) + }); + }; + + /** + * Generates the payload for ExtDirect + * + * @param {String} method + * @param {Object} data + * @return {{action, data, method, type}} + */ + Workspaces.generateExtDirectPayload = function(method, data) { + if (typeof data === 'undefined') { + data = {}; + } + return Workspaces.generateExtDirectPayloadBody('ExtDirect', method, data); + }; + + /** + * Generates the payload for ExtDirectMassActions + * + * @param {String} method + * @param {Object} data + * @return {{action, data, method, type}} + */ + Workspaces.generateExtDirectMassActionsPayload = function(method, data) { + if (typeof data === 'undefined') { + data = {}; + } + return Workspaces.generateExtDirectPayloadBody('ExtDirectMassActions', method, data); + }; + + /** + * Generates the payload for ExtDirectActions + * + * @param {String} method + * @param {Object} data + * @return {{action, data, method, type}} + */ + Workspaces.generateExtDirectActionsPayload = function(method, data) { + if (typeof data === 'undefined') { + data = []; + } + return Workspaces.generateExtDirectPayloadBody('ExtDirectActions', method, data); + }; + + /** + * Generates the payload body + * + * @param {String} action + * @param {String} method + * @param {Object} data + * @return {{action: String, data: Object, method: String, type: string}} + */ + Workspaces.generateExtDirectPayloadBody = function(action, method, data) { + if (data instanceof Array) { + data.push(TYPO3.settings.Workspaces.token); + } else { + data = [ + data, + TYPO3.settings.Workspaces.token + ]; + } + return { + action: action, + data: data, + method: method, + type: 'rpc' + }; + }; + + /** + * Serialize a form to a JavaScript object + * + * @see http://stackoverflow.com/a/1186309/4828813 + * @return {Object} + */ + $.fn.serializeObject = function() { + var o = {}; + var a = this.serializeArray(); + $.each(a, function() { + if (typeof o[this.name] !== 'undefined') { + if (!o[this.name].push) { + o[this.name] = [o[this.name]]; + } + o[this.name].push(this.value || ''); + } else { + o[this.name] = this.value || ''; + } + }); + return o; + }; + + return Workspaces; +}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js deleted file mode 100644 index 13e2ecdbd935..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/actions.js +++ /dev/null @@ -1,403 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - - -Ext.ns('TYPO3.Workspaces'); - -TYPO3.Workspaces.Actions = { - - runningMassAction: null, - currentSendToMode: 'next', - - checkIntegrity: function(parameters, callbackFunction, callbackArguments) { - TYPO3.Workspaces.ExtDirect.checkIntegrity( - parameters, - function (response) { - switch (response.result) { - case 'error': - top.TYPO3.Dialog.ErrorDialog({ - minWidth: 400, - title: 'Error', - msg: '<div class="scope">' + TYPO3.l10n.localize('integrity.hasIssuesDescription') + '</div>' - }); - break; - case 'warning': - top.TYPO3.Dialog.QuestionDialog({ - minWidth: 400, - title: 'Warning', - msg: '<div class="scope">' + TYPO3.l10n.localize('integrity.hasIssuesDescription') + '</div>' + - '<div class="question">' + TYPO3.l10n.localize('integrity.hasIssuesQuestion') + '</div>', - fn: function(result) { - if (result == 'yes') { - callbackFunction.call(this, callbackArguments) - } - } - }); - break; - default: - callbackFunction.call(this, callbackArguments); - } - } - ) - }, - - triggerMassAction: function(action, language) { - switch (action) { - case 'publish': - case 'swap': - this.runningMassAction = TYPO3.Workspaces.ExtDirectMassActions.publishWorkspace; - break; - case 'discard': - this.runningMassAction = TYPO3.Workspaces.ExtDirectMassActions.flushWorkspace; - break; - } - - // Publishing large amount of changes may require a longer timeout - Ext.Ajax.timeout = 3600000; - - this.runMassAction({ - init: true, - total:0, - processed:0, - language: language, - swap: (action == 'swap') - }); - }, - - runMassAction: function(parameters) { - if (parameters.init) { - top.Ext.getCmp('executeMassActionForm').hide(); - top.Ext.getCmp('executeMassActionProgressBar').show(); - top.Ext.getCmp('executeMassActionOkButton').disable(); - } - - var progress = parameters.total > 0 ? parameters.processed / parameters.total : 0; - var label = parameters.total > 0 ? parameters.processed + '/' + parameters.total : TYPO3.l10n.localize('runMassAction.init'); - top.Ext.getCmp('executeMassActionProgressBar').updateProgress(progress, label, true); - - this.runningMassAction(parameters, TYPO3.Workspaces.Actions.runMassActionCallback); - }, - - runMassActionCallback: function(response) { - if (response.error) { - top.Ext.getCmp('executeMassActionProgressBar').hide(); - top.Ext.getCmp('executeMassActionOkButton').hide(); - top.Ext.getCmp('executeMassActionCancelButton').setText(TYPO3.l10n.localize('close')); - top.Ext.getCmp('executeMassActionForm').show(); - top.Ext.getCmp('executeMassActionForm').update(response.error); - TYPO3.Workspaces.Helpers.refreshPageTree(); - } else { - if (response.total > response.processed) { - TYPO3.Workspaces.Actions.runMassAction(response); - } else { - top.Ext.getCmp('executeMassActionProgressBar').hide(); - top.Ext.getCmp('executeMassActionOkButton').hide(); - top.Ext.getCmp('executeMassActionCancelButton').setText(TYPO3.l10n.localize('close')); - top.Ext.getCmp('executeMassActionForm').show(); - top.Ext.getCmp('executeMassActionForm').update(TYPO3.l10n.localize('runMassAction.done').replace('%d', response.total)); - TYPO3.Workspaces.Helpers.refreshPageTree(); - } - } - }, - generateWorkspacePreviewLink: function() { - TYPO3.Workspaces.ExtDirectActions.generateWorkspacePreviewLink(TYPO3.settings.Workspaces.id, function(response) { - top.TYPO3.Dialog.InformationDialog({ - title: TYPO3.l10n.localize('previewLink'), - msg: String.format('<a href="{0}" target="_blank">{0}</a>', response) - }); - }); - }, - swapSingleRecord: function(table, t3ver_oid, orig_uid) { - TYPO3.Workspaces.ExtDirectActions.swapSingleRecord(table, t3ver_oid, orig_uid, function(response) { - TYPO3.Workspaces.MainStore.load(); - }); - }, - deleteSingleRecord: function(table, uid) { - TYPO3.Workspaces.ExtDirectActions.deleteSingleRecord(table, uid, function(response) { - TYPO3.Workspaces.Helpers.refreshPageTree(); - TYPO3.Workspaces.MainStore.load(); - }); - }, - viewSingleRecord: function(table, uid) { - TYPO3.Workspaces.ExtDirectActions.viewSingleRecord(table, uid, function(response) { - eval(response); - }); - }, - sendToStageWindow: function(response, selection) { - if (Ext.isObject(response.error)) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - } else { - var dialog = TYPO3.Workspaces.Helpers.getSendToStageWindow({ - title: response.title, - items: response.items, - executeHandler: function(event) { - var values = top.Ext.getCmp('sendToStageForm').getForm().getValues(); - affects = response.affects; - affects.elements = TYPO3.Workspaces.Helpers.getElementsArrayOfSelection(selection); - var parameters = { - affects: affects, - receipients: TYPO3.Workspaces.Helpers.getElementIdsFromFormValues(values, 'receipients'), - additional: values.additional, - comments: values.comments - }; - - TYPO3.Workspaces.Actions.sendToStageExecute(parameters); - top.TYPO3.Windows.close('sendToStageWindow'); - TYPO3.Workspaces.MainStore.reload(); - TYPO3.Workspaces.Helpers.refreshPageTree(); - } - }); - } - }, - sendToNextStageWindow: function(table, uid, t3ver_oid) { - TYPO3.Workspaces.ExtDirectActions.sendToNextStageWindow(uid, table, t3ver_oid, function(response) { - TYPO3.Workspaces.Actions.currentSendToMode = 'next'; - TYPO3.Workspaces.Actions.sendToStageWindow(response); - }); - }, - sendToPrevStageWindow: function(table, uid) { - TYPO3.Workspaces.ExtDirectActions.sendToPrevStageWindow(uid, table, function(response) { - TYPO3.Workspaces.Actions.currentSendToMode = 'prev'; - TYPO3.Workspaces.Actions.sendToStageWindow(response); - }); - }, - sendToSpecificStageWindow: function(selection, nextStage) { - var elements = []; - - Ext.each(selection, function(row) { - elements.push({table: row.json.table, uid: row.json.uid}) - }); - - TYPO3.Workspaces.ExtDirectActions.sendToSpecificStageWindow(nextStage, elements, function(response) { - TYPO3.Workspaces.Actions.currentSendToMode = 'specific'; - TYPO3.Workspaces.Actions.sendToStageWindow(response, selection); - }); - }, - sendToStageExecute: function (parameters) { - switch (TYPO3.Workspaces.Actions.currentSendToMode) { - case 'next': - TYPO3.Workspaces.ExtDirectActions.sendToNextStageExecute(parameters, TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction); - break; - case 'prev': - TYPO3.Workspaces.ExtDirectActions.sendToPrevStageExecute(parameters, TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction); - break; - case 'specific': - TYPO3.Workspaces.ExtDirectActions.sendToSpecificStageExecute(parameters, TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction); - break; - } - - }, - updateColModel: function(colModel) { - var dataArray = []; - for (var i = 0; i < colModel.config.length; i++) { - if (colModel.config[i].dataIndex !== '') { - dataArray.push({ - 'position': i, - 'column': colModel.config[i].dataIndex, - 'hidden': colModel.config[i].hidden ? 1 : 0 - }); - } - } - TYPO3.Workspaces.ExtDirectActions.saveColumnModel(dataArray); - }, - loadColModel: function(grid) { - TYPO3.Workspaces.ExtDirectActions.loadColumnModel(function(response) { - var colModel = grid.getColumnModel(); - for (var field in response) { - var colIndex = colModel.getIndexById(field); - if (colIndex != -1) { - colModel.setHidden(colModel.getIndexById(field), (response[field].hidden == 1 ? true : false)); - colModel.moveColumn(colModel.getIndexById(field), response[field].position); - } - } - }); - }, - handlerResponseOnExecuteAction: function(response) { - if (!Ext.isObject(response)) { - response = { error: { message: TYPO3.l10n.localize('error.noResponse') }}; - } - - if (Ext.isObject(response.error)) { - var error = response.error; - var code = (error.code ? ' #' + error.code : ''); - top.TYPO3.Dialog.ErrorDialog({ title: 'Error' + code, msg: error.message }); - } - }, - - /** - * Process "send to next stage" action. - * - * This method is used in the split frontend preview part. - * - * @return void - */ - sendPageToNextStage: function () { - TYPO3.Workspaces.ExtDirectActions.sendPageToNextStage(TYPO3.settings.Workspaces.id, function (response) { - if (Ext.isObject(response.error)) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - } else { - var dialog = TYPO3.Workspaces.Helpers.getSendToStageWindow({ - title: TYPO3.l10n.localize('nextStage'), - items: response.items.items, - executeHandler: function(event) { - var values = top.Ext.getCmp('sendToStageForm').getForm().getValues(); - affects = response.affects; - var parameters = { - affects: affects, - receipients: TYPO3.Workspaces.Helpers.getElementIdsFromFormValues(values, 'receipients'), - additional: values.additional, - comments: values.comments, - stageId: response.stageId - }; - TYPO3.Workspaces.ExtDirectActions.sentCollectionToStage(parameters, function (response) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - TYPO3.Workspaces.ExtDirectActions.updateStageChangeButtons(TYPO3.settings.Workspaces.id, TYPO3.Workspaces.Actions.updateStageChangeButtons); - - if (response.refreshLivePanel == true) { - Ext.getCmp('livePanel').refresh(); - Ext.getCmp('livePanel-hbox').refresh(); - Ext.getCmp('livePanel-vbox').refresh(); - } - }); - top.TYPO3.Windows.close('sendToStageWindow'); - } - }); - } - }); - }, - - /** - * Process "send to previous stage" action. - * - * This method is used in the split frontend preview part. - * - * @return void - */ - sendPageToPrevStage: function () { - TYPO3.Workspaces.ExtDirectActions.sendPageToPreviousStage(TYPO3.settings.Workspaces.id, function (response) { - if (Ext.isObject(response.error)) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - } else { - var dialog = TYPO3.Workspaces.Helpers.getSendToStageWindow({ - title: TYPO3.l10n.localize('nextStage'), - items: response.items.items, - executeHandler: function(event) { - var values = top.Ext.getCmp('sendToStageForm').getForm().getValues(); - - affects = response.affects; - var parameters = { - affects: affects, - receipients: TYPO3.Workspaces.Helpers.getElementIdsFromFormValues(values, 'receipients'), - additional: values.additional, - comments: values.comments, - stageId: response.stageId - }; - TYPO3.Workspaces.ExtDirectActions.sentCollectionToStage(parameters, function (response) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - TYPO3.Workspaces.ExtDirectActions.updateStageChangeButtons(TYPO3.settings.Workspaces.id, TYPO3.Workspaces.Actions.updateStageChangeButtons); - }); - top.TYPO3.Windows.close('sendToStageWindow'); - } - }); - } - }); - }, - - /** - * Update the visible state for the buttons "next stage", "prev stage" and "discard". - * - * This method is used in the split frontend preview part. - * - * @param object response - * @return void - */ - updateStageChangeButtons: function (response) { - - if (Ext.isObject(response.error)) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - } else { - for (componentId in response) { - if (response[componentId].visible) { - if (!top.Ext.getCmp(componentId).isVisible()) { - top.Ext.getCmp(componentId).show(); - } - top.Ext.getCmp(componentId).setText(response[componentId].text.substr(0, 35)); - top.Ext.getCmp(componentId).setTooltip(response[componentId].text); - } else { - if (top.Ext.getCmp(componentId).isVisible()) { - top.Ext.getCmp(componentId).hide(); - } - } - } - // force doLayout on each plugin containing the preview panel - Ext.getCmp('preview').plugins.each(function (item, index) { - if (Ext.isFunction(item.doLayout)) { - item.doLayout(); - } - }); - } - }, - - /** - * Process the discard all items from current page action. - * - * This method is used in the split frontend preview part. - * - * @return void - */ - discardPage: function () { - var configuration = { - title: TYPO3.l10n.localize('window.discardAll.title'), - msg: TYPO3.l10n.localize('window.discardAll.message'), - fn: function(result) { - if (result == 'yes') { - TYPO3.Workspaces.ExtDirectActions.discardStagesFromPage(TYPO3.settings.Workspaces.id, function (response) { - TYPO3.Workspaces.Actions.handlerResponseOnExecuteAction(response); - TYPO3.Workspaces.ExtDirectActions.updateStageChangeButtons(TYPO3.settings.Workspaces.id, TYPO3.Workspaces.Actions.updateStageChangeButtons); - Ext.getCmp('wsPanel').refresh(); - Ext.getCmp('wsPanel-hbox').refresh(); - Ext.getCmp('wsPanel-vbox').refresh(); - }); - } - } - }; - - top.TYPO3.Dialog.QuestionDialog(configuration); - }, - - /** - * Generate workspace preview links for all available languages of a page - * - * @return {void} - */ - generateWorkspacePreviewLinksForAllLanguages: function() { - TYPO3.Workspaces.ExtDirectActions.generateWorkspacePreviewLinksForAllLanguages(TYPO3.settings.Workspaces.id, function(response) { - - var msg = '<ul>'; - - for (language in response) { - var url = response[language]; - - msg += String.format('<li style="margin: 0 0 8px;"><strong>{1}</strong><br /><a href="{0}" target="_blank">{0}</a></li>', url, language); - } - - msg += '</ul>'; - - top.TYPO3.Dialog.InformationDialog({ - title: TYPO3.l10n.localize('previewLink'), - minWidth: '400', - msg: msg - }); - }); - } -}; diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/component.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/component.js deleted file mode 100644 index 1e1e8ff5b86b..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/component.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - - -Ext.ns('TYPO3.Workspaces'); - -Ext.override(Ext.grid.GroupingView, { - constructId : function(value, field, idx) { - var cfg = this.cm.config[idx], - groupRenderer = cfg.groupRenderer || cfg.renderer, - val = (this.groupMode == 'value') ? value : this.getGroup(value, {data:{}}, groupRenderer, 0, idx, this.ds); - - var id = this.getPrefix(field) + val; - id = id.replace(/[^a-zA-Z0-9_]/g, ''); - return id; - } -}); - -Ext.ns('Ext.ux.TYPO3.Workspace'); -Ext.ux.TYPO3.Workspace.RowPanel = Ext.extend(Ext.Panel, { - constructor: function(config) { - config = config || { - frame:true, - width:'100%', - autoHeight:true, - layout:'fit', - title: TYPO3.l10n.localize('rowDetails') - }; - Ext.apply(this, config); - Ext.ux.TYPO3.Workspace.RowPanel.superclass.constructor.call(this, config); - } -}); - -TYPO3.Workspaces.RowExpander = new TYPO3.Workspaces.Component.RowExpander(); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/configuration.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/configuration.js deleted file mode 100644 index f790a00d3e37..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/configuration.js +++ /dev/null @@ -1,400 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -Ext.ns('TYPO3.Workspaces'); -TYPO3.Workspaces.Configuration = {}; - -TYPO3.Workspaces.Configuration.GridFilters = new Ext.ux.grid.GridFilters({ - encode : false, // json encode the filter query - local : true, // defaults to false (remote filtering) - filters : [ - { - type : 'numeric', - dataIndex : 'uid' - }, - { - type : 'string', - dataIndex : 'workspace_Title' - }, - { - type : 'numeric', - dataIndex : 'languageValue' - }, - { - type : 'string', - dataIndex : 'label_Live' - }, - { - type : 'string', - dataIndex : 'label_Workspace' - }, - { - type : 'numeric', - dataIndex : 'change' - } - ] -}); - -TYPO3.Workspaces.Configuration.Integrity = new Ext.grid.Column({ - width: 24, - hideable: true, - sortable: false, - header: TYPO3.settings.Workspaces.icons.integrity + ' ', - renderer: function(value, meta, record) { - if (record.json.integrity.status !== 'success') { - var icon = TYPO3.settings.Workspaces.icons[record.json.integrity.status]; - var title = TYPO3.l10n.localize('status.' + record.json.integrity.status); - var message = record.json.integrity.messages; - - return '<span ext:qtitle="' + title + '" ext:qtip="' + message + '">' + icon + ' </span>'; - } - } -}); -TYPO3.Workspaces.Configuration.WsPath = { - id: 'path_Workspace', - dataIndex : 'path_Workspace', - width: 120, - hidden: true, - hideable: false, - sortable: true, - header : TYPO3.l10n.localize('column.wsPath'), - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - var path = record.json.path_Workspace; - return path; - }, - filter : {type: 'string'} -}; - -TYPO3.Workspaces.Configuration.LivePath = { - id: 'path_Live', - dataIndex : 'path_Live', - width: 120, - hidden: true, - hideable: true, - sortable: true, - header : TYPO3.l10n.localize('column.livePath'), - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - var path = record.json.path_Live; - return path; - }, - filter : {type: 'string'} -}; - -TYPO3.Workspaces.Configuration.WsTitleWithIcon = { - id: 'label_Workspace', - dataIndex : 'label_Workspace', - // basic definition - defaultWidth: 120, - // width is extended depending on collection levels - // the value is set in addition to this.defaultWidth - width: 120, - // additional width used for each collection level - levelWidth: 18, - hideable: false, - sortable: true, - header : TYPO3.l10n.localize('column.wsTitle'), - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - var dekoClass = 'item-state-' + record.json.state_Workspace; - value = "<span class=\"" + dekoClass + "\">" + value + "</span>"; - // Prepend icon - if (record.json.icon_Live !== record.json.icon_Workspace) { - value = record.json.icon_Workspace + " " + value; - } - // Prepend nested collection level - var levelStyle = 'margin-left: ' + record.json.Workspaces_CollectionLevel * this.levelWidth + 'px;'; - if (record.json.Workspaces_CollectionChildren > 0) { - value = '<div class="typo3-workspaces-collection-level-node" style="' + levelStyle + '"> </div>' + value; - } else if (record.json.Workspaces_CollectionLevel > 0) { - value = '<div class="typo3-workspaces-collection-level-leaf" style="' + levelStyle + '"> </div>' + value; - } else { - value = '<div class="typo3-workspaces-collection-level-none" style="' + levelStyle + '"> </div>' + value; - } - return value; - }, - filter : {type: 'string'} -}; - -TYPO3.Workspaces.Configuration.Language = { - id: 'language', - dataIndex: 'languageValue', - width: 30, - hideable: true, - sortable: true, - header: TYPO3.settings.Workspaces.icons.language, - filter: { type: 'string '}, - renderer: function(value, metaData, record) { - return record.json.language.icon; - } -}; - -TYPO3.Workspaces.Configuration.TitleWithIcon = { - id: 'label_Live', - dataIndex : 'label_Live', - width: 120, - hideable: false, - sortable: true, - header : TYPO3.l10n.localize('column.liveTitle'), - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - var dekoClass = ''; - if (record.json.state_Workspace == 'unhidden') { - dekoClass = 'item-state-hidden'; - } - - value = "<span class=\"" + dekoClass + "\">" + value + "</span>"; - return record.json.icon_Live + " " + value; - }, - filter : {type: 'string'} -}; - -TYPO3.Workspaces.Configuration.ChangeDate = { - id: 'workspace_Tstamp', - dataIndex : 'workspace_Tstamp', - width: 120, - sortable: true, - header : TYPO3.l10n.localize('column.changeDate'), - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - return record.json.workspace_Formated_Tstamp; - }, - hidden: true, - filter : {type : 'string'} -}; - -TYPO3.Workspaces.Configuration.SendToPrevStageButton = { - xtype: 'actioncolumn', - header:'', - width: 18, - hidden: false, - items:[ - { - iconCls: 't3-icon t3-icon-extensions t3-icon-extensions-workspaces t3-icon-workspaces-sendtoprevstage', - tooltip: TYPO3.l10n.localize('tooltip.sendToPrevStage'), - handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - TYPO3.Workspaces.Actions.sendToPrevStageWindow(record.json.table, record.json.uid); - } - } - ] -}; - -TYPO3.Workspaces.Configuration.SendToNextStageButton = { - xtype: 'actioncolumn', - header:'', - width: 18, - hidden: false, - items: [ - {},{ // empty dummy important!!!! - iconCls: 't3-icon t3-icon-extensions t3-icon-extensions-workspaces t3-icon-workspaces-sendtonextstage', - tooltip: TYPO3.l10n.localize('tooltip.sendToNextStage'), - handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - TYPO3.Workspaces.Actions.sendToNextStageWindow(record.json.table, record.json.uid, record.json.t3ver_oid); - } - } - ] -}; - -TYPO3.Workspaces.Configuration.Stage = { - id: 'label_Stage', - dataIndex : 'label_Stage', - width: 80, - sortable: true, - header : TYPO3.l10n.localize('column.stage'), - hidden: false, - filter : { - type : 'string' - }, - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - var returnCode = ''; - if (record.json.allowedAction_prevStage) { - var tempTooltip = TYPO3.Workspaces.Configuration.SendToPrevStageButton.items[0].tooltip; - TYPO3.Workspaces.Configuration.SendToPrevStageButton.items[0].tooltip += ' "'+ record.json.label_prevStage + '"'; - var prevButton = new Ext.grid.ActionColumn(TYPO3.Workspaces.Configuration.SendToPrevStageButton); - returnCode += prevButton.renderer(1, metaData, record, rowIndex, 1, store); - TYPO3.Workspaces.Configuration.SendToPrevStageButton.items[0].tooltip = tempTooltip; - } else { - returnCode += "<span class=\"t3-icon t3-icon-empty t3-icon-empty-empty\"> </span>"; - } - returnCode += record.json.label_Stage; - if (record.json.allowedAction_nextStage) { - var tempTooltip = TYPO3.Workspaces.Configuration.SendToNextStageButton.items[1].tooltip; - TYPO3.Workspaces.Configuration.SendToNextStageButton.items[1].tooltip += ' "'+ record.json.label_nextStage + '"'; - var nextButton = new Ext.grid.ActionColumn(TYPO3.Workspaces.Configuration.SendToNextStageButton); - returnCode += nextButton.renderer(2, metaData, record, rowIndex, 2, store); - TYPO3.Workspaces.Configuration.SendToNextStageButton.items[1].tooltip = tempTooltip; - } else { - returnCode += "<span class=\"t3-icon t3-icon-empty t3-icon-empty-empty\"> </span>"; - } - return returnCode; - }, - processEvent : function(name, e, grid, rowIndex, colIndex){ - var m = e.getTarget().className.match(/x-action-col-(\d+)/); - if(m && m[1] == 0) { - TYPO3.Workspaces.Configuration.SendToPrevStageButton.items[0].handler(grid, rowIndex, colIndex); - return false; - } else if (m && m[1] == 1 ) { - TYPO3.Workspaces.Configuration.SendToNextStageButton.items[1].handler(grid, rowIndex, colIndex); - return false; - } - return Ext.grid.ActionColumn.superclass.processEvent.apply(this, arguments); - } -} - -TYPO3.Workspaces.Configuration.RowButtons = { - xtype: 'actioncolumn', - header: TYPO3.l10n.localize('column.actions'), - width: 80, - hideable: false, - hidden: false, - menuDisabled: true, - items: [ - { - iconCls:'t3-icon t3-icon-actions t3-icon-actions-version t3-icon-version-workspace-preview' - ,tooltip: TYPO3.l10n.localize('tooltip.viewElementAction') - ,handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - TYPO3.Workspaces.Actions.viewSingleRecord(record.json.table, record.json.uid); - }, - getClass: function(v, meta, rec) { - if(!rec.json.allowedAction_view) { - return 'icon-hidden'; - } else { - return ''; - } - } - }, - { - iconCls:'t3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-open', - tooltip: TYPO3.l10n.localize('tooltip.editElementAction'), - handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - var newUrl = TYPO3.settings.FormEngine.moduleUrl + '&returnUrl=' + encodeURIComponent(document.location.href) + '&id=' + TYPO3.settings.Workspaces.id + '&edit[' + record.json.table + '][' + record.json.uid + ']=edit'; - // Append workspace of record in all-workspaces view - if (TYPO3.settings.Workspaces.allView) { - newUrl += '&workspace=' + record.json.t3ver_wsid; - } - window.location.href = newUrl; - }, - getClass: function(v, meta, rec) { - if(!rec.json.allowedAction_edit) { - return 'icon-hidden'; - } else { - return ''; - } - } - }, - { - iconCls:'t3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-pagemodule-open', - tooltip: TYPO3.l10n.localize('tooltip.openPage'), - handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - if (record.json.table == 'pages') { - top.loadEditId(record.json.t3ver_oid); - } else { - top.loadEditId(record.json.livepid); - } - }, - getClass: function(v, meta, rec) { - if(!rec.json.allowedAction_editVersionedPage || !TYPO3.Workspaces.Helpers.isDefined('top.TYPO3.configuration.pageModule') || !top.TYPO3.configuration.pageModule) { - return 'icon-hidden'; - } else { - return ''; - } - } - }, - { - iconCls:'t3-icon t3-icon-actions t3-icon-actions-version t3-icon-version-document-remove', - tooltip: TYPO3.l10n.localize('tooltip.discardVersion'), - handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - var configuration = { - title: TYPO3.l10n.localize('window.discard.title'), - msg: TYPO3.l10n.localize('window.discard.message'), - fn: function(result) { - if (result == 'yes') { - TYPO3.Workspaces.Actions.deleteSingleRecord(record.json.table, record.json.uid); - } - } - }; - - top.TYPO3.Dialog.QuestionDialog(configuration); - }, - getClass: function(v, meta, rec) { - if(!rec.json.allowedAction_delete) { - return 'icon-hidden'; - } else { - return ''; - } - } - }, - { - iconCls: 't3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-history-open', - tooltip: TYPO3.l10n.localize('tooltip.showHistory'), - handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - TYPO3.Workspaces.Helpers.getHistoryWindow({ - table: record.json.table, - liveId: record.json.t3ver_oid, - versionId: record.json.uid - }); - } - } - ] -}; - -TYPO3.Workspaces.Configuration.SwapButton = { - xtype: 'actioncolumn', - header: '', - id: 'wsSwapColumn', - width: 18, - menuDisabled: true, - sortable: false, - hidden: false, - items: [ - { - iconCls:'t3-icon t3-icon-actions t3-icon-actions-version t3-icon-version-swap-workspace' - ,tooltip: TYPO3.l10n.localize('tooltip.swap') - ,handler: function(grid, rowIndex, colIndex) { - var record = TYPO3.Workspaces.MainStore.getAt(rowIndex); - var parameters = { - type: 'selection', - selection: [{ - table: record.json.table, - versionId: record.json.uid, - liveId: record.json.t3ver_oid - }] - }; - - var configuration = { - title: TYPO3.l10n.localize('window.swap.title'), - msg: TYPO3.l10n.localize('window.swap.message'), - fn: function(result) { - if (result == 'yes') { - TYPO3.Workspaces.Actions.swapSingleRecord(record.json.table, record.json.t3ver_oid, record.json.uid); - } - } - }; - - TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() { - top.TYPO3.Dialog.QuestionDialog(configuration); - }); - }, - getClass: function(v, meta, rec) { - if(!rec.json.allowedAction_swap) { - return 'icon-hidden'; - } else { - return ''; - } - } - } - ] -}; diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/grid.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/grid.js deleted file mode 100644 index be493f0faa11..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/grid.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -Ext.ns('TYPO3.Workspaces'); - -Ext.override(Ext.grid.GridView, { - beforeColMenuShow : function() { - var colModel = this.cm, - colCount = colModel.getColumnCount(), - colMenu = this.colMenu, - i, text; - - colMenu.removeAll(); - - for (i = 0; i < colCount; i++) { - if (colModel.config[i].hideable !== false) { - text = colModel.getColumnHeader(i); - if (colModel.getColumnId(i) === 'wsSwapColumn') { - text = TYPO3.l10n.localize('column.wsSwapColumn'); - } - colMenu.add(new Ext.menu.CheckItem({ - text: text, - itemId: 'col-' + colModel.getColumnId(i), - checked: !colModel.isHidden(i), - disabled: colModel.config[i].hideable === false, - hideOnClick: false - })); - } - } - } -}); - -/** override mousedown for grid to select checkbox respecting singleSelect */ -Ext.override(Ext.grid.CheckboxSelectionModel, { - handleMouseDown: function(g, rowIndex, e) { - e.stopEvent(); - if (this.isSelected(rowIndex)) { - this.deselectRow(rowIndex); - } else { - this.selectRow(rowIndex, true); - this.grid.getView().focusRow(rowIndex); - } - } -}); - -TYPO3.Workspaces.SelectionModel = new Ext.grid.CheckboxSelectionModel({ - singleSelect: false, - hidden: true, - listeners: { - beforerowselect : function (selection, rowIndex, keep, rec) { - if (rec.json.allowedAction_nextStage || rec.json.allowedAction_prevStage || rec.json.allowedAction_swap) { - return true; - } else { - return false; - } - }, - selectionchange: function (selection) { - var record = selection.grid.getSelectionModel().getSelections(); - if (record.length > 0) { - TYPO3.Workspaces.Toolbar.selectStateActionCombo.setDisabled(false); - TYPO3.Workspaces.Toolbar.selectionActionCombo.setDisabled(false); - TYPO3.Workspaces.Toolbar.selectStateMassActionCombo.setDisabled(true); - } else { - TYPO3.Workspaces.Toolbar.selectStateActionCombo.setDisabled(true); - TYPO3.Workspaces.Toolbar.selectionActionCombo.setDisabled(true); - TYPO3.Workspaces.Toolbar.selectStateMassActionCombo.setDisabled(false); - } - } - } -}); - -TYPO3.Workspaces.WorkspaceGrid = new Ext.grid.GridPanel({ - initColModel: function() { - if (TYPO3.settings.Workspaces.isLiveWorkspace) { - this.colModel = new Ext.grid.ColumnModel({ - columns: [ - TYPO3.Workspaces.RowExpander, - {id: 'uid', dataIndex : 'uid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.uid'), hidden: true, filterable : true }, - {id: 't3ver_oid', dataIndex : 't3ver_oid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.oid'), hidden: true, filterable : true }, - {id: 'workspace_Title', dataIndex : 'workspace_Title', width: 120, sortable: true, header : TYPO3.l10n.localize('column.workspaceName'), hidden: true, filter : {type : 'string'}}, - TYPO3.Workspaces.Configuration.WsPath, - TYPO3.Workspaces.Configuration.LivePath, - TYPO3.Workspaces.Configuration.WsTitleWithIcon, - TYPO3.Workspaces.Configuration.TitleWithIcon, - TYPO3.Workspaces.Configuration.ChangeDate, - TYPO3.Workspaces.Configuration.Integrity, - TYPO3.Workspaces.Configuration.Language - ].concat(TYPO3.Workspaces.Helpers.getAdditionalColumnHandler()), - listeners: { - columnmoved: TYPO3.Workspaces.Actions.updateColModel, - hiddenchange: TYPO3.Workspaces.Actions.updateColModel - } - }); - } else { - this.colModel = new Ext.grid.ColumnModel({ - columns: [ - TYPO3.Workspaces.SelectionModel, - TYPO3.Workspaces.RowExpander, - {id: 'uid', dataIndex : 'uid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.uid'), hidden: true, filterable : true }, - {id: 't3ver_oid', dataIndex : 't3ver_oid', width: 40, sortable: true, header : TYPO3.l10n.localize('column.oid'), hidden: true, filterable : true }, - {id: 'workspace_Title', dataIndex : 'workspace_Title', width: 120, sortable: true, header : TYPO3.l10n.localize('column.workspaceName'), hidden: true, filter : {type : 'string'}}, - TYPO3.Workspaces.Configuration.WsPath, - TYPO3.Workspaces.Configuration.LivePath, - TYPO3.Workspaces.Configuration.WsTitleWithIcon, - TYPO3.Workspaces.Configuration.SwapButton, - TYPO3.Workspaces.Configuration.TitleWithIcon, - TYPO3.Workspaces.Configuration.ChangeDate, - TYPO3.Workspaces.Configuration.Stage, - TYPO3.Workspaces.Configuration.RowButtons, - TYPO3.Workspaces.Configuration.Integrity, - TYPO3.Workspaces.Configuration.Language - ].concat(TYPO3.Workspaces.Helpers.getAdditionalColumnHandler()), - listeners: { - columnmoved: TYPO3.Workspaces.Actions.updateColModel, - hiddenchange: TYPO3.Workspaces.Actions.updateColModel - } - }); - } - }, - border : true, - store : TYPO3.Workspaces.MainStore, - colModel : null, - sm: TYPO3.Workspaces.SelectionModel, - loadMask : true, - height: 630, - stripeRows: true, - // below the grid we need 40px space for the legend - heightOffset: 40, - plugins : [ - TYPO3.Workspaces.RowExpander, - TYPO3.Workspaces.Configuration.GridFilters, - new Ext.ux.plugins.FitToParent() - ], - view : new Ext.grid.GroupingView({ - forceFit: true, - groupTextTpl : '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "' + TYPO3.l10n.localize('items') + '" : "' + TYPO3.l10n.localize('item') + '"]})', - enableGroupingMenu: false, - enableNoGroups: false, - hideGroupedColumn: true - }), - bbar : TYPO3.Workspaces.Toolbar.FullBottomBar, - tbar : TYPO3.Workspaces.Toolbar.FullTopToolbar -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/GridFilters.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/GridFilters.js deleted file mode 100644 index 07dbfe339635..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/GridFilters.js +++ /dev/null @@ -1,740 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -Ext.namespace('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.GridFilters - * @extends Ext.util.Observable - * <p>GridFilter is a plugin (<code>ptype='gridfilters'</code>) for grids that - * allow for a slightly more robust representation of filtering than what is - * provided by the default store.</p> - * <p>Filtering is adjusted by the user using the grid's column header menu - * (this menu can be disabled through configuration). Through this menu users - * can configure, enable, and disable filters for each column.</p> - * <p><b><u>Features:</u></b></p> - * <div class="mdetail-params"><ul> - * <li><b>Filtering implementations</b> : - * <div class="sub-desc"> - * Default filtering for Strings, Numeric Ranges, Date Ranges, Lists (which can - * be backed by a Ext.data.Store), and Boolean. Additional custom filter types - * and menus are easily created by extending Ext.ux.grid.filter.Filter. - * </div></li> - * <li><b>Graphical indicators</b> : - * <div class="sub-desc"> - * Columns that are filtered have {@link #filterCls a configurable css class} - * applied to the column headers. - * </div></li> - * <li><b>Paging</b> : - * <div class="sub-desc"> - * If specified as a plugin to the grid's configured PagingToolbar, the current page - * will be reset to page 1 whenever you update the filters. - * </div></li> - * <li><b>Automatic Reconfiguration</b> : - * <div class="sub-desc"> - * Filters automatically reconfigure when the grid 'reconfigure' event fires. - * </div></li> - * <li><b>Stateful</b> : - * Filter information will be persisted across page loads by specifying a - * <code>stateId</code> in the Grid configuration. - * <div class="sub-desc"> - * The filter collection binds to the - * <code>{@link Ext.grid.GridPanel#beforestaterestore beforestaterestore}</code> - * and <code>{@link Ext.grid.GridPanel#beforestatesave beforestatesave}</code> - * events in order to be stateful. - * </div></li> - * <li><b>Grid Changes</b> : - * <div class="sub-desc"><ul> - * <li>A <code>filters</code> <i>property</i> is added to the grid pointing to - * this plugin.</li> - * <li>A <code>filterupdate</code> <i>event</i> is added to the grid and is - * fired upon onStateChange completion.</li> - * </ul></div></li> - * <li><b>Server side code examples</b> : - * <div class="sub-desc"><ul> - * <li><a href="http://www.vinylfox.com/extjs/grid-filter-php-backend-code.php">PHP</a> - (Thanks VinylFox)</li> - * <li><a href="http://extjs.com/forum/showthread.php?p=77326#post77326">Ruby on Rails</a> - (Thanks Zyclops)</li> - * <li><a href="http://extjs.com/forum/showthread.php?p=176596#post176596">Ruby on Rails</a> - (Thanks Rotomaul)</li> - * <li><a href="http://www.debatablybeta.com/posts/using-extjss-grid-filtering-with-django/">Python</a> - (Thanks Matt)</li> - * <li><a href="http://mcantrell.wordpress.com/2008/08/22/extjs-grids-and-grails/">Grails</a> - (Thanks Mike)</li> - * </ul></div></li> - * </ul></div> - * <p><b><u>Example usage:</u></b></p> - * <pre><code> -var store = new Ext.data.GroupingStore({ - ... -}); - -var filters = new Ext.ux.grid.GridFilters({ - autoReload: false, //don't reload automatically - local: true, //only filter locally - // filters may be configured through the plugin, - // or in the column definition within the column model configuration - filters: [{ - type: 'numeric', - dataIndex: 'id' - }, { - type: 'string', - dataIndex: 'name' - }, { - type: 'numeric', - dataIndex: 'price' - }, { - type: 'date', - dataIndex: 'dateAdded' - }, { - type: 'list', - dataIndex: 'size', - options: ['extra small', 'small', 'medium', 'large', 'extra large'], - phpMode: true - }, { - type: 'boolean', - dataIndex: 'visible' - }] -}); -var cm = new Ext.grid.ColumnModel([{ - ... -}]); - -var grid = new Ext.grid.GridPanel({ - ds: store, - cm: cm, - view: new Ext.grid.GroupingView(), - plugins: [filters], - height: 400, - width: 700, - bbar: new Ext.PagingToolbar({ - store: store, - pageSize: 15, - plugins: [filters] //reset page to page 1 if filters change - }) - }); - -store.load({params: {start: 0, limit: 15}}); - -// a filters property is added to the grid -grid.filters - * </code></pre> - */ -Ext.ux.grid.GridFilters = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Boolean} autoReload - * Defaults to true, reloading the datasource when a filter change happens. - * Set this to false to prevent the datastore from being reloaded if there - * are changes to the filters. See <code>{@link updateBuffer}</code>. - */ - autoReload : true, - /** - * @cfg {Boolean} encode - * Specify true for {@link #buildQuery} to use Ext.util.JSON.encode to - * encode the filter query parameter sent with a remote request. - * Defaults to false. - */ - /** - * @cfg {Array} filters - * An Array of filters config objects. Refer to each filter type class for - * configuration details specific to each filter type. Filters for Strings, - * Numeric Ranges, Date Ranges, Lists, and Boolean are the standard filters - * available. - */ - /** - * @cfg {String} filterCls - * The css class to be applied to column headers with active filters. - * Defaults to <tt>'ux-filterd-column'</tt>. - */ - filterCls : 'ux-filtered-column', - /** - * @cfg {Boolean} local - * <tt>true</tt> to use Ext.data.Store filter functions (local filtering) - * instead of the default (<tt>false</tt>) server side filtering. - */ - local : false, - /** - * @cfg {String} menuFilterText - * defaults to <tt>'Filters'</tt>. - */ - menuFilterText : 'Filters', - /** - * @cfg {String} paramPrefix - * The url parameter prefix for the filters. - * Defaults to <tt>'filter'</tt>. - */ - paramPrefix : 'filter', - /** - * @cfg {Boolean} showMenu - * Defaults to true, including a filter submenu in the default header menu. - */ - showMenu : true, - /** - * @cfg {String} stateId - * Name of the value to be used to store state information. - */ - stateId : undefined, - /** - * @cfg {Integer} updateBuffer - * Number of milliseconds to defer store updates since the last filter change. - */ - updateBuffer : 500, - - /** @private */ - constructor : function (config) { - config = config || {}; - this.deferredUpdate = new Ext.util.DelayedTask(this.reload, this); - this.filters = new Ext.util.MixedCollection(); - this.filters.getKey = function (o) { - return o ? o.dataIndex : null; - }; - this.addFilters(config.filters); - delete config.filters; - Ext.apply(this, config); - }, - - /** @private */ - init : function (grid) { - if (grid instanceof Ext.grid.GridPanel) { - this.grid = grid; - - this.bindStore(this.grid.getStore(), true); - // assumes no filters were passed in the constructor, so try and use ones from the colModel - if(this.filters.getCount() == 0){ - this.addFilters(this.grid.getColumnModel()); - } - - this.grid.filters = this; - - this.grid.addEvents({'filterupdate': true}); - - grid.on({ - scope: this, - beforestaterestore: this.applyState, - beforestatesave: this.saveState, - beforedestroy: this.destroy, - reconfigure: this.onReconfigure - }); - - if (grid.rendered){ - this.onRender(); - } else { - grid.on({ - scope: this, - single: true, - render: this.onRender - }); - } - - } else if (grid instanceof Ext.PagingToolbar) { - this.toolbar = grid; - } - }, - - /** - * @private - * Handler for the grid's beforestaterestore event (fires before the state of the - * grid is restored). - * @param {Object} grid The grid object - * @param {Object} state The hash of state values returned from the StateProvider. - */ - applyState : function (grid, state) { - var key, filter; - this.applyingState = true; - this.clearFilters(); - if (state.filters) { - for (key in state.filters) { - filter = this.filters.get(key); - if (filter) { - filter.setValue(state.filters[key]); - filter.setActive(true); - } - } - } - this.deferredUpdate.cancel(); - if (this.local) { - this.reload(); - } - delete this.applyingState; - delete state.filters; - }, - - /** - * Saves the state of all active filters - * @param {Object} grid - * @param {Object} state - * @return {Boolean} - */ - saveState : function (grid, state) { - var filters = {}; - this.filters.each(function (filter) { - if (filter.active) { - filters[filter.dataIndex] = filter.getValue(); - } - }); - return (state.filters = filters); - }, - - /** - * @private - * Handler called when the grid is rendered - */ - onRender : function () { - this.grid.getView().on('refresh', this.onRefresh, this); - this.createMenu(); - }, - - /** - * @private - * Handler called by the grid 'beforedestroy' event - */ - destroy : function () { - this.removeAll(); - this.purgeListeners(); - - if(this.filterMenu){ - Ext.menu.MenuMgr.unregister(this.filterMenu); - this.filterMenu.destroy(); - this.filterMenu = this.menu.menu = null; - } - }, - - /** - * Remove all filters, permanently destroying them. - */ - removeAll : function () { - if(this.filters){ - Ext.destroy.apply(Ext, this.filters.items); - // remove all items from the collection - this.filters.clear(); - } - }, - - - /** - * Changes the data store bound to this view and refreshes it. - * @param {Store} store The store to bind to this view - */ - bindStore : function(store, initial){ - if(!initial && this.store){ - if (this.local) { - store.un('load', this.onLoad, this); - } else { - store.un('beforeload', this.onBeforeLoad, this); - } - } - if(store){ - if (this.local) { - store.on('load', this.onLoad, this); - } else { - store.on('beforeload', this.onBeforeLoad, this); - } - } - this.store = store; - }, - - /** - * @private - * Handler called when the grid reconfigure event fires - */ - onReconfigure : function () { - this.bindStore(this.grid.getStore()); - this.store.clearFilter(); - this.removeAll(); - this.addFilters(this.grid.getColumnModel()); - this.updateColumnHeadings(); - }, - - createMenu : function () { - var view = this.grid.getView(), - hmenu = view.hmenu; - - if (this.showMenu && hmenu) { - - this.sep = hmenu.addSeparator(); - this.filterMenu = new Ext.menu.Menu({ - id: this.grid.id + '-filters-menu' - }); - this.menu = hmenu.add({ - checked: false, - itemId: 'filters', - text: this.menuFilterText, - menu: this.filterMenu - }); - - this.menu.on({ - scope: this, - checkchange: this.onCheckChange, - beforecheckchange: this.onBeforeCheck - }); - hmenu.on('beforeshow', this.onMenu, this); - } - this.updateColumnHeadings(); - }, - - /** - * @private - * Get the filter menu from the filters MixedCollection based on the clicked header - */ - getMenuFilter : function () { - var view = this.grid.getView(); - if (!view || view.hdCtxIndex === undefined) { - return null; - } - return this.filters.get( - view.cm.config[view.hdCtxIndex].dataIndex - ); - }, - - /** - * @private - * Handler called by the grid's hmenu beforeshow event - */ - onMenu : function (filterMenu) { - var filter = this.getMenuFilter(); - - if (filter) { -/* -TODO: lazy rendering - if (!filter.menu) { - filter.menu = filter.createMenu(); - } -*/ - this.menu.menu = filter.menu; - this.menu.setChecked(filter.active, false); - // disable the menu if filter.disabled explicitly set to true - this.menu.setDisabled(filter.disabled === true); - } - - this.menu.setVisible(filter !== undefined); - this.sep.setVisible(filter !== undefined); - }, - - /** @private */ - onCheckChange : function (item, value) { - this.getMenuFilter().setActive(value); - }, - - /** @private */ - onBeforeCheck : function (check, value) { - return !value || this.getMenuFilter().isActivatable(); - }, - - /** - * @private - * Handler for all events on filters. - * @param {String} event Event name - * @param {Object} filter Standard signature of the event before the event is fired - */ - onStateChange : function (event, filter) { - if (event === 'serialize') { - return; - } - - if (filter == this.getMenuFilter()) { - this.menu.setChecked(filter.active, false); - } - - if ((this.autoReload || this.local) && !this.applyingState) { - this.deferredUpdate.delay(this.updateBuffer); - } - this.updateColumnHeadings(); - - if (!this.applyingState) { - this.grid.saveState(); - } - this.grid.fireEvent('filterupdate', this, filter); - }, - - /** - * @private - * Handler for store's beforeload event when configured for remote filtering - * @param {Object} store - * @param {Object} options - */ - onBeforeLoad : function (store, options) { - options.params = options.params || {}; - this.cleanParams(options.params); - var params = this.buildQuery(this.getFilterData()); - Ext.apply(options.params, params); - }, - - /** - * @private - * Handler for store's load event when configured for local filtering - * @param {Object} store - * @param {Object} options - */ - onLoad : function (store, options) { - store.filterBy(this.getRecordFilter()); - }, - - /** - * @private - * Handler called when the grid's view is refreshed - */ - onRefresh : function () { - this.updateColumnHeadings(); - }, - - /** - * Update the styles for the header row based on the active filters - */ - updateColumnHeadings : function () { - var view = this.grid.getView(), - i, len, filter; - if (view.mainHd) { - for (i = 0, len = view.cm.config.length; i < len; i++) { - filter = this.getFilter(view.cm.config[i].dataIndex); - Ext.fly(view.getHeaderCell(i))[filter && filter.active ? 'addClass' : 'removeClass'](this.filterCls); - } - } - }, - - /** @private */ - reload : function () { - if (this.local) { - this.grid.store.clearFilter(true); - this.grid.store.filterBy(this.getRecordFilter()); - } else { - var start, - store = this.grid.store; - this.deferredUpdate.cancel(); - if (this.toolbar) { - start = store.paramNames.start; - if (store.lastOptions && store.lastOptions.params && store.lastOptions.params[start]) { - store.lastOptions.params[start] = 0; - } - } - store.reload(); - } - }, - - /** - * Method factory that generates a record validator for the filters active at the time - * of invokation. - * @private - */ - getRecordFilter : function () { - var f = [], len, i; - this.filters.each(function (filter) { - if (filter.active) { - f.push(filter); - } - }); - - len = f.length; - return function (record) { - for (i = 0; i < len; i++) { - if (!f[i].validateRecord(record)) { - return false; - } - } - return true; - }; - }, - - /** - * Adds a filter to the collection and observes it for state change. - * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object. - * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object. - */ - addFilter : function (config) { - var Cls = this.getFilterClass(config.type), - filter = config.menu ? config : (new Cls(config)); - this.filters.add(filter); - - Ext.util.Observable.capture(filter, this.onStateChange, this); - return filter; - }, - - /** - * Adds filters to the collection. - * @param {Array/Ext.grid.ColumnModel} filters Either an Array of - * filter configuration objects or an Ext.grid.ColumnModel. The columns - * of a passed Ext.grid.ColumnModel will be examined for a <code>filter</code> - * property and, if present, will be used as the filter configuration object. - */ - addFilters : function (filters) { - if (filters) { - var i, len, filter, cm = false, dI; - if (filters instanceof Ext.grid.ColumnModel) { - filters = filters.config; - cm = true; - } - for (i = 0, len = filters.length; i < len; i++) { - filter = false; - if (cm) { - dI = filters[i].dataIndex; - filter = filters[i].filter || filters[i].filterable; - if (filter){ - filter = (filter === true) ? {} : filter; - Ext.apply(filter, {dataIndex:dI}); - // filter type is specified in order of preference: - // filter type specified in config - // type specified in store's field's type config - filter.type = filter.type || this.store.fields.get(dI).type.type; - } - } else { - filter = filters[i]; - } - // if filter config found add filter for the column - if (filter) { - this.addFilter(filter); - } - } - } - }, - - /** - * Returns a filter for the given dataIndex, if one exists. - * @param {String} dataIndex The dataIndex of the desired filter object. - * @return {Ext.ux.grid.filter.Filter} - */ - getFilter : function (dataIndex) { - return this.filters.get(dataIndex); - }, - - /** - * Turns all filters off. This does not clear the configuration information - * (see {@link #removeAll}). - */ - clearFilters : function () { - this.filters.each(function (filter) { - filter.setActive(false); - }); - }, - - /** - * Returns an Array of the currently active filters. - * @return {Array} filters Array of the currently active filters. - */ - getFilterData : function () { - var filters = [], i, len; - - this.filters.each(function (f) { - if (f.active) { - var d = [].concat(f.serialize()); - for (i = 0, len = d.length; i < len; i++) { - filters.push({ - field: f.dataIndex, - data: d[i] - }); - } - } - }); - return filters; - }, - - /** - * Function to take the active filters data and build it into a query. - * The format of the query depends on the <code>{@link #encode}</code> - * configuration: - * <div class="mdetail-params"><ul> - * - * <li><b><tt>false</tt></b> : <i>Default</i> - * <div class="sub-desc"> - * Flatten into query string of the form (assuming <code>{@link #paramPrefix}='filters'</code>: - * <pre><code> -filters[0][field]="someDataIndex"& -filters[0][data][comparison]="someValue1"& -filters[0][data][type]="someValue2"& -filters[0][data][value]="someValue3"& - * </code></pre> - * </div></li> - * <li><b><tt>true</tt></b> : - * <div class="sub-desc"> - * JSON encode the filter data - * <pre><code> -filters[0][field]="someDataIndex"& -filters[0][data][comparison]="someValue1"& -filters[0][data][type]="someValue2"& -filters[0][data][value]="someValue3"& - * </code></pre> - * </div></li> - * </ul></div> - * Override this method to customize the format of the filter query for remote requests. - * @param {Array} filters A collection of objects representing active filters and their configuration. - * Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured - * to be unique as any one filter may be a composite of more basic filters for the same dataIndex. - * @return {Object} Query keys and values - */ - buildQuery : function (filters) { - var p = {}, i, f, root, dataPrefix, key, tmp, - len = filters.length; - - if (!this.encode){ - for (i = 0; i < len; i++) { - f = filters[i]; - root = [this.paramPrefix, '[', i, ']'].join(''); - p[root + '[field]'] = f.field; - - dataPrefix = root + '[data]'; - for (key in f.data) { - p[[dataPrefix, '[', key, ']'].join('')] = f.data[key]; - } - } - } else { - tmp = []; - for (i = 0; i < len; i++) { - f = filters[i]; - tmp.push(Ext.apply( - {}, - {field: f.field}, - f.data - )); - } - // only build if there is active filter - if (tmp.length > 0){ - p[this.paramPrefix] = Ext.util.JSON.encode(tmp); - } - } - return p; - }, - - /** - * Removes filter related query parameters from the provided object. - * @param {Object} p Query parameters that may contain filter related fields. - */ - cleanParams : function (p) { - // if encoding just delete the property - if (this.encode) { - delete p[this.paramPrefix]; - // otherwise scrub the object of filter data - } else { - var regex, key; - regex = new RegExp('^' + this.paramPrefix + '\[[0-9]+\]'); - for (key in p) { - if (regex.test(key)) { - delete p[key]; - } - } - } - }, - - /** - * Function for locating filter classes, overwrite this with your favorite - * loader to provide dynamic filter loading. - * @param {String} type The type of filter to load ('Filter' is automatically - * appended to the passed type; eg, 'string' becomes 'StringFilter'). - * @return {Class} The Ext.ux.grid.filter.Class - */ - getFilterClass : function (type) { - // map the supported Ext.data.Field type values into a supported filter - switch(type) { - case 'auto': - type = 'string'; - break; - case 'int': - case 'float': - type = 'numeric'; - break; - case 'bool': - type = 'boolean'; - break; - } - return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter']; - } -}); - -// register ptype -Ext.preg('gridfilters', Ext.ux.grid.GridFilters); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/GridFilters.css b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/GridFilters.css deleted file mode 100644 index 0d1057866ad5..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/GridFilters.css +++ /dev/null @@ -1,53 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * GridFilters Styles - **/ -/* -.x-grid3-hd-row .ux-filtered-column { - border-left: 1px solid #C7E3B4; - border-right: 1px solid #C7E3B4; -} - -.x-grid3-hd-row .ux-filtered-column .x-grid3-hd-inner { - background-image: url(../images/header_bg.gif); -} - -.ux-filtered-column .x-grid3-hd-btn { - background-image: url(../images/hd-btn.gif); -} -*/ -.x-grid3-hd-row td.ux-filtered-column { - font-style: italic; - font-weight: bold; -} - -.ux-filtered-column.sort-asc .x-grid3-sort-icon { - background-image: url(../images/sort_filtered_asc.gif) !important; -} - -.ux-filtered-column.sort-desc .x-grid3-sort-icon { - background-image: url(../images/sort_filtered_desc.gif) !important; -} - -.ux-gridfilter-text-icon { - background-image: url(../images/find.png) !important; -} - -/* Temporary Patch for Bug ??? */ -.x-menu-list-item-indent .x-menu-item-icon { - position: relative; - top: 3px; - left: 3px; - margin-right: 10px; -} -li.x-menu-list-item-indent { - padding-left:0px; -} -li.x-menu-list-item div { - display: block; -} \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/RangeMenu.css b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/RangeMenu.css deleted file mode 100644 index 9495126e2e86..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/css/RangeMenu.css +++ /dev/null @@ -1,20 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * RangeMenu Styles - **/ -.ux-rangemenu-gt { - background-image: url(../images/greater_than.png) !important; -} - -.ux-rangemenu-lt { - background-image: url(../images/less_than.png) !important; -} - -.ux-rangemenu-eq { - background-image: url(../images/equals.png) !important; -} diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/BooleanFilter.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/BooleanFilter.js deleted file mode 100644 index 3d9af5baf737..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/BooleanFilter.js +++ /dev/null @@ -1,103 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * @class Ext.ux.grid.filter.BooleanFilter - * @extends Ext.ux.grid.filter.Filter - * Boolean filters use unique radio group IDs (so you can have more than one!) - * <p><b><u>Example Usage:</u></b></p> - * <pre><code> -var filters = new Ext.ux.grid.GridFilters({ - ... - filters: [{ - // required configs - type: 'boolean', - dataIndex: 'visible' - - // optional configs - defaultValue: null, // leave unselected (false selected by default) - yesText: 'Yes', // default - noText: 'No' // default - }] -}); - * </code></pre> - */ -Ext.ux.grid.filter.BooleanFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - /** - * @cfg {Boolean} defaultValue - * Set this to null if you do not want either option to be checked by default. Defaults to false. - */ - defaultValue : false, - /** - * @cfg {String} yesText - * Defaults to 'Yes'. - */ - yesText : 'Yes', - /** - * @cfg {String} noText - * Defaults to 'No'. - */ - noText : 'No', - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - var gId = Ext.id(); - this.options = [ - new Ext.menu.CheckItem({text: this.yesText, group: gId, checked: this.defaultValue === true}), - new Ext.menu.CheckItem({text: this.noText, group: gId, checked: this.defaultValue === false})]; - - this.menu.add(this.options[0], this.options[1]); - - for(var i=0; i<this.options.length; i++){ - this.options[i].on('click', this.fireUpdate, this); - this.options[i].on('checkchange', this.fireUpdate, this); - } - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.options[0].checked; - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.options[value ? 0 : 1].setChecked(true); - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var args = {type: 'boolean', value: this.getValue()}; - return args; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - return record.get(this.dataIndex) == this.getValue(); - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/DateFilter.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/DateFilter.js deleted file mode 100644 index af8e3f128298..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/DateFilter.js +++ /dev/null @@ -1,313 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * @class Ext.ux.grid.filter.DateFilter - * @extends Ext.ux.grid.filter.Filter - * Filter by a configurable Ext.menu.DateMenu - * <p><b><u>Example Usage:</u></b></p> - * <pre><code> -var filters = new Ext.ux.grid.GridFilters({ - ... - filters: [{ - // required configs - type: 'date', - dataIndex: 'dateAdded', - - // optional configs - dateFormat: 'm/d/Y', // default - beforeText: 'Before', // default - afterText: 'After', // default - onText: 'On', // default - pickerOpts: { - // any DateMenu configs - }, - - active: true // default is false - }] -}); - * </code></pre> - */ -Ext.ux.grid.filter.DateFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - /** - * @cfg {String} afterText - * Defaults to 'After'. - */ - afterText : 'After', - /** - * @cfg {String} beforeText - * Defaults to 'Before'. - */ - beforeText : 'Before', - /** - * @cfg {Object} compareMap - * Map for assigning the comparison values used in serialization. - */ - compareMap : { - before: 'lt', - after: 'gt', - on: 'eq' - }, - /** - * @cfg {String} dateFormat - * The date format to return when using getValue. - * Defaults to 'm/d/Y'. - */ - dateFormat : 'm/d/Y', - - /** - * @cfg {Date} maxDate - * Allowable date as passed to the Ext.DatePicker - * Defaults to undefined. - */ - /** - * @cfg {Date} minDate - * Allowable date as passed to the Ext.DatePicker - * Defaults to undefined. - */ - /** - * @cfg {Array} menuItems - * The items to be shown in this menu - * Defaults to:<pre> - * menuItems : ['before', 'after', '-', 'on'], - * </pre> - */ - menuItems : ['before', 'after', '-', 'on'], - - /** - * @cfg {Object} menuItemCfgs - * Default configuration options for each menu item - */ - menuItemCfgs : { - selectOnFocus: true, - width: 125 - }, - - /** - * @cfg {String} onText - * Defaults to 'On'. - */ - onText : 'On', - - /** - * @cfg {Object} pickerOpts - * Configuration options for the date picker associated with each field. - */ - pickerOpts : {}, - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - var menuCfg, i, len, item, cfg, Cls; - - menuCfg = Ext.apply(this.pickerOpts, { - minDate: this.minDate, - maxDate: this.maxDate, - format: this.dateFormat, - listeners: { - scope: this, - select: this.onMenuSelect - } - }); - - this.fields = {}; - for (i = 0, len = this.menuItems.length; i < len; i++) { - item = this.menuItems[i]; - if (item !== '-') { - cfg = { - itemId: 'range-' + item, - text: this[item + 'Text'], - menu: new Ext.menu.DateMenu( - Ext.apply(menuCfg, { - itemId: item - }) - ), - listeners: { - scope: this, - checkchange: this.onCheckChange - } - }; - Cls = Ext.menu.CheckItem; - item = this.fields[item] = new Cls(cfg); - } - //this.add(item); - this.menu.add(item); - } - }, - - onCheckChange : function () { - this.setActive(this.isActivatable()); - this.fireEvent('update', this); - }, - - /** - * @private - * Handler method called when there is a keyup event on an input - * item of this menu. - */ - onInputKeyUp : function (field, e) { - var k = e.getKey(); - if (k == e.RETURN && field.isValid()) { - e.stopEvent(); - this.menu.hide(true); - return; - } - }, - - /** - * Handler for when the menu for a field fires the 'select' event - * @param {Object} date - * @param {Object} menuItem - * @param {Object} value - * @param {Object} picker - */ - onMenuSelect : function (menuItem, value, picker) { - var fields = this.fields, - field = this.fields[menuItem.itemId]; - - field.setChecked(true); - - if (field == fields.on) { - fields.before.setChecked(false, true); - fields.after.setChecked(false, true); - } else { - fields.on.setChecked(false, true); - if (field == fields.after && fields.before.menu.picker.value < value) { - fields.before.setChecked(false, true); - } else if (field == fields.before && fields.after.menu.picker.value > value) { - fields.after.setChecked(false, true); - } - } - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - var key, result = {}; - for (key in this.fields) { - if (this.fields[key].checked) { - result[key] = this.fields[key].menu.picker.getValue(); - } - } - return result; - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - * @param {Boolean} preserve true to preserve the checked status - * of the other fields. Defaults to false, unchecking the - * other fields - */ - setValue : function (value, preserve) { - var key; - for (key in this.fields) { - if(value[key]){ - this.fields[key].menu.picker.setValue(value[key]); - this.fields[key].setChecked(true); - } else if (!preserve) { - this.fields[key].setChecked(false); - } - } - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to return <tt>true</tt> if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - var key; - for (key in this.fields) { - if (this.fields[key].checked) { - return true; - } - } - return false; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var args = []; - for (var key in this.fields) { - if(this.fields[key].checked){ - args.push({ - type: 'date', - comparison: this.compareMap[key], - value: this.getFieldValue(key).format(this.dateFormat) - }); - } - } - return args; - }, - - /** - * Get and return the date menu picker value - * @param {String} item The field identifier ('before', 'after', 'on') - * @return {Date} Gets the current selected value of the date field - */ - getFieldValue : function(item){ - return this.fields[item].menu.picker.getValue(); - }, - - /** - * Gets the menu picker associated with the passed field - * @param {String} item The field identifier ('before', 'after', 'on') - * @return {Object} The menu picker - */ - getPicker : function(item){ - return this.fields[item].menu.picker; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - var key, - pickerValue, - val = record.get(this.dataIndex); - - if(!Ext.isDate(val)){ - return false; - } - val = val.clearTime(true).getTime(); - - for (key in this.fields) { - if (this.fields[key].checked) { - pickerValue = this.getFieldValue(key).clearTime(true).getTime(); - if (key == 'before' && pickerValue <= val) { - return false; - } - if (key == 'after' && pickerValue >= val) { - return false; - } - if (key == 'on' && pickerValue != val) { - return false; - } - } - } - return true; - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/Filter.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/Filter.js deleted file mode 100644 index 79f7883bcb74..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/Filter.js +++ /dev/null @@ -1,185 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -Ext.namespace('Ext.ux.grid.filter'); - -/** - * @class Ext.ux.grid.filter.Filter - * @extends Ext.util.Observable - * Abstract base class for filter implementations. - */ -Ext.ux.grid.filter.Filter = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Boolean} active - * Indicates the initial status of the filter (defaults to false). - */ - active : false, - /** - * True if this filter is active. Use setActive() to alter after configuration. - * @type Boolean - * @property active - */ - /** - * @cfg {String} dataIndex - * The {@link Ext.data.Store} dataIndex of the field this filter represents. - * The dataIndex does not actually have to exist in the store. - */ - dataIndex : null, - /** - * The filter configuration menu that will be installed into the filter submenu of a column menu. - * @type Ext.menu.Menu - * @property - */ - menu : null, - /** - * @cfg {Number} updateBuffer - * Number of milliseconds to wait after user interaction to fire an update. Only supported - * by filters: 'list', 'numeric', and 'string'. Defaults to 500. - */ - updateBuffer : 500, - - constructor : function (config) { - Ext.apply(this, config); - - this.addEvents( - /** - * @event activate - * Fires when an inactive filter becomes active - * @param {Ext.ux.grid.filter.Filter} this - */ - 'activate', - /** - * @event deactivate - * Fires when an active filter becomes inactive - * @param {Ext.ux.grid.filter.Filter} this - */ - 'deactivate', - /** - * @event serialize - * Fires after the serialization process. Use this to attach additional parameters to serialization - * data before it is encoded and sent to the server. - * @param {Array/Object} data A map or collection of maps representing the current filter configuration. - * @param {Ext.ux.grid.filter.Filter} filter The filter being serialized. - */ - 'serialize', - /** - * @event update - * Fires when a filter configuration has changed - * @param {Ext.ux.grid.filter.Filter} this The filter object. - */ - 'update' - ); - Ext.ux.grid.filter.Filter.superclass.constructor.call(this); - - this.menu = new Ext.menu.Menu(); - this.init(config); - if(config && config.value){ - this.setValue(config.value); - this.setActive(config.active !== false, true); - delete config.value; - } - }, - - /** - * Destroys this filter by purging any event listeners, and removing any menus. - */ - destroy : function(){ - if (this.menu){ - this.menu.destroy(); - } - this.purgeListeners(); - }, - - /** - * Template method to be implemented by all subclasses that is to - * initialize the filter and install required menu items. - * Defaults to Ext.emptyFn. - */ - init : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * get and return the value of the filter. - * Defaults to Ext.emptyFn. - * @return {Object} The 'serialized' form of this filter - * @methodOf Ext.ux.grid.filter.Filter - */ - getValue : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * set the value of the filter and fire the 'update' event. - * Defaults to Ext.emptyFn. - * @param {Object} data The value to set the filter - * @methodOf Ext.ux.grid.filter.Filter - */ - setValue : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * return <tt>true</tt> if the filter has enough configuration information to be activated. - * Defaults to <tt>return true</tt>. - * @return {Boolean} - */ - isActivatable : function(){ - return true; - }, - - /** - * Template method to be implemented by all subclasses that is to - * get and return serialized filter data for transmission to the server. - * Defaults to Ext.emptyFn. - */ - getSerialArgs : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * validates the provided Ext.data.Record against the filters configuration. - * Defaults to <tt>return true</tt>. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function(){ - return true; - }, - - /** - * Returns the serialized filter data for transmission to the server - * and fires the 'serialize' event. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - * @methodOf Ext.ux.grid.filter.Filter - */ - serialize : function(){ - var args = this.getSerialArgs(); - this.fireEvent('serialize', args, this); - return args; - }, - - /** @private */ - fireUpdate : function(){ - if (this.active) { - this.fireEvent('update', this); - } - this.setActive(this.isActivatable()); - }, - - /** - * Sets the status of the filter and fires the appropriate events. - * @param {Boolean} active The new filter state. - * @param {Boolean} suppressEvent True to prevent events from being fired. - * @methodOf Ext.ux.grid.filter.Filter - */ - setActive : function(active, suppressEvent){ - if(this.active != active){ - this.active = active; - if (suppressEvent !== true) { - this.fireEvent(active ? 'activate' : 'deactivate', this); - } - } - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/ListFilter.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/ListFilter.js deleted file mode 100644 index 234e9e575126..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/ListFilter.js +++ /dev/null @@ -1,176 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * @class Ext.ux.grid.filter.ListFilter - * @extends Ext.ux.grid.filter.Filter - * <p>List filters are able to be preloaded/backed by an Ext.data.Store to load - * their options the first time they are shown. ListFilter utilizes the - * {@link Ext.ux.menu.ListMenu} component.</p> - * <p>Although not shown here, this class accepts all configuration options - * for {@link Ext.ux.menu.ListMenu}.</p> - * - * <p><b><u>Example Usage:</u></b></p> - * <pre><code> -var filters = new Ext.ux.grid.GridFilters({ - ... - filters: [{ - type: 'list', - dataIndex: 'size', - phpMode: true, - // options will be used as data to implicitly creates an ArrayStore - options: ['extra small', 'small', 'medium', 'large', 'extra large'] - }] -}); - * </code></pre> - * - */ -Ext.ux.grid.filter.ListFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - - /** - * @cfg {Array} options - * <p><code>data</code> to be used to implicitly create a data store - * to back this list when the data source is <b>local</b>. If the - * data for the list is remote, use the <code>{@link #store}</code> - * config instead.</p> - * <br><p>Each item within the provided array may be in one of the - * following formats:</p> - * <div class="mdetail-params"><ul> - * <li><b>Array</b> : - * <pre><code> -options: [ - [11, 'extra small'], - [18, 'small'], - [22, 'medium'], - [35, 'large'], - [44, 'extra large'] -] - * </code></pre> - * </li> - * <li><b>Object</b> : - * <pre><code> -labelField: 'name', // override default of 'text' -options: [ - {id: 11, name:'extra small'}, - {id: 18, name:'small'}, - {id: 22, name:'medium'}, - {id: 35, name:'large'}, - {id: 44, name:'extra large'} -] - * </code></pre> - * </li> - * <li><b>String</b> : - * <pre><code> - * options: ['extra small', 'small', 'medium', 'large', 'extra large'] - * </code></pre> - * </li> - */ - /** - * @cfg {Boolean} phpMode - * <p>Adjust the format of this filter. Defaults to false.</p> - * <br><p>When GridFilters <code>@cfg encode = false</code> (default):</p> - * <pre><code> -// phpMode == false (default): -filter[0][data][type] list -filter[0][data][value] value1 -filter[0][data][value] value2 -filter[0][field] prod - -// phpMode == true: -filter[0][data][type] list -filter[0][data][value] value1, value2 -filter[0][field] prod - * </code></pre> - * When GridFilters <code>@cfg encode = true</code>: - * <pre><code> -// phpMode == false (default): -filter : [{"type":"list","value":["small","medium"],"field":"size"}] - -// phpMode == true: -filter : [{"type":"list","value":"small,medium","field":"size"}] - * </code></pre> - */ - phpMode : false, - /** - * @cfg {Ext.data.Store} store - * The {@link Ext.data.Store} this list should use as its data source - * when the data source is <b>remote</b>. If the data for the list - * is local, use the <code>{@link #options}</code> config instead. - */ - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - * @param {Object} config - */ - init : function (config) { - this.dt = new Ext.util.DelayedTask(this.fireUpdate, this); - - // if a menu already existed, do clean up first - if (this.menu){ - this.menu.destroy(); - } - this.menu = new Ext.ux.menu.ListMenu(config); - this.menu.on('checkchange', this.onCheckChange, this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.menu.getSelected(); - }, - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.menu.setSelected(value); - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to return <tt>true</tt> if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - return this.getValue().length > 0; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var args = {type: 'list', value: this.phpMode ? this.getValue().join(',') : this.getValue()}; - return args; - }, - - /** @private */ - onCheckChange : function(){ - this.dt.delay(this.updateBuffer); - }, - - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - return this.getValue().indexOf(record.get(this.dataIndex)) > -1; - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/NumericFilter.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/NumericFilter.js deleted file mode 100644 index 181af8dca268..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/NumericFilter.js +++ /dev/null @@ -1,202 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * @class Ext.ux.grid.filter.NumericFilter - * @extends Ext.ux.grid.filter.Filter - * Filters using an Ext.ux.menu.RangeMenu. - * <p><b><u>Example Usage:</u></b></p> - * <pre><code> -var filters = new Ext.ux.grid.GridFilters({ - ... - filters: [{ - type: 'numeric', - dataIndex: 'price' - }] -}); - * </code></pre> - */ -Ext.ux.grid.filter.NumericFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - - /** - * @cfg {Object} fieldCls - * The Class to use to construct each field item within this menu - * Defaults to:<pre> - * fieldCls : Ext.form.NumberField - * </pre> - */ - fieldCls : Ext.form.NumberField, - /** - * @cfg {Object} fieldCfg - * The default configuration options for any field item unless superseded - * by the <code>{@link #fields}</code> configuration. - * Defaults to:<pre> - * fieldCfg : {} - * </pre> - * Example usage: - * <pre><code> -fieldCfg : { - width: 150, -}, - * </code></pre> - */ - /** - * @cfg {Object} fields - * The field items may be configured individually - * Defaults to <tt>undefined</tt>. - * Example usage: - * <pre><code> -fields : { - gt: { // override fieldCfg options - width: 200, - fieldCls: Ext.ux.form.CustomNumberField // to override default {@link #fieldCls} - } -}, - * </code></pre> - */ - /** - * @cfg {Object} iconCls - * The iconCls to be applied to each comparator field item. - * Defaults to:<pre> -iconCls : { - gt : 'ux-rangemenu-gt', - lt : 'ux-rangemenu-lt', - eq : 'ux-rangemenu-eq' -} - * </pre> - */ - iconCls : { - gt : 'ux-rangemenu-gt', - lt : 'ux-rangemenu-lt', - eq : 'ux-rangemenu-eq' - }, - - /** - * @cfg {Object} menuItemCfgs - * Default configuration options for each menu item - * Defaults to:<pre> -menuItemCfgs : { - emptyText: 'Enter Filter Text...', - selectOnFocus: true, - width: 125 -} - * </pre> - */ - menuItemCfgs : { - emptyText: 'Enter Filter Text...', - selectOnFocus: true, - width: 125 - }, - - /** - * @cfg {Array} menuItems - * The items to be shown in this menu. Items are added to the menu - * according to their position within this array. Defaults to:<pre> - * menuItems : ['lt','gt','-','eq'] - * </pre> - */ - menuItems : ['lt', 'gt', '-', 'eq'], - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - // if a menu already existed, do clean up first - if (this.menu){ - this.menu.destroy(); - } - this.menu = new Ext.ux.menu.RangeMenu(Ext.apply(config, { - // pass along filter configs to the menu - fieldCfg : this.fieldCfg || {}, - fieldCls : this.fieldCls, - fields : this.fields || {}, - iconCls: this.iconCls, - menuItemCfgs: this.menuItemCfgs, - menuItems: this.menuItems, - updateBuffer: this.updateBuffer - })); - // relay the event fired by the menu - this.menu.on('update', this.fireUpdate, this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.menu.getValue(); - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.menu.setValue(value); - }, - - /** - * @private - * Template method that is to return <tt>true</tt> if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - var values = this.getValue(); - for (key in values) { - if (values[key] !== undefined) { - return true; - } - } - return false; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var key, - args = [], - values = this.menu.getValue(); - for (key in values) { - args.push({ - type: 'numeric', - comparison: key, - value: values[key] - }); - } - return args; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - var val = record.get(this.dataIndex), - values = this.getValue(); - if (values.eq !== undefined && val != values.eq) { - return false; - } - if (values.lt !== undefined && val >= values.lt) { - return false; - } - if (values.gt !== undefined && val <= values.gt) { - return false; - } - return true; - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/StringFilter.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/StringFilter.js deleted file mode 100644 index d1af8e2edd20..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/filter/StringFilter.js +++ /dev/null @@ -1,132 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -/** - * @class Ext.ux.grid.filter.StringFilter - * @extends Ext.ux.grid.filter.Filter - * Filter by a configurable Ext.form.TextField - * <p><b><u>Example Usage:</u></b></p> - * <pre><code> -var filters = new Ext.ux.grid.GridFilters({ - ... - filters: [{ - // required configs - type: 'string', - dataIndex: 'name', - - // optional configs - value: 'foo', - active: true, // default is false - iconCls: 'ux-gridfilter-text-icon' // default - // any Ext.form.TextField configs accepted - }] -}); - * </code></pre> - */ -Ext.ux.grid.filter.StringFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - - /** - * @cfg {String} iconCls - * The iconCls to be applied to the menu item. - * Defaults to <tt>'ux-gridfilter-text-icon'</tt>. - */ - iconCls : 'ux-gridfilter-text-icon', - - emptyText: 'Enter Filter Text...', - selectOnFocus: true, - width: 125, - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - Ext.applyIf(config, { - enableKeyEvents: true, - iconCls: this.iconCls, - listeners: { - scope: this, - keyup: this.onInputKeyUp - } - }); - - this.inputItem = new Ext.form.TextField(config); - this.menu.add(this.inputItem); - this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.inputItem.getValue(); - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.inputItem.setValue(value); - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to return <tt>true</tt> if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - return this.inputItem.getValue().length > 0; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - return {type: 'string', value: this.getValue()}; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - var val = record.get(this.dataIndex); - - if(typeof val != 'string') { - return (this.getValue().length === 0); - } - - return val.toLowerCase().indexOf(this.getValue().toLowerCase()) > -1; - }, - - /** - * @private - * Handler method called when there is a keyup event on this.inputItem - */ - onInputKeyUp : function (field, e) { - var k = e.getKey(); - if (k == e.RETURN && field.isValid()) { - e.stopEvent(); - this.menu.hide(true); - return; - } - // restart the timer - this.updateTask.delay(this.updateBuffer); - } -}); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/equals.png b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/equals.png deleted file mode 100644 index 4769cb1583178d094a4385f475486de77f3e4099..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!D3?x-;bCrM;TYyi9>;M1%jg5^hLV6|wMHowh z{DK)Ap4~_Ta)dlx978ywlM@_RKiSXNuqly&!Q6+*xaot#5}+)Dr>mdKI;Vst0Hr1x AWB>pF diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/find.png b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/find.png deleted file mode 100644 index ccb710716d7b55ac0272b7dbaab86fbc08acc9a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 638 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47<~hLLR|m<{~sEgx?s)Disn8+ zVNo`A_6c)WPg%IWxqnXIlI;pgszCK^ljaxIwgW|gY#>N0sPqeskyTI@5fhh`kz-_J z1Pb~DMKQCmSlPRpSlX#->hSRky7>g_8Jh!5;O6Gx;N;TK)>l+fmynhPYEe?v0O|#* z$tbF+?VQ>=aW2q`f~wZFI}WYeaoEE@3}{GpY5lIlXHxSkwDpa1%bQkhIgnq`oKw~y zCLsm1u5b2=+TQtH)0cG5Se8{%H+jLj^up>z>vmb#I;G^67go0cU1eZm33Lh2QfXO* z>WQm(`S_P?*b|$U$I8mq)HhpHToUNE%9aV%j&3eq0YFd1XB0-{G}TUA7M<JNIDLbo zyRVv--n7LVb&brt0wYRVrWjk;0>dgixzNGQCn>umIyq;`@?D%<+`vEs`aLea;Mt^~ z6M>G?DhcunX1K-ih>4Nq;gerH%&Z=d{;{(Dd}^@u9n;|l%U7&8{O9V`sy{y&-|=6O zx_$qu%~YVSZJsWUAr*1GCk~gfI5MzaFyA70d*!Na$IX&&|N5Wmr(d*b?(RJ^{7Q}l zr1t$U=sd5$`agcpR1NjVTMwP)b~Jy`BeP*z$t>CU7(1`matB_omOl1Cxgd5=)xyt; z3TNJlT&meWb<>ic!rjcBDUTm|`Y$!!x%lwIGdc~Hex|3pBdo;E95P~96Y=WiPpxYe zj;nKT6eh2X$}P^G%_TMSM&Y4um!0=-E-hWO<?RIPs_obB-~RvS*SY70t?_Q!@+yng SUHcF80fVQjpUXO@geCwQQ2I*% diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/greater_than.png b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/greater_than.png deleted file mode 100644 index e2d4ae150d3c241f2c80273c09a092e311d3dcf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!S%6Q7>;M1%jY$U`53hlY6f6ny z3ubs_W2|@gZ^ySkK0n|6diw8+uH4h#KuKFq7sn8Z%X52o3NjdQ9By#<*Z+6A?^`ui ztx~Jm8Xo#hi%k3!TIO!_oa4Lkrk%meeRaxNlKHO;ZFYugo@4HQ%*dbM@oOv4AO=rY KKbLh*2~7Y63q;5O diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/less_than.png b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/less_than.png deleted file mode 100644 index 19a38fbb05d6ea291a349333de0617c227c58c60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!S%6Q7>;M1%jY$U`53hlY6f6ny z3udr+Wqeof@3)RGzyAGv`sbZbyQJb@prpO0i(`m{<lKYX85;})m>oRdyO`|%ziCRx zwekbIZkbz5TYR`BYUPgr%|mZ2ncto1F4$_qDl=6n=7rpkhZP%Mu=WWs^0S@k@&Fpe N;OXk;vd$@?2>|;3N7euU diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/sort_filtered_asc.gif b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/sort_filtered_asc.gif deleted file mode 100644 index 8e33691a0a7c72e72e063474971f28dc8736c1e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmZ?wbhEHb<Yi!G_{hNU^7Esf%_aZ;|7T!eQ2fsw;Txdfl30=mq?_$43KEmEQx&KP zxbT_91+}n5AvG_>R;gUc-i|@>CkrDx0~3P|(0L3XCp$3lPZ3?t^mbps3{9yl)w!z~ G7_0$AkSzxQ diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/sort_filtered_desc.gif b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/images/sort_filtered_desc.gif deleted file mode 100644 index ad58af86d85ad657184ed6e5e1b1fc1a71546a46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmZ?wbhEHb<Yi!G_{hNU^7Esf%_aZ;|7T!eQ2fsw;Txdfl30=mq?_$43KEmEQx&KP zxbT_91+}n5AvG_>R;gUc-i|@>CkrDx0~3P|(0L3XCp$3lyYw+0I{SK&Wq_LK?3L>n G7_0$GS}m;r diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/ListMenu.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/ListMenu.js deleted file mode 100644 index c5162a4d5014..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/ListMenu.js +++ /dev/null @@ -1,177 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -Ext.namespace('Ext.ux.menu'); - -/** - * @class Ext.ux.menu.ListMenu - * @extends Ext.menu.Menu - * This is a supporting class for {@link Ext.ux.grid.filter.ListFilter}. - * Although not listed as configuration options for this class, this class - * also accepts all configuration options from {@link Ext.ux.grid.filter.ListFilter}. - */ -Ext.ux.menu.ListMenu = Ext.extend(Ext.menu.Menu, { - /** - * @cfg {String} labelField - * Defaults to 'text'. - */ - labelField : 'text', - /** - * @cfg {String} paramPrefix - * Defaults to 'Loading...'. - */ - loadingText : 'Loading...', - /** - * @cfg {Boolean} loadOnShow - * Defaults to true. - */ - loadOnShow : true, - /** - * @cfg {Boolean} single - * Specify true to group all items in this list into a single-select - * radio button group. Defaults to false. - */ - single : false, - - constructor : function (cfg) { - this.selected = []; - this.addEvents( - /** - * @event checkchange - * Fires when there is a change in checked items from this list - * @param {Object} item Ext.menu.CheckItem - * @param {Object} checked The checked value that was set - */ - 'checkchange' - ); - - Ext.ux.menu.ListMenu.superclass.constructor.call(this, cfg = cfg || {}); - - if(!cfg.store && cfg.options){ - var options = []; - for(var i=0, len=cfg.options.length; i<len; i++){ - var value = cfg.options[i]; - switch(Ext.type(value)){ - case 'array': options.push(value); break; - case 'object': options.push([value.id, value[this.labelField]]); break; - case 'string': options.push([value, value]); break; - } - } - - this.store = new Ext.data.Store({ - reader: new Ext.data.ArrayReader({id: 0}, ['id', this.labelField]), - data: options, - listeners: { - 'load': this.onLoad, - scope: this - } - }); - this.loaded = true; - } else { - this.add({text: this.loadingText, iconCls: 'loading-indicator'}); - this.store.on('load', this.onLoad, this); - } - }, - - destroy : function () { - if (this.store) { - this.store.destroy(); - } - Ext.ux.menu.ListMenu.superclass.destroy.call(this); - }, - - /** - * Lists will initially show a 'loading' item while the data is retrieved from the store. - * In some cases the loaded data will result in a list that goes off the screen to the - * right (as placement calculations were done with the loading item). This adapter will - * allow show to be called with no arguments to show with the previous arguments and - * thus recalculate the width and potentially hang the menu from the left. - */ - show : function () { - var lastArgs = null; - return function(){ - if(arguments.length === 0){ - Ext.ux.menu.ListMenu.superclass.show.apply(this, lastArgs); - } else { - lastArgs = arguments; - if (this.loadOnShow && !this.loaded) { - this.store.load(); - } - Ext.ux.menu.ListMenu.superclass.show.apply(this, arguments); - } - }; - }(), - - /** @private */ - onLoad : function (store, records) { - var visible = this.isVisible(); - this.hide(false); - - this.removeAll(true); - - var gid = this.single ? Ext.id() : null; - for(var i=0, len=records.length; i<len; i++){ - var item = new Ext.menu.CheckItem({ - text: records[i].get(this.labelField), - group: gid, - checked: this.selected.indexOf(records[i].id) > -1, - hideOnClick: false}); - - item.itemId = records[i].id; - item.on('checkchange', this.checkChange, this); - - this.add(item); - } - - this.loaded = true; - - if (visible) { - this.show(); - } - this.fireEvent('load', this, records); - }, - - /** - * Get the selected items. - * @return {Array} selected - */ - getSelected : function () { - return this.selected; - }, - - /** @private */ - setSelected : function (value) { - value = this.selected = [].concat(value); - - if (this.loaded) { - this.items.each(function(item){ - item.setChecked(false, true); - for (var i = 0, len = value.length; i < len; i++) { - if (item.itemId == value[i]) { - item.setChecked(true, true); - } - } - }, this); - } - }, - - /** - * Handler for the 'checkchange' event from an check item in this menu - * @param {Object} item Ext.menu.CheckItem - * @param {Object} checked The checked value that was set - */ - checkChange : function (item, checked) { - var value = []; - this.items.each(function(item){ - if (item.checked) { - value.push(item.itemId); - } - },this); - this.selected = value; - - this.fireEvent('checkchange', item, checked); - } -}); \ No newline at end of file diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/RangeMenu.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/RangeMenu.js deleted file mode 100644 index 539281a92fdb..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/gridfilters/menu/RangeMenu.js +++ /dev/null @@ -1,128 +0,0 @@ -/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -Ext.ns('Ext.ux.menu'); - -/** - * @class Ext.ux.menu.RangeMenu - * @extends Ext.menu.Menu - * Custom implementation of Ext.menu.Menu that has preconfigured - * items for gt, lt, eq. - * <p><b><u>Example Usage:</u></b></p> - * <pre><code> - - * </code></pre> - */ -Ext.ux.menu.RangeMenu = Ext.extend(Ext.menu.Menu, { - - constructor : function (config) { - - Ext.ux.menu.RangeMenu.superclass.constructor.call(this, config); - - this.addEvents( - /** - * @event update - * Fires when a filter configuration has changed - * @param {Ext.ux.grid.filter.Filter} this The filter object. - */ - 'update' - ); - - this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this); - - var i, len, item, cfg, Cls; - - for (i = 0, len = this.menuItems.length; i < len; i++) { - item = this.menuItems[i]; - if (item !== '-') { - // defaults - cfg = { - itemId: 'range-' + item, - enableKeyEvents: true, - iconCls: this.iconCls[item] || 'no-icon', - listeners: { - scope: this, - keyup: this.onInputKeyUp - } - }; - Ext.apply( - cfg, - // custom configs - Ext.applyIf(this.fields[item] || {}, this.fieldCfg[item]), - // configurable defaults - this.menuItemCfgs - ); - Cls = cfg.fieldCls || this.fieldCls; - item = this.fields[item] = new Cls(cfg); - } - this.add(item); - } - }, - - /** - * @private - * called by this.updateTask - */ - fireUpdate : function () { - this.fireEvent('update', this); - }, - - /** - * Get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - var result = {}, key, field; - for (key in this.fields) { - field = this.fields[key]; - if (field.isValid() && String(field.getValue()).length > 0) { - result[key] = field.getValue(); - } - } - return result; - }, - - /** - * Set the value of this menu and fires the 'update' event. - * @param {Object} data The data to assign to this menu - */ - setValue : function (data) { - var key; - for (key in this.fields) { - this.fields[key].setValue(data[key] !== undefined ? data[key] : ''); - } - this.fireEvent('update', this); - }, - - /** - * @private - * Handler method called when there is a keyup event on an input - * item of this menu. - */ - onInputKeyUp : function (field, e) { - var k = e.getKey(); - if (k == e.RETURN && field.isValid()) { - e.stopEvent(); - this.hide(true); - return; - } - - if (field == this.fields.eq) { - if (this.fields.gt) { - this.fields.gt.setValue(null); - } - if (this.fields.lt) { - this.fields.lt.setValue(null); - } - } - else { - this.fields.eq.setValue(null); - } - - // restart the timer - this.updateTask.delay(this.updateBuffer); - } -}); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/helpers.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/helpers.js deleted file mode 100644 index 345049c172d0..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/helpers.js +++ /dev/null @@ -1,212 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -Ext.ns('TYPO3.Workspaces'); - -TYPO3.Workspaces.Helpers = { - /** - * Gets an form values object like {'element-1':on, 'element-2':on} and returns - * the checked results in an array like ['1', '2']. - * - * @param object values - * @param string elementPrefix - * @return array - */ - getElementIdsFromFormValues: function(values, elementPrefix) { - var results = []; - var pattern = new RegExp('^' + elementPrefix + '-' + '(.+)$'); - - Ext.iterate(values, function(key, value) { - if (value == 'on' && pattern.test(key)) { - results.push(RegExp.$1); - } - }); - - return results; - }, - - getSendToStageWindow: function(configuration) { - top.TYPO3.Windows.close('sendToStageWindow'); - return top.TYPO3.Windows.showWindow({ - id: 'sendToStageWindow', - title: configuration.title, - items: [ - { - xtype: 'form', - id: 'sendToStageForm', - width: '100%', - bodyStyle: 'padding: 5px 5px 3px 5px; border-width: 0; margin-bottom: 7px;', - items: configuration.items - } - ], - buttons: [ - { - text: TYPO3.l10n.localize('ok'), - handler: configuration.executeHandler - }, - { - text: TYPO3.l10n.localize('cancel'), - handler: function(event) { - top.TYPO3.Windows.close('sendToStageWindow'); - } - } - ] - }); - }, - - getElementsArrayOfSelection: function(selection) { - var elements = []; - - Ext.each(selection, function(item) { - var element = { - table: item.data.table, - t3ver_oid: item.data.t3ver_oid, - uid: item.data.uid - } - elements.push(element); - }); - - return elements; - }, - - getElementsArrayOfSelectionForIntegrityCheck: function(selection) { - var elements = []; - - Ext.each(selection, function(item) { - var element = { - table: item.data.table, - liveId: item.data.t3ver_oid, - versionId: item.data.uid - } - elements.push(element); - }); - - return elements; - }, - - getHistoryWindow: function(configuration) { - top.TYPO3.Windows.close('historyWindow'); - return top.TYPO3.Windows.showWindow({ - id: 'historyWindow', - title: 'Record History', - stateful: false, - modal: false, - - autoHeight: true, - boxMaxHeight: 500, - width: 700, - - buttons: [ - { - text: TYPO3.l10n.localize('ok'), - handler: function(event) { - top.TYPO3.Windows.close('historyWindow'); - } - } - ], - - items: [ - { - xtype: 'grid', - - border : false, - loadMask : true, - stripeRows: true, - autoHeight: true, - style: 'min-height: 100px', - - viewConfig: { - forceFit: true - }, - - store: { - xtype: 'directstore', - autoLoad: true, - autoDestroy: true, - reader: new Ext.data.JsonReader({ - idProperty : 'id', - root : 'data', - totalProperty : 'total', - fields: [ - { name: 'datetime' }, - { name: 'user' }, - { name: 'differences' } - ] - }), - proxy: new Ext.data.DirectProxy({ - directFn : TYPO3.Workspaces.ExtDirect.getHistory - }), - listeners: { - beforeload: function(store, options) { - store.setBaseParam('table', configuration.table); - store.setBaseParam('liveId', configuration.liveId); - store.setBaseParam('versionId', configuration.versionId); - } - } - }, - - colModel: new Ext.grid.ColumnModel({ - columns: [ - { width: 30, id: 'datetime', header: 'Date' }, - { width: 20, id: 'user', header: 'User', dataIndex: 'user' }, - { - id: 'differences', - header: 'Differences', - dataIndex: 'differences', - renderer: function(value, metaData, record, rowIndex, colIndex, store) { - if (typeof value === 'string') { - return value; - } else { - var template = new Ext.XTemplate( - '<tpl for=".">', - '<div style="display: block; padding: 3px 0;"><strong>{label}</strong> {html}</div>', - '</tpl>' - ); - return template.apply(value); - } - } - } - ] - }) - } - ] - }); - }, - - getAdditionalColumnHandler: function() { - var handlers = []; - Ext.each(TYPO3.settings.Workspaces.extension.AdditionalColumn.Handler, function(objectName) { - handlers.push(eval(objectName)); - }); - return handlers; - }, - - isDefined: function(propertyName) { - var result = true; - var pointer = window; - Ext.each(propertyName.split('.'), function(item) { - if (typeof pointer[item] === 'undefined') { - result = false; - return false; - } - pointer = pointer[item]; - }); - return result; - }, - - refreshPageTree: function() { - if (TYPO3.Workspaces.Helpers.isDefined('top.TYPO3.Backend.NavigationContainer.PageTree')) { - top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree(); - } - } -}; diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/preview.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/preview.js deleted file mode 100644 index 4af8b6401fcc..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/preview.js +++ /dev/null @@ -1,391 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -Ext.onReady(function() { - - Ext.state.Manager.setProvider(new TYPO3.state.ExtDirectProvider({ - key: 'moduleData.Workspaces.States', - autoRead: false - })); - - if (Ext.isObject(TYPO3.settings.Workspaces.States)) { - Ext.state.Manager.getProvider().initState(TYPO3.settings.Workspaces.States); - } - // late binding of ExtDirect - TYPO3.Workspaces.MainStore.proxy = new Ext.data.DirectProxy({ - directFn : TYPO3.Workspaces.ExtDirect.getWorkspaceInfos - }); - - var iconClsChecked = 't3-icon t3-icon-status t3-icon-status-status t3-icon-status-checked'; - var iconClsEmpty = 't3-icon t3-icon-empty t3-icon-empty-empty t3-icon-empty'; - var viewMode = 0; - var changePreviewMode = function(config, mode) { - var visual = Ext.getCmp('wsVisualWrap'); - if ((typeof mode != 'undefined') && (mode != viewMode)) { - viewMode = mode; - visual.remove(0); - visual.add(Ext.apply(config, {})); - }; - visual.doLayout(); - } - - var sliderSetup = { - layout: 'fit', - x: 0, y:0, - anchor: '100% 100%', - autoScroll: true, - items: [{ - layout: 'absolute', - id: 'visualPanel', - items: [{ - x: 0, y:0, - anchor: '100% 100%', - id: 'wsContainer', - layout: 'absolute', - autoScroll: false, - items:[{ - xtype: 'iframePanel', - x: 0, y:0, - id: 'wsPanel', - showLoadingIndicator: false, - src: wsUrl, - autoScroll: false - }] - },{ - x: 0, y:0, - anchor: '100% 0%', - id: 'liveContainer', - layout: 'absolute', - bodyStyle: 'height:0px;border-bottom: 2px solid red;', - autoScroll: false, - items:[{ - xtype: 'iframePanel', - x: 0, y:0, - id: 'livePanel', - showLoadingIndicator: false, - src: liveUrl, - autoScroll: false - }] - }] - }] - }; - var hboxSetup = { - layout: 'hbox', - x: 0, y:0, - anchor: '100% 100%', - layoutConfig: { - align : 'stretch', - pack : 'start' - }, - id: 'visualPanel-hbox', - items: [{ - xtype: 'iframePanel', - x: 0, y:0, - id: 'wsPanel-hbox', - showLoadingIndicator: false, - src: wsUrl, - autoScroll: false, - flex: 1 - },{ - xtype: 'iframePanel', - x: 0, y:0, - id: 'livePanel-hbox', - showLoadingIndicator: false, - src: liveUrl, - autoScroll: false, - flex: 1 - }] - }; - var vboxSetup = { - layout: 'vbox', - x: 0, y:0, - anchor: '100% 100%', - layoutConfig: { - align : 'stretch', - pack : 'start' - }, - id: 'visualPanel-vbox', - items: [{ - xtype: 'iframePanel', - x: 0, y:0, - id: 'wsPanel-vbox', - showLoadingIndicator: false, - src: wsUrl, - autoScroll: false, - flex: 1 - },{ - xtype: 'iframePanel', - x: 0, y:0, - id: 'livePanel-vbox', - showLoadingIndicator: false, - src: liveUrl, - autoScroll: false, - flex: 1 - }] - }; - - var viewport = new Ext.Viewport({ - layout: 'border', - items: [{ - xtype: 'tabpanel', - region: 'center', // a center region is ALWAYS required for border layout - id: 'preview', - activeTab: 0, - plugins : [{ - ptype : 'Ext.ux.plugins.TabStripContainer', - id: 'controls', - width: 930, - items: [ - { - xtype: 'panel', - width: 360, - items: [{ - xtype: 'panel', - id: 'slider', - layout: 'hbox', - items: [ - { - xtype: 'button', - id: 'sizeSliderButtonLive', - cls: 'sliderButton', - text: TYPO3.l10n.localize('livePreview'), - tooltip: TYPO3.l10n.localize('livePreviewDetail'), - width: 100, - listeners: { - click: { - fn: function () { - Ext.getCmp('sizeSlider').setValue(0); - } - } - } - }, - { - xtype: 'slider', - id: 'sizeSlider', - margins: '0 10 0 10', - maxValue: 100, - minValue: 0, - value: 100, - width: 150, - flex: 1, - listeners: { - change: { - fn: function resizeFromValue(slider, newValue, thumb) { - var height = Ext.getCmp('wsPanel').getHeight(); - Ext.getCmp('liveContainer').setHeight(height * (100 - newValue) / 100); - } - } - } - }, - { - xtype: 'button', - id: 'sizeSliderButtonWorkspace', - cls: 'sliderButton', - text: TYPO3.l10n.localize('workspacePreview'), - tooltip: TYPO3.l10n.localize('workspacePreviewDetail'), - width: 100, - listeners: { - click: { - fn: function () { - Ext.getCmp('sizeSlider').setValue(100); - } - } - } - } - ] - }] - }, { - xtype: 'buttongroup', - id: 'stageButtonGroup', - columns: 4, - width: 400, - items: [{ - text: TYPO3.l10n.localize('nextStage').substr(0, 35), - tooltip: TYPO3.l10n.localize('nextStage'), - xtype: 'button', - iconCls: 'x-btn-text', - id: 'feToolbarButtonNextStage', - hidden: TYPO3.settings.Workspaces.disableNextStageButton, - listeners: { - click: { - fn: function () { - TYPO3.Workspaces.Actions.sendPageToNextStage(); - } - } - } - }, { - text: TYPO3.l10n.localize('previousStage').substr(0, 35), - tooltip: TYPO3.l10n.localize('previousStage'), - xtype: 'button', - iconCls: 'x-btn-text', - id: 'feToolbarButtonPreviousStage', - hidden: TYPO3.settings.Workspaces.disablePreviousStageButton, - listeners: { - click: { - fn: function () { - TYPO3.Workspaces.Actions.sendPageToPrevStage(); - } - } - } - }, { - text: TYPO3.l10n.localize('discard'), - iconCls: 'x-btn-text', - xtype: 'button', - id: 'feToolbarButtonDiscardStage', - hidden: TYPO3.settings.Workspaces.disableDiscardStageButton, - listeners: { - click: { - fn: function () { - TYPO3.Workspaces.Actions.discardPage(); - } - } - } - }, { - xtype: 'button', - iconCls: 'x-btn-icon t3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-options-view', - id: 'visual-mode-options', - menu: { - id: 'visual-mode-selector', - stateful: true, - stateId: 'WorkspacePreviewModeSelect', - stateEvents: ['itemclick'], - items: [{ - text: TYPO3.l10n.localize('modeSlider'), - id: 'visual-mode-selector-slider', - checked: false, - group: 'mode', - hidden: TYPO3.settings.Workspaces.SplitPreviewModes.indexOf('slider') == -1, - checkHandler: modeChange - },{ - text: TYPO3.l10n.localize('modeVbox'), - id: 'visual-mode-selector-vbox', - checked: false, - group: 'mode', - hidden: TYPO3.settings.Workspaces.SplitPreviewModes.indexOf('vbox') == -1, - checkHandler: modeChange - - },{ - text: TYPO3.l10n.localize('modeHbox'), - id: 'visual-mode-selector-hbox', - checked: false, - group: 'mode', - hidden: TYPO3.settings.Workspaces.SplitPreviewModes.indexOf('hbox') == -1, - checkHandler: modeChange - }], - getState:function() { - return {viewMode: viewMode}; - }, - applyState: function(state) { - modeChange(null, true, viewMode); - } - } - }] - } - ] - }], - items: [{ - title: TYPO3.l10n.localize('visualPreview'), - id: 'wsVisual', - layout: 'fit', - anchor: '100% 100%', - listeners: { - activate: function () { - if (Ext.isObject(top.Ext.getCmp('slider'))) { - top.Ext.getCmp('slider').show(); - top.Ext.getCmp('visual-mode-options').show(); - TYPO3.Workspaces.ExtDirectActions.updateStageChangeButtons(TYPO3.settings.Workspaces.id, TYPO3.Workspaces.Actions.updateStageChangeButtons); - } - } - }, - items: [{ - id: 'wsVisualWrap', - layout: 'absolute', - anchor: '100% 100%', - x: 0, y:0, - items: [sliderSetup] - }] - },{ - title: TYPO3.l10n.localize('listView'), - id: 'wsSettings', - layout: 'fit', - listeners: { - activate: function () { - top.Ext.getCmp('slider').hide(); - top.Ext.getCmp('visual-mode-options').hide(); - top.Ext.getCmp('feToolbarButtonNextStage').hide(); - top.Ext.getCmp('feToolbarButtonPreviousStage').hide(); - top.Ext.getCmp('feToolbarButtonDiscardStage').hide(); - } - }, - items: [{ - xtype: 'iframePanel', - id: 'settingsPanel', - showLoadingIndicator: false, - src: wsSettingsUrl - }] - }] - }] - }); - - function modeChange(item, checked, mode) { - if (checked) { - var id , - ids = ['visual-mode-selector-slider', 'visual-mode-selector-hbox', 'visual-mode-selector-vbox'], - slider = Ext.getCmp('slider'), - itemSlider = Ext.getCmp('visual-mode-selector-slider'), - itemHbox = Ext.getCmp('visual-mode-selector-hbox'), - itemVbox = Ext.getCmp('visual-mode-selector-vbox'); - - if (item) { - mode = ids.indexOf(item.id); - } - - Ext.select('#visual-mode-selector ul li a img.t3-icon-status-checked').removeClass(iconClsChecked.split(" ")); - - var splitPreviewModes = TYPO3.settings.Workspaces.SplitPreviewModes; - if (splitPreviewModes.length == 1) { - Ext.getCmp('visual-mode-options').hide(); - } - - if (splitPreviewModes.indexOf('vbox') == -1 && mode === 2) { - mode = 0 - } - if (splitPreviewModes.indexOf('slider') == -1 && mode === 0) { - mode = 1 - } - if (splitPreviewModes.indexOf('hbox') == -1 && mode === 1) { - mode = 2 - } - - if (mode === 0) { - changePreviewMode(sliderSetup, mode); - slider.show(); - itemSlider.setIconClass(iconClsChecked); - itemHbox.setIconClass(iconClsEmpty); - itemVbox.setIconClass(iconClsEmpty); - } else if (mode === 1) { - changePreviewMode(vboxSetup, mode); - slider.hide(); - itemSlider.setIconClass(iconClsEmpty); - itemHbox.setIconClass(iconClsChecked); - itemVbox.setIconClass(iconClsEmpty); - } else if (mode === 2) { - changePreviewMode(hboxSetup, mode); - slider.hide(); - itemSlider.setIconClass(iconClsEmpty); - itemHbox.setIconClass(iconClsEmpty); - itemVbox.setIconClass(iconClsChecked); - } - } - } -}); diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar.js deleted file mode 100644 index a3387ee664b3..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/toolbar.js +++ /dev/null @@ -1,407 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -Ext.ns('TYPO3.Workspaces'); - -TYPO3.Workspaces.Toolbar = {}; - -TYPO3.Workspaces.Toolbar.search = new Ext.app.SearchField({ - store: TYPO3.Workspaces.MainStore, - trigger1Class : 't3-icon t3-icon-actions t3-icon-actions-input t3-icon-input-clear t3-tceforms-input-clearer', - trigger2Class : 't3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-tree-search-open', - width: 200 -}); - -TYPO3.Workspaces.Toolbar.selectActionStore = new Ext.data.DirectStore({ - storeId : 'stagesService', - root : 'data', - totalProperty : 'total', - idProperty : 'uid', - fields : [ - {name : 'uid'}, - {name : 'title'} - ], - listeners : { - load : function(store, records) { - if (records.length == 0) { - TYPO3.Workspaces.Toolbar.selectStateActionCombo.hide(); - } else { - TYPO3.Workspaces.Toolbar.selectStateActionCombo.show(); - TYPO3.Workspaces.WorkspaceGrid.colModel.setHidden(0, false); - } - } - } -}); - -TYPO3.Workspaces.Toolbar.selectStateActionCombo = new Ext.form.ComboBox({ - width: 150, - listWidth: 350, - lazyRender: true, - valueField: 'uid', - displayField: 'title', - mode: 'local', - emptyText: TYPO3.l10n.localize('chooseAction'), - selectOnFocus: true, - disabled : true, - hidden : true, // we hide it by default and show it in case there are any actions available - triggerAction: 'all', - editable: false, - forceSelection: true, - store: TYPO3.Workspaces.Toolbar.selectActionStore, - listeners: { - 'select' : function () { - var selection = TYPO3.Workspaces.WorkspaceGrid.getSelectionModel().getSelections(); - var nextStage = this.getValue(); - - // Use integrity check since "publish execute" stage is effective - if (nextStage == -20) { - var parameters = { - type: 'selection', - selection: TYPO3.Workspaces.Helpers.getElementsArrayOfSelectionForIntegrityCheck(selection) - }; - - TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() { - TYPO3.Workspaces.Actions.sendToSpecificStageWindow(selection, nextStage); - }); - } else { - TYPO3.Workspaces.Actions.sendToSpecificStageWindow(selection, nextStage); - } - } - } -}); - -TYPO3.Workspaces.Toolbar.selectMassActionStore = new Ext.data.DirectStore({ - storeId : 'stagesService', - root : 'data', - totalProperty : 'total', - idProperty : 'action', - fields : [ - {name : 'action'}, - {name : 'title'} - ], - listeners : { - load : function(store, records) { - if (records.length == 0 || TYPO3.settings.Workspaces.singleView === '1') { - TYPO3.Workspaces.Toolbar.selectionActionCombo.hide(); - TYPO3.Workspaces.Toolbar.selectStateMassActionCombo.hide(); - } else { - TYPO3.Workspaces.Toolbar.selectionActionCombo.show(); - TYPO3.Workspaces.Toolbar.selectStateMassActionCombo.show(); - } - } - } -}); - -TYPO3.Workspaces.Toolbar.selectionActionCombo = new Ext.form.ComboBox({ - width: 150, - lazyRender: true, - valueField: 'action', - displayField: 'title', - mode: 'local', - emptyText: 'choose selection action', - selectOnFocus: true, - triggerAction: 'all', - editable: false, - disabled : true, // disabled per default, enabled if selections are done in the grid - hidden : true, // hidden per default, shown if actions are available - forceSelection: true, - store: TYPO3.Workspaces.Toolbar.selectMassActionStore, - listeners: { - 'select' : function(combo, record) { - var label; - var checkIntegrity = false; - var selection = TYPO3.Workspaces.Helpers.getElementsArrayOfSelectionForIntegrityCheck( - TYPO3.Workspaces.WorkspaceGrid.getSelectionModel().getSelections() - ); - - switch (record.data.action) { - case 'publish': - label = TYPO3.l10n.localize('tooltip.publishSelected'); - checkIntegrity = true; - break; - case 'swap': - label = TYPO3.l10n.localize('tooltip.swapSelected'); - checkIntegrity = true; - break; - case 'discard': - label = TYPO3.l10n.localize('tooltip.discardSelected'); - break; - } - - top.TYPO3.Windows.close('executeSelectionActionWindow'); - - var configuration = { - id: 'executeSelectionActionWindow', - title: TYPO3.l10n.localize('window.selectionAction.title'), - items: [ - { - xtype: 'form', - id: 'executeSelectionActionForm', - width: '100%', - html: label, - bodyStyle: 'padding: 5px 5px 3px 5px; border-width: 0; margin-bottom: 7px;' - } - ], - buttons: [ - { - id: 'executeSelectionActionOkButton', - data: { action: record.data.action, selection: selection }, - scope: this, - text: TYPO3.l10n.localize('ok'), - disabled:false, - handler: function(event) { - top.Ext.getCmp('executeSelectionActionForm').update('Working...'); - top.Ext.getCmp('executeSelectionActionOkButton').disable(); - TYPO3.Workspaces.ExtDirectActions.executeSelectionAction(event.data, function(response) { - top.Ext.getCmp('executeSelectionActionOkButton').hide(); - top.Ext.getCmp('executeSelectionActionCancelButton').setText(TYPO3.lang.close); - if (response.error) { - top.Ext.getCmp('executeSelectionActionForm').update('<strong>' + TYPO3.l10n.localize('status.error') + ':</strong> ' + response.error); - } else { - top.Ext.getCmp('executeSelectionActionForm').update(TYPO3.l10n.localize('runMassAction.done').replace('%d', response.total)); - top.TYPO3.Backend.NavigationContainer.PageTree.refreshTree(); - } - }); - } - }, - { - id: 'executeSelectionActionCancelButton', - scope: this, - text: TYPO3.l10n.localize('cancel'), - handler: function() { - top.TYPO3.Windows.close('executeSelectionActionWindow'); - top.TYPO3.ModuleMenu.App.reloadFrames(); - } - } - ] - }; - - if (checkIntegrity) { - var parameters = { - type: 'selection', - selection: selection - }; - - TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() { - top.TYPO3.Windows.showWindow(configuration); - }); - } else { - top.TYPO3.Windows.showWindow(configuration); - } - } - } -}); - -TYPO3.Workspaces.Toolbar.selectStateMassActionCombo = new Ext.form.ComboBox({ - width: 150, - lazyRender: true, - valueField: 'action', - displayField: 'title', - mode: 'local', - emptyText: TYPO3.l10n.localize('chooseMassAction'), - selectOnFocus: true, - triggerAction: 'all', - editable: false, - hidden : true, // we hide it by default and show it in case there are any actions available - forceSelection: true, - store: TYPO3.Workspaces.Toolbar.selectMassActionStore, - listeners: { - 'select' : function (combo, record) { - var label = ''; - var affectWholeWorkspaceWarning = TYPO3.l10n.localize('tooltip.affectWholeWorkspace'); - var language = TYPO3.Workspaces.MainStore.baseParams.language; - var checkIntegrity = false; - - switch (record.data.action) { - case 'publish': - label = TYPO3.l10n.localize('tooltip.publishAll'); - checkIntegrity = true; - break; - case 'swap': - label = TYPO3.l10n.localize('tooltip.swapAll'); - checkIntegrity = true; - break; - case 'discard': - label = TYPO3.l10n.localize('tooltip.discardAll'); - break; - } - top.TYPO3.Windows.close('executeMassActionWindow'); - - var configuration = { - id: 'executeMassActionWindow', - title: TYPO3.l10n.localize('window.massAction.title'), - items: [ - { - xtype: 'form', - id: 'executeMassActionForm', - width: '100%', - html: label + '<br /><br />' + affectWholeWorkspaceWarning, - bodyStyle: 'padding: 5px 5px 3px 5px; border-width: 0; margin-bottom: 7px;' - }, - { - xtype: 'progress', - id: 'executeMassActionProgressBar', - autoWidth: true, - autoHeight: true, - hidden: true, - value: 0 - } - ], - buttons: [ - { - id: 'executeMassActionOkButton', - data: record.data, - scope: this, - text: TYPO3.l10n.localize('ok'), - disabled:false, - handler: function(event) { - TYPO3.Workspaces.Actions.triggerMassAction( - event.data.action, - language - ); - } - }, - { - id: 'executeMassActionCancelButton', - scope: this, - text: TYPO3.l10n.localize('cancel'), - handler: function() { - top.TYPO3.Windows.close('executeMassActionWindow'); - // if clicks during action - this also interrupts the running process -- not the nices way but efficient - top.TYPO3.ModuleMenu.App.reloadFrames(); - } - } - ] - }; - - if (checkIntegrity && language != 'all') { - var parameters = { - type: 'all', - language: language - }; - TYPO3.Workspaces.Actions.checkIntegrity(parameters, function() { - top.TYPO3.Windows.showWindow(configuration); - }); - } else { - top.TYPO3.Windows.showWindow(configuration); - } - } - } -}); - -TYPO3.Workspaces.Toolbar.Pager = new Ext.PagingToolbar({ - store : TYPO3.Workspaces.MainStore, - pageSize : 30, - displayInfo: false, - plugins : [ TYPO3.Workspaces.Configuration.GridFilters ] -}); - -/**************************************************** - * Depth menu - ****************************************************/ -TYPO3.Workspaces.Toolbar.depthFilter = new Ext.form.ComboBox({ - width: 150, - lazyRender: true, - valueField: 'depth', - displayField: 'label', - id: 'depthSelector', - mode: 'local', - emptyText: TYPO3.l10n.localize('depth'), - selectOnFocus: true, - triggerAction: 'all', - editable: false, - forceSelection: true, - hidden: (TYPO3.settings.Workspaces.singleView === '1'), - store: new Ext.data.SimpleStore({ - autoLoad: true, - fields: ['depth','label'], - data : [ - ['0', TYPO3.l10n.localize('depth_0')], - ['1', TYPO3.l10n.localize('depth_1')], - ['2', TYPO3.l10n.localize('depth_2')], - ['3', TYPO3.l10n.localize('depth_3')], - ['4', TYPO3.l10n.localize('depth_4')], - ['999', TYPO3.l10n.localize('depth_infi')] - ] - }), - value: 999, - listeners: { - 'select': { - fn: function(cmp, rec, index) { - var depth = rec.get('depth'); - TYPO3.Workspaces.MainStore.setBaseParam('depth', depth); - TYPO3.Workspaces.MainStore.load({ - params: { - wsId: 0 - } - }); - } - } - } -}); - -TYPO3.Workspaces.Toolbar.LanguageSelector = new Ext.form.ComboBox({ - width: 150, - listWidth: 350, - lazyRender: true, - valueField: 'uid', - displayField: 'title', - mode: 'local', - emptyText: TYPO3.l10n.localize('language.selectLanguage'), - selectOnFocus: true, - triggerAction: 'all', - editable: false, - forceSelection: true, - tpl: '<tpl for="."><div class="x-combo-list-item">{icon} {title}</div></tpl>', - store: new Ext.data.DirectStore({ - storeId: 'languages', - root: 'data', - totalProperty: 'total', - idProperty: 'uid', - fields: [ - {name : 'uid'}, - {name : 'title'}, - {name : 'icon'} - ], - listeners: { - load: function() { - TYPO3.Workspaces.Toolbar.LanguageSelector.setValue(TYPO3.settings.Workspaces.language); - } - } - }), - listeners: { - select: function (comboBox, record, index) { - TYPO3.Workspaces.ExtDirectActions.saveLanguageSelection(this.getValue()); - TYPO3.Workspaces.MainStore.setBaseParam('language', this.getValue()); - TYPO3.Workspaces.MainStore.load(); - } - } -}); - -TYPO3.Workspaces.Toolbar.FullTopToolbar = [ - TYPO3.Workspaces.Toolbar.depthFilter, - '-', - TYPO3.Workspaces.Toolbar.LanguageSelector, - {xtype: 'tbfill'}, - TYPO3.Workspaces.Toolbar.search -]; - -TYPO3.Workspaces.Toolbar.FullBottomBar = [ - (TYPO3.settings.Workspaces.isLiveWorkspace == true || TYPO3.settings.Workspaces.allView) ? {hidden: true} : TYPO3.Workspaces.Toolbar.selectStateActionCombo, - (TYPO3.settings.Workspaces.isLiveWorkspace == true || TYPO3.settings.Workspaces.allView) ? {hidden: true} : '-', - (TYPO3.settings.Workspaces.isLiveWorkspace == true || TYPO3.settings.Workspaces.allView) ? {hidden: true} : TYPO3.Workspaces.Toolbar.selectionActionCombo, - (TYPO3.settings.Workspaces.isLiveWorkspace == true || TYPO3.settings.Workspaces.allView) ? {hidden: true} : '-', - (TYPO3.settings.Workspaces.isLiveWorkspace == true || TYPO3.settings.Workspaces.allView) ? {hidden: true} : TYPO3.Workspaces.Toolbar.selectStateMassActionCombo, - {xtype: 'tbfill'}, - TYPO3.Workspaces.Toolbar.Pager -]; diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/workspaces.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/workspaces.js deleted file mode 100644 index 3f792ac839f6..000000000000 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/workspaces.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -Ext.ns('TYPO3.Workspaces'); - -TYPO3.Workspaces.App = { - - init : function() { - // With a large amount of unpublished changes, the workspace grid requires a longer Ajax timeout - Ext.Ajax.timeout = 120000; - - TYPO3.Workspaces.WorkspaceGrid.initColModel(); - TYPO3.Workspaces.WorkspaceGrid.render('workspacegrid'); - - TYPO3.Workspaces.MainStore.load(); - TYPO3.Workspaces.Toolbar.selectActionStore.load(); - TYPO3.Workspaces.Toolbar.selectMassActionStore.load(); - TYPO3.Workspaces.Toolbar.LanguageSelector.getStore().load(); - } -}; - -Ext.onReady(function() { - Ext.state.Manager.setProvider(new TYPO3.state.ExtDirectProvider({ - key: 'moduleData.Workspaces.States', - autoRead: false - })); - - if (Ext.isObject(TYPO3.settings.Workspaces.States)) { - Ext.state.Manager.getProvider().initState(TYPO3.settings.Workspaces.States); - } - - // Quicktips initialisieren - Ext.QuickTips.init(); - - // rearrange columns in grid - TYPO3.Workspaces.Actions.loadColModel(TYPO3.Workspaces.WorkspaceGrid); - - // late binding of ExtDirect - TYPO3.Workspaces.Toolbar.selectMassActionStore.proxy = new Ext.data.DirectProxy({ - directFn : TYPO3.Workspaces.ExtDirectMassActions.getMassStageActions - }); - // late binding of ExtDirect - TYPO3.Workspaces.Toolbar.selectActionStore.proxy = new Ext.data.DirectProxy({ - directFn : TYPO3.Workspaces.ExtDirect.getStageActions - }); - // late binding of ExtDirect - TYPO3.Workspaces.Toolbar.LanguageSelector.getStore().proxy = new Ext.data.DirectProxy({ - directFn : TYPO3.Workspaces.ExtDirect.getSystemLanguages - }); - - TYPO3.Workspaces.RowExpander.detailStore.proxy = new Ext.data.DirectProxy({ - directFn: TYPO3.Workspaces.ExtDirect.getRowDetails - }); - // late binding of ExtDirect - TYPO3.Workspaces.MainStore.proxy = new Ext.data.DirectProxy({ - directFn : TYPO3.Workspaces.ExtDirect.getWorkspaceInfos - }); - - // Workspace Tabs are not used in - // frontend list preview - if (Ext.get('workspacetabs')) { - TYPO3.Workspaces.Tabs = new Ext.Panel({ - renderTo: 'workspacetabs', - autoWidth: true, - layout: 'fit', - items: [ - { - xtype: 'WorkspacesTabPanel', - unstyled: true, - items: TYPO3.settings.Workspaces.workspaceTabs, - activeTab: 'workspace-' + TYPO3.settings.Workspaces.activeWorkspaceId - } - ] - }); - } - - // fire grid - var WS = new TYPO3.Workspaces.App.init(); - -}); diff --git a/typo3/sysext/workspaces/Tests/Functional/ActionHandler/ActionHandlerTest.php b/typo3/sysext/workspaces/Tests/Functional/ActionHandler/ActionHandlerTest.php index f5dc344ed54a..ddd3d0cd8add 100644 --- a/typo3/sysext/workspaces/Tests/Functional/ActionHandler/ActionHandlerTest.php +++ b/typo3/sysext/workspaces/Tests/Functional/ActionHandler/ActionHandlerTest.php @@ -49,7 +49,7 @@ class ActionHandlerTest extends \TYPO3\CMS\Core\Tests\FunctionalTestCase // Prepare parameter $parameter = new \stdClass(); $parameter->additional = ''; - $parameter->receipients = array(); + $parameter->recipients = array(); $parameter->comments = ''; // Send to LIVE -- GitLab