Full Site Editing - Version 3.36079

Version Description

Download this release

Release Info

Developer antoniosejas
Plugin Icon wp plugin Full Site Editing
Version 3.36079
Comparing to
See all releases

Code changes from version 3.35918 to 3.36079

build_meta.txt CHANGED
@@ -1,3 +1,3 @@
1
- commit_hash=17fdaf0f019a758a6ba9a3e004bd0e3dbbf7bc8a
2
- commit_url=https://github.com/Automattic/wp-calypso/commit/17fdaf0f019a758a6ba9a3e004bd0e3dbbf7bc8a
3
- build_number=3.35918
1
+ commit_hash=71848faa7a136cbb435e3a6e360ab9e003d97f5a
2
+ commit_url=https://github.com/Automattic/wp-calypso/commit/71848faa7a136cbb435e3a6e360ab9e003d97f5a
3
+ build_number=3.36079
full-site-editing-plugin.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Plugin Name: WordPress.com Editing Toolkit
4
  * Description: Enhances your page creation workflow within the Block Editor.
5
- * Version: 3.35918
6
  * Author: Automattic
7
  * Author URI: https://automattic.com/wordpress-plugins/
8
  * License: GPLv2 or later
@@ -42,7 +42,7 @@ namespace A8C\FSE;
42
  *
43
  * @var string
44
  */
45
- define( 'A8C_ETK_PLUGIN_VERSION', '3.35918' );
46
 
47
  // Always include these helper files for dotcom FSE.
48
  require_once __DIR__ . '/dotcom-fse/helpers.php';
@@ -428,3 +428,11 @@ function load_block_description_links() {
428
  require_once __DIR__ . '/wpcom-block-description-links/class-wpcom-block-description-links.php';
429
  }
430
  add_action( 'plugins_loaded', __NAMESPACE__ . '\load_block_description_links' );
 
 
 
 
 
 
 
 
2
  /**
3
  * Plugin Name: WordPress.com Editing Toolkit
4
  * Description: Enhances your page creation workflow within the Block Editor.
5
+ * Version: 3.36079
6
  * Author: Automattic
7
  * Author URI: https://automattic.com/wordpress-plugins/
8
  * License: GPLv2 or later
42
  *
43
  * @var string
44
  */
45
+ define( 'A8C_ETK_PLUGIN_VERSION', '3.36079' );
46
 
47
  // Always include these helper files for dotcom FSE.
48
  require_once __DIR__ . '/dotcom-fse/helpers.php';
428
  require_once __DIR__ . '/wpcom-block-description-links/class-wpcom-block-description-links.php';
429
  }
430
  add_action( 'plugins_loaded', __NAMESPACE__ . '\load_block_description_links' );
431
+
432
+ /**
433
+ * Load Tutorials API
434
+ */
435
+ function load_tutorials() {
436
+ require_once __DIR__ . '/tutorials/tutorials.php';
437
+ }
438
+ add_action( 'plugins_loaded', __NAMESPACE__ . '\load_tutorials' );
jetpack-timeline/blocks/src/style.scss CHANGED
@@ -78,6 +78,7 @@ $timeline-border-width: 4px;
78
  // Bubbles.
79
  .wp-block-jetpack-timeline-item {
80
  width: calc( 50% - #{ $timeline-width } + #{ $timeline-border-width * 0.5 } );
 
81
  }
82
 
83
  // Left aligned.
78
  // Bubbles.
79
  .wp-block-jetpack-timeline-item {
80
  width: calc( 50% - #{ $timeline-width } + #{ $timeline-border-width * 0.5 } );
81
+ box-sizing: border-box;
82
  }
83
 
84
  // Left aligned.
jetpack-timeline/dist/jetpack-timeline.asset.php CHANGED
@@ -1 +1 @@
1
- <?php return array('dependencies' => array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives'), 'version' => '985418f62d161af1a9b6');
1
+ <?php return array('dependencies' => array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives'), 'version' => 'f0d32f10451f52017160');
jetpack-timeline/dist/jetpack-timeline.css CHANGED
@@ -1,2 +1,2 @@
1
  [data-type="jetpack/timeline"] .block-list-appender{display:none}[data-type="jetpack/timeline"].is-selected .block-list-appender,[data-type="jetpack/timeline"].has-child-selected .block-list-appender{display:block}.components-button.timeline-item-appender{background:#1e1e1e;border-radius:2px;color:#fff;min-width:24px;height:24px;margin-right:12px;padding-left:0;padding-right:8px;animation:none !important}.components-button.timeline-item-appender:hover{color:#fff}
2
- .wp-block-jetpack-timeline.wp-block-jetpack-timeline{padding:0}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item{list-style-type:none;position:relative;padding:1em 2em;margin-bottom:36px;margin-left:36px}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item__bubble{display:block;width:36px;height:4px;background-color:currentColor;position:absolute;top:50%;transform:translateY(-2px);left:-36px}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item::after{content:"";display:block;background:currentColor;position:absolute;left:-36px;top:-36px;bottom:-36px;width:4px}.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:first-child .timeline-item::after,.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:first-child .timeline-item::after{top:50%}.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:nth-last-child(2) .timeline-item::after,.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:last-child .timeline-item::after{bottom:50%}@media only screen and (min-width: 640px){ul.wp-block-jetpack-timeline.is-alternating{display:flex;flex-direction:column}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item{width:calc( 50% - 36px + 2px )}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right),ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right){margin-left:0;margin-right:auto}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item__bubble{left:auto;right:-36px}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item::after,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item::after,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item::after{left:auto;right:-36px}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-right.is-right.is-right,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(even) .wp-block-jetpack-timeline-item:not(.is-left),ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item:nth-child(even):not(.is-left){margin-left:auto;margin-right:0}}
1
  [data-type="jetpack/timeline"] .block-list-appender{display:none}[data-type="jetpack/timeline"].is-selected .block-list-appender,[data-type="jetpack/timeline"].has-child-selected .block-list-appender{display:block}.components-button.timeline-item-appender{background:#1e1e1e;border-radius:2px;color:#fff;min-width:24px;height:24px;margin-right:12px;padding-left:0;padding-right:8px;animation:none !important}.components-button.timeline-item-appender:hover{color:#fff}
2
+ .wp-block-jetpack-timeline.wp-block-jetpack-timeline{padding:0}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item{list-style-type:none;position:relative;padding:1em 2em;margin-bottom:36px;margin-left:36px}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item__bubble{display:block;width:36px;height:4px;background-color:currentColor;position:absolute;top:50%;transform:translateY(-2px);left:-36px}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item::after{content:"";display:block;background:currentColor;position:absolute;left:-36px;top:-36px;bottom:-36px;width:4px}.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:first-child .timeline-item::after,.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:first-child .timeline-item::after{top:50%}.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:nth-last-child(2) .timeline-item::after,.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:last-child .timeline-item::after{bottom:50%}@media only screen and (min-width: 640px){ul.wp-block-jetpack-timeline.is-alternating{display:flex;flex-direction:column}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item{width:calc( 50% - 36px + 2px );box-sizing:border-box}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right),ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right){margin-left:0;margin-right:auto}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item__bubble{left:auto;right:-36px}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item::after,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item::after,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item::after{left:auto;right:-36px}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-right.is-right.is-right,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(even) .wp-block-jetpack-timeline-item:not(.is-left),ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item:nth-child(even):not(.is-left){margin-left:auto;margin-right:0}}
jetpack-timeline/dist/jetpack-timeline.css.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jetpack-timeline.css","mappings":"AAIC,oDACC,aAGD,uIAEC,cAMF,0CAWC,yBAVA,mBACA,kBACA,WAEA,YACA,kBAFA,eAGA,eACA,iBAGA,CAEA,gDACC,WCjBD,qDACC,UAGD,6DACC,qBAKA,kBAlBe,CAqBf,iBANA,gBADA,iBAde,CAwBf,oFAIC,8BAHA,cAEA,UAzBqB,CA8BrB,WAHA,kBACA,QACA,2BALA,UAMA,CAID,kFAGC,wBAIA,aANA,WACA,cAGA,WADA,kBAEA,UAEA,SA1CqB,CA+CvB,8LAEC,QAGD,mMAEC,WAWF,yCACC,4CACC,aACA,sBAGA,4EACC,uBAID,4VAGC,cACA,kBASA,4zBACC,UACA,YAKF,2VAGC,iBACA","sources":["webpack://EditingToolkit/./editing-toolkit-plugin/jetpack-timeline/blocks/src/editor.scss","webpack://EditingToolkit/./editing-toolkit-plugin/jetpack-timeline/blocks/src/style.scss"],"sourcesContent":["// Editor-only styles.\n[data-type='jetpack/timeline'] {\n\n\t// Always show the Timeline appender, even when a child block is selected.\n\t.block-list-appender {\n\t\tdisplay: none;\n\t}\n\n\t&.is-selected .block-list-appender,\n\t&.has-child-selected .block-list-appender {\n\t\tdisplay: block;\n\t}\n}\n\n// We replicate the appender look here.\n// @todo: once https://github.com/WordPress/gutenberg/issues/16659 we should replace the button with the actual appender.\n.components-button.timeline-item-appender {\n\tbackground: #1e1e1e;\n\tborder-radius: 2px;\n\tcolor: #fff;\n\tmin-width: 24px;\n\theight: 24px;\n\tmargin-right: 12px;\n\tpadding-left: 0;\n\tpadding-right: 8px;\n\n\t// Temporarily make this important. It can be retired once this custom appender is replaced with the real one.\n\tanimation: none !important;\n\n\t&:hover {\n\t\tcolor: #fff;\n\t}\n}\n","/**\n * Timeline Block styles.\n */\n\n// Variables.\n$timeline-width: 36px;\n$timeline-gutter: 24px;\n$timeline-border-width: 4px;\n\n// Styles shared between editor and frontend.\n.wp-block-jetpack-timeline {\n\n\t// This padding needs extra specificity.\n\t&.wp-block-jetpack-timeline {\n\t\tpadding: 0;\n\t}\n\n\tli.wp-block-jetpack-timeline-item {\n\t\tlist-style-type: none;\n\t\tposition: relative;\n\t\tpadding: 1em 2em;\n\n\t\t// Make the spacing between items consistent so we can connect the timeline\n\t\tmargin-bottom: $timeline-width;\n\n\t\t// Make room for the timeline.\n\t\tmargin-left: $timeline-width;\n\n\t\t// Draw the line connecting to the timeline.\n\t\t.timeline-item__bubble {\n\t\t\tdisplay: block;\n\t\t\twidth: $timeline-width;\n\t\t\theight: $timeline-border-width;\n\t\t\tbackground-color: currentColor;\n\t\t\tposition: absolute;\n\t\t\ttop: 50%;\n\t\t\ttransform: translateY( -( $timeline-border-width * 0.5 ) );\n\t\t\tleft: -$timeline-width;\n\t\t}\n\n\t\t// Draw the vertical timeline line.\n\t\t.timeline-item::after {\n\t\t\tcontent: '';\n\t\t\tdisplay: block;\n\t\t\tbackground: currentColor;\n\t\t\tposition: absolute;\n\t\t\tleft: -$timeline-width;\n\t\t\ttop: -$timeline-width;\n\t\t\tbottom: -$timeline-width;\n\t\t\twidth: $timeline-border-width;\n\t\t}\n\t}\n\n\t// Add special timeline starting point and end point.\n\t[data-type='jetpack/timeline-item']:first-child .timeline-item::after, // Editor\n\t& > li.wp-block-jetpack-timeline-item:first-child .timeline-item::after { // Frontend\n\t\ttop: 50%;\n\t}\n\n\t[data-type='jetpack/timeline-item']:nth-last-child( 2 ) .timeline-item::after, // Editor, is the 2nd last child here\n\t& > li.wp-block-jetpack-timeline-item:last-child .timeline-item::after { // Frontend\n\t\tbottom: 50%;\n\t}\n\n}\n\n\n/**\n * Alternating Bubbles\n */\n\n\n@media only screen and ( min-width: 640px ) {\n\tul.wp-block-jetpack-timeline.is-alternating {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\n\t\t// Bubbles.\n\t\t.wp-block-jetpack-timeline-item {\n\t\t\twidth: calc( 50% - #{ $timeline-width } + #{ $timeline-border-width * 0.5 } );\n\t\t}\n\n\t\t// Left aligned.\n\t\t.wp-block-jetpack-timeline-item.is-left.is-left.is-left, // Both, explicitly set. Needs specificity.\n\t\t[data-type='jetpack/timeline-item']:nth-child( odd ) .wp-block-jetpack-timeline-item:not( .is-right ), // Editor\n\t\t& > .wp-block-jetpack-timeline-item:nth-child( odd ):not( .is-right ) { // Frontend\n\t\t\tmargin-left: 0;\n\t\t\tmargin-right: auto;\n\n\t\t\t// Adjust the the line connecting to the timeline.\n\t\t\t.timeline-item__bubble {\n\t\t\t\tleft: auto;\n\t\t\t\tright: -$timeline-width;\n\t\t\t}\n\n\t\t\t// Adjust the vertical line.\n\t\t\t.timeline-item::after {\n\t\t\t\tleft: auto;\n\t\t\t\tright: -$timeline-width;\n\t\t\t}\n\t\t}\n\n\t\t// Right aligned.\n\t\t.wp-block-jetpack-timeline-item.is-right.is-right.is-right, // Both, explicitly set. Needs specificity.\n\t\t[data-type='jetpack/timeline-item']:nth-child( even ) .wp-block-jetpack-timeline-item:not( .is-left ), // Editor\n\t\t.wp-block-jetpack-timeline-item:nth-child( even ):not( .is-left ) { // Frontend\n\t\t\tmargin-left: auto;\n\t\t\tmargin-right: 0;\n\t\t}\n\t}\n}\n"],"names":[],"sourceRoot":""}
1
+ {"version":3,"file":"jetpack-timeline.css","mappings":"AAIC,oDACC,aAGD,uIAEC,cAMF,0CAWC,yBAVA,mBACA,kBACA,WAEA,YACA,kBAFA,eAGA,eACA,iBAGA,CAEA,gDACC,WCjBD,qDACC,UAGD,6DACC,qBAKA,kBAlBe,CAqBf,iBANA,gBADA,iBAde,CAwBf,oFAIC,8BAHA,cAEA,UAzBqB,CA8BrB,WAHA,kBACA,QACA,2BALA,UAMA,CAID,kFAGC,wBAIA,aANA,WACA,cAGA,WADA,kBAEA,UAEA,SA1CqB,CA+CvB,8LAEC,QAGD,mMAEC,WAWF,yCACC,4CACC,aACA,sBAGA,4EAEC,sBADA,sBACA,CAID,4VAGC,cACA,kBASA,4zBACC,UACA,YAKF,2VAGC,iBACA","sources":["webpack://EditingToolkit/./editing-toolkit-plugin/jetpack-timeline/blocks/src/editor.scss","webpack://EditingToolkit/./editing-toolkit-plugin/jetpack-timeline/blocks/src/style.scss"],"sourcesContent":["// Editor-only styles.\n[data-type='jetpack/timeline'] {\n\n\t// Always show the Timeline appender, even when a child block is selected.\n\t.block-list-appender {\n\t\tdisplay: none;\n\t}\n\n\t&.is-selected .block-list-appender,\n\t&.has-child-selected .block-list-appender {\n\t\tdisplay: block;\n\t}\n}\n\n// We replicate the appender look here.\n// @todo: once https://github.com/WordPress/gutenberg/issues/16659 we should replace the button with the actual appender.\n.components-button.timeline-item-appender {\n\tbackground: #1e1e1e;\n\tborder-radius: 2px;\n\tcolor: #fff;\n\tmin-width: 24px;\n\theight: 24px;\n\tmargin-right: 12px;\n\tpadding-left: 0;\n\tpadding-right: 8px;\n\n\t// Temporarily make this important. It can be retired once this custom appender is replaced with the real one.\n\tanimation: none !important;\n\n\t&:hover {\n\t\tcolor: #fff;\n\t}\n}\n","/**\n * Timeline Block styles.\n */\n\n// Variables.\n$timeline-width: 36px;\n$timeline-gutter: 24px;\n$timeline-border-width: 4px;\n\n// Styles shared between editor and frontend.\n.wp-block-jetpack-timeline {\n\n\t// This padding needs extra specificity.\n\t&.wp-block-jetpack-timeline {\n\t\tpadding: 0;\n\t}\n\n\tli.wp-block-jetpack-timeline-item {\n\t\tlist-style-type: none;\n\t\tposition: relative;\n\t\tpadding: 1em 2em;\n\n\t\t// Make the spacing between items consistent so we can connect the timeline\n\t\tmargin-bottom: $timeline-width;\n\n\t\t// Make room for the timeline.\n\t\tmargin-left: $timeline-width;\n\n\t\t// Draw the line connecting to the timeline.\n\t\t.timeline-item__bubble {\n\t\t\tdisplay: block;\n\t\t\twidth: $timeline-width;\n\t\t\theight: $timeline-border-width;\n\t\t\tbackground-color: currentColor;\n\t\t\tposition: absolute;\n\t\t\ttop: 50%;\n\t\t\ttransform: translateY( -( $timeline-border-width * 0.5 ) );\n\t\t\tleft: -$timeline-width;\n\t\t}\n\n\t\t// Draw the vertical timeline line.\n\t\t.timeline-item::after {\n\t\t\tcontent: '';\n\t\t\tdisplay: block;\n\t\t\tbackground: currentColor;\n\t\t\tposition: absolute;\n\t\t\tleft: -$timeline-width;\n\t\t\ttop: -$timeline-width;\n\t\t\tbottom: -$timeline-width;\n\t\t\twidth: $timeline-border-width;\n\t\t}\n\t}\n\n\t// Add special timeline starting point and end point.\n\t[data-type='jetpack/timeline-item']:first-child .timeline-item::after, // Editor\n\t& > li.wp-block-jetpack-timeline-item:first-child .timeline-item::after { // Frontend\n\t\ttop: 50%;\n\t}\n\n\t[data-type='jetpack/timeline-item']:nth-last-child( 2 ) .timeline-item::after, // Editor, is the 2nd last child here\n\t& > li.wp-block-jetpack-timeline-item:last-child .timeline-item::after { // Frontend\n\t\tbottom: 50%;\n\t}\n\n}\n\n\n/**\n * Alternating Bubbles\n */\n\n\n@media only screen and ( min-width: 640px ) {\n\tul.wp-block-jetpack-timeline.is-alternating {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\n\t\t// Bubbles.\n\t\t.wp-block-jetpack-timeline-item {\n\t\t\twidth: calc( 50% - #{ $timeline-width } + #{ $timeline-border-width * 0.5 } );\n\t\t\tbox-sizing: border-box;\n\t\t}\n\n\t\t// Left aligned.\n\t\t.wp-block-jetpack-timeline-item.is-left.is-left.is-left, // Both, explicitly set. Needs specificity.\n\t\t[data-type='jetpack/timeline-item']:nth-child( odd ) .wp-block-jetpack-timeline-item:not( .is-right ), // Editor\n\t\t& > .wp-block-jetpack-timeline-item:nth-child( odd ):not( .is-right ) { // Frontend\n\t\t\tmargin-left: 0;\n\t\t\tmargin-right: auto;\n\n\t\t\t// Adjust the the line connecting to the timeline.\n\t\t\t.timeline-item__bubble {\n\t\t\t\tleft: auto;\n\t\t\t\tright: -$timeline-width;\n\t\t\t}\n\n\t\t\t// Adjust the vertical line.\n\t\t\t.timeline-item::after {\n\t\t\t\tleft: auto;\n\t\t\t\tright: -$timeline-width;\n\t\t\t}\n\t\t}\n\n\t\t// Right aligned.\n\t\t.wp-block-jetpack-timeline-item.is-right.is-right.is-right, // Both, explicitly set. Needs specificity.\n\t\t[data-type='jetpack/timeline-item']:nth-child( even ) .wp-block-jetpack-timeline-item:not( .is-left ), // Editor\n\t\t.wp-block-jetpack-timeline-item:nth-child( even ):not( .is-left ) { // Frontend\n\t\t\tmargin-left: auto;\n\t\t\tmargin-right: 0;\n\t\t}\n\t}\n}\n"],"names":[],"sourceRoot":""}
jetpack-timeline/dist/jetpack-timeline.rtl.css CHANGED
@@ -1 +1 @@
1
- [data-type="jetpack/timeline"] .block-list-appender{display:none}[data-type="jetpack/timeline"].has-child-selected .block-list-appender,[data-type="jetpack/timeline"].is-selected .block-list-appender{display:block}.components-button.timeline-item-appender{animation:none!important;background:#1e1e1e;border-radius:2px;color:#fff;height:24px;margin-left:12px;min-width:24px;padding-left:8px;padding-right:0}.components-button.timeline-item-appender:hover{color:#fff}.wp-block-jetpack-timeline.wp-block-jetpack-timeline{padding:0}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item{list-style-type:none;margin-bottom:36px;margin-right:36px;padding:1em 2em;position:relative}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item__bubble{background-color:currentColor;display:block;height:4px;position:absolute;right:-36px;top:50%;transform:translateY(-2px);width:36px}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item:after{background:currentColor;bottom:-36px;content:"";display:block;position:absolute;right:-36px;top:-36px;width:4px}.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:first-child .timeline-item:after,.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:first-child .timeline-item:after{top:50%}.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:last-child .timeline-item:after,.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:nth-last-child(2) .timeline-item:after{bottom:50%}@media only screen and (min-width:640px){ul.wp-block-jetpack-timeline.is-alternating{display:flex;flex-direction:column}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item{width:calc(50% - 34px)}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right),ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right){margin-left:auto;margin-right:0}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item:after,ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item:after,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item:after,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item__bubble{left:-36px;right:auto}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-right.is-right.is-right,ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item:nth-child(2n):not(.is-left),ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(2n) .wp-block-jetpack-timeline-item:not(.is-left){margin-left:0;margin-right:auto}}
1
+ [data-type="jetpack/timeline"] .block-list-appender{display:none}[data-type="jetpack/timeline"].has-child-selected .block-list-appender,[data-type="jetpack/timeline"].is-selected .block-list-appender{display:block}.components-button.timeline-item-appender{animation:none!important;background:#1e1e1e;border-radius:2px;color:#fff;height:24px;margin-left:12px;min-width:24px;padding-left:8px;padding-right:0}.components-button.timeline-item-appender:hover{color:#fff}.wp-block-jetpack-timeline.wp-block-jetpack-timeline{padding:0}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item{list-style-type:none;margin-bottom:36px;margin-right:36px;padding:1em 2em;position:relative}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item__bubble{background-color:currentColor;display:block;height:4px;position:absolute;right:-36px;top:50%;transform:translateY(-2px);width:36px}.wp-block-jetpack-timeline li.wp-block-jetpack-timeline-item .timeline-item:after{background:currentColor;bottom:-36px;content:"";display:block;position:absolute;right:-36px;top:-36px;width:4px}.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:first-child .timeline-item:after,.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:first-child .timeline-item:after{top:50%}.wp-block-jetpack-timeline>li.wp-block-jetpack-timeline-item:last-child .timeline-item:after,.wp-block-jetpack-timeline [data-type="jetpack/timeline-item"]:nth-last-child(2) .timeline-item:after{bottom:50%}@media only screen and (min-width:640px){ul.wp-block-jetpack-timeline.is-alternating{display:flex;flex-direction:column}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item{box-sizing:border-box;width:calc(50% - 34px)}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right),ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right){margin-left:auto;margin-right:0}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item:after,ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-left.is-left.is-left .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item:after,ul.wp-block-jetpack-timeline.is-alternating>.wp-block-jetpack-timeline-item:nth-child(odd):not(.is-right) .timeline-item__bubble,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item:after,ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(odd) .wp-block-jetpack-timeline-item:not(.is-right) .timeline-item__bubble{left:-36px;right:auto}ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item.is-right.is-right.is-right,ul.wp-block-jetpack-timeline.is-alternating .wp-block-jetpack-timeline-item:nth-child(2n):not(.is-left),ul.wp-block-jetpack-timeline.is-alternating [data-type="jetpack/timeline-item"]:nth-child(2n) .wp-block-jetpack-timeline-item:not(.is-left){margin-left:0;margin-right:auto}}
phpunit.xml.dist CHANGED
@@ -22,5 +22,8 @@
22
  <testsuite name="block-patterns">
23
  <directory suffix="-test.php">./block-patterns/test/</directory>
24
  </testsuite>
 
 
 
25
  </testsuites>
26
  </phpunit>
22
  <testsuite name="block-patterns">
23
  <directory suffix="-test.php">./block-patterns/test/</directory>
24
  </testsuite>
25
+ <testsuite name="tutorials">
26
+ <directory suffix="-test.php">./tutorials/test/</directory>
27
+ </testsuite>
28
  </testsuites>
29
  </phpunit>
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: automattic
3
  Tags: block, blocks, editor, gutenberg, page
4
  Requires at least: 5.5
5
  Tested up to: 6.0
6
- Stable tag: 3.35918
7
  Requires PHP: 5.6.20
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
3
  Tags: block, blocks, editor, gutenberg, page
4
  Requires at least: 5.5
5
  Tested up to: 6.0
6
+ Stable tag: 3.36079
7
  Requires PHP: 5.6.20
8
  License: GPLv2 or later
9
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
tutorials/class-wpcom-tutorials.php ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tutorials (We would have called them Guided Tours but it's been done).
4
+ *
5
+ * Each Tutorial contains a flat list of Tasks. Each Task may contain several Steps in
6
+ * a `WpcomTourKit` component on the frontend, but we are only concerned with the top
7
+ * two levels of Tutorial and its contained Tasks, tracking Task completion status
8
+ * on the backend. This API will be used to generate
9
+ *
10
+ * @package A8C\FSE
11
+ */
12
+
13
+ /**
14
+ * WPCom_Tutorials class
15
+ */
16
+ class WPCom_Tutorials {
17
+
18
+ /**
19
+ * Storage for our singleton instance.
20
+ *
21
+ * @var null|WPCom_Tutorials
22
+ */
23
+ private static $instance = null;
24
+
25
+ /**
26
+ * Internal storage for registered Tutorials.
27
+ *
28
+ * @var array
29
+ */
30
+ private $registry = array();
31
+
32
+ /**
33
+ * Gets our singleton instance, initiating it the first time.
34
+ *
35
+ * @return WPCom_Tutorials instance.
36
+ */
37
+ public static function get_instance() {
38
+ if ( is_null( self::$instance ) ) {
39
+ self::$instance = new self();
40
+ }
41
+
42
+ return self::$instance;
43
+ }
44
+
45
+ /**
46
+ * Registers a Tutorial.
47
+ *
48
+ * @param string $id A unique ID for the Tutorial.
49
+ * @param array $options `title` and `tasks` required.
50
+ *
51
+ * @return bool True if successfully registered, false if it failed.
52
+ */
53
+ public function register( $id, $options ) {
54
+ if ( ! $this->validate_options( $options ) ) {
55
+ return false;
56
+ }
57
+ $options['id'] = $id;
58
+ $this->registry[ $id ] = $options;
59
+ return true;
60
+ }
61
+
62
+ /**
63
+ * Unregisters a registered Tutorial
64
+ *
65
+ * @param string $id Tutorial ID.
66
+ * @return bool True if successfully unregistered, false if not found.
67
+ */
68
+ public function unregister( $id ) {
69
+ if ( ! isset( $this->registry[ $id ] ) ) {
70
+ return false;
71
+ }
72
+ unset( $this->registry[ $id ] );
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * Validates the options passed in `WPCom_Tutorials::register`.
78
+ *
79
+ * @param array $options Associative array passed with registration.
80
+ * @return bool Validation success.
81
+ */
82
+ private function validate_options( $options ) {
83
+ if ( ! isset( $options['title'] ) || ! is_string( $options['title'] ) ) {
84
+ return false;
85
+ }
86
+ if ( ! isset( $options['tasks'] ) || ! is_array( $options['tasks'] ) ) {
87
+ return false;
88
+ }
89
+ foreach ( $options['tasks'] as $task ) {
90
+ if ( ! $this->validate_task( $task ) ) {
91
+ return false;
92
+ }
93
+ }
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * Validates a single task for expected structure.
99
+ *
100
+ * @param array $task A single task (id and title).
101
+ * @return bool Validation success.
102
+ */
103
+ private function validate_task( $task ) {
104
+ return isset( $task['id'] )
105
+ && is_string( $task['id'] )
106
+ && isset( $task['title'] )
107
+ && is_string( $task['title'] );
108
+ }
109
+
110
+ /**
111
+ * Retrieves a Tutorial from the registry.
112
+ *
113
+ * @param string $id The ID of the Tutorial to fetch.
114
+ * @return null|array Tutorial associative array if found, null if not.
115
+ */
116
+ public function get_tutorial( $id ) {
117
+ if ( ! isset( $this->registry[ $id ] ) ) {
118
+ return null;
119
+ }
120
+ $tutorial = $this->registry[ $id ];
121
+
122
+ // Populating status at runtime so that any invalidations happen within the Options API.
123
+ $tutorial['tasks'] = $this->get_tasks( $tutorial );
124
+ return $tutorial;
125
+ }
126
+
127
+ /**
128
+ * Retrives all registered Tutorial IDs
129
+ *
130
+ * @return array list of registered Tutorial IDs.
131
+ */
132
+ public function get_registered_ids() {
133
+ return array_keys( $this->registry );
134
+ }
135
+
136
+ /**
137
+ * Gets registered tasks for a Tutorial, with stored completion status.
138
+ *
139
+ * @param array $tutorial Tutorial object.
140
+ * @return array Tasks for Tutorial, with task completion status.
141
+ */
142
+ private function get_tasks( $tutorial ) {
143
+ $tasks_with_statuses = array();
144
+ $statuses = $this->get_statuses( $tutorial['id'] );
145
+
146
+ foreach ( $tutorial['tasks'] as $task ) {
147
+ // Default status is pending.
148
+ $task['status'] = 'pending';
149
+ if ( isset( $statuses[ $task['id'] ] ) ) {
150
+ $task['status'] = $statuses[ $task['id'] ];
151
+ }
152
+ $tasks_with_statuses[] = $task;
153
+ }
154
+
155
+ return $tasks_with_statuses;
156
+ }
157
+
158
+ /**
159
+ * Gets the completion statuses for a specific Tutorial.
160
+ *
161
+ * @param string $id Tutorial ID.
162
+ * @return array Completion statuses for Tutorial ID.
163
+ */
164
+ public function get_statuses( $id ) {
165
+ $statuses = $this->load_persisted_statuses();
166
+ return isset( $statuses[ $id ] ) ? $statuses[ $id ] : array();
167
+ }
168
+
169
+ /**
170
+ * Loads all Tutorial statuses from the database.
171
+ *
172
+ * @return array All Tutorial completion statuses.
173
+ */
174
+ private function load_persisted_statuses() {
175
+ if ( function_exists( 'get_user_attribute' ) ) {
176
+ return get_user_attribute( get_current_user_id(), 'wpcom_tutorials_status' );
177
+ }
178
+ return (array) get_user_meta( get_current_user_id(), 'wpcom_tutorials_status', true );
179
+ }
180
+
181
+ /**
182
+ * Marks a task with a status.
183
+ *
184
+ * @param string $tutorial_id Tutorial ID.
185
+ * @param string $task_id Task ID.
186
+ * @param string $status Status for the task. Allowed: complete|skipped|pending.
187
+ * @return bool True if status is updated, false otherwise.
188
+ */
189
+ public function mark_task( $tutorial_id, $task_id, $status ) {
190
+ if ( ! $this->validate_status( $status ) ) {
191
+ return false;
192
+ }
193
+ if ( ! $this->task_exists( $tutorial_id, $task_id ) ) {
194
+ return false;
195
+ }
196
+
197
+ $statuses = $this->load_persisted_statuses();
198
+ if ( ! isset( $statuses[ $tutorial_id ] ) ) {
199
+ $statuses[ $tutorial_id ] = array();
200
+ }
201
+ // pending is the natural state with no stored status.
202
+ if ( 'pending' === $status ) {
203
+ if ( isset( $statuses[ $tutorial_id ][ $task_id ] ) ) {
204
+ unset( $statuses[ $tutorial_id ][ $task_id ] );
205
+ }
206
+ } else {
207
+ $statuses[ $tutorial_id ][ $task_id ] = $status;
208
+ }
209
+
210
+ return $this->persist_statuses( $statuses );
211
+ }
212
+
213
+ /**
214
+ * Checks if a task exists for a registered tutorial.
215
+ *
216
+ * @param string $tutorial_id A tutorial ID.
217
+ * @param string $task_id A task ID.
218
+ * @return bool True if it exists, otherwise false.
219
+ */
220
+ private function task_exists( $tutorial_id, $task_id ) {
221
+ $tutorial = $this->get_tutorial( $tutorial_id );
222
+ foreach ( $tutorial['tasks'] as $task ) {
223
+ if ( $task_id === $task['id'] ) {
224
+ return true;
225
+ }
226
+ }
227
+ return false;
228
+ }
229
+
230
+ /**
231
+ * Persist statuses to the DB, using the platform-appropriate tech
232
+ *
233
+ * @param array $statuses All statuses to persist.
234
+ * @return bool True if persisting succeeded, otherwise false.
235
+ */
236
+ public function persist_statuses( $statuses ) {
237
+ if ( function_exists( 'update_user_attribute' ) ) {
238
+ return update_user_attribute( get_current_user_id(), 'wpcom_tutorials_status', $statuses );
239
+ }
240
+ return update_user_meta( get_current_user_id(), 'wpcom_tutorials_status', $statuses );
241
+ }
242
+
243
+ /**
244
+ * Checks if the status is valid.
245
+ *
246
+ * @param string $status A status to check. Allowed values: complete|pending|skipped.
247
+ * @return bool If status if valid, true, otherwise false.
248
+ */
249
+ private function validate_status( $status ) {
250
+ $allowed = array( 'complete', 'pending', 'skipped' );
251
+ return in_array( $status, $allowed, true );
252
+ }
253
+ }
tutorials/test/tutorials-test.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // phpcs:ignoreFile
3
+
4
+ use PHPUnit\Framework\TestCase;
5
+
6
+ require_once dirname( __FILE__, 2 ) . '/tutorials.php';
7
+
8
+ class WPCom_Tutorials_Test extends TestCase {
9
+
10
+ public function tearDown() {
11
+ // cleanse after each test
12
+ foreach ( wpcom_get_registered_tutorial_ids() as $id ) {
13
+ wpcom_tutorials()->unregister( $id );
14
+ }
15
+ // delete status storage
16
+ wpcom_tutorials()->persist_statuses( [] );
17
+ }
18
+
19
+ public function test_registered_tutorial_is_returned() {
20
+ wpcom_register_tutorial(
21
+ 'foo',
22
+ array(
23
+ 'title' => 'Foo',
24
+ 'tasks' => $this->get_tasks(),
25
+ )
26
+ );
27
+
28
+ $return_tutorial = wpcom_get_tutorial( 'foo' );
29
+
30
+ $expected_tutorial = array(
31
+ 'id' => 'foo',
32
+ 'title' => 'Foo',
33
+ 'tasks' => $this->get_tasks_with_status(),
34
+ );
35
+
36
+ $this->assertEquals( $return_tutorial, $expected_tutorial );
37
+ }
38
+
39
+ public function test_all_registered_ids_are_returned() {
40
+ $this->assertEquals( wpcom_get_registered_tutorial_ids(), array() );
41
+
42
+ wpcom_register_tutorial(
43
+ 'foo',
44
+ array(
45
+ 'title' => 'Foo',
46
+ 'tasks' => $this->get_tasks(),
47
+ )
48
+ );
49
+ wpcom_register_tutorial(
50
+ 'bar',
51
+ array(
52
+ 'title' => 'Bar',
53
+ 'tasks' => $this->get_tasks(),
54
+ )
55
+ );
56
+ wpcom_register_tutorial(
57
+ 'baz',
58
+ array(
59
+ 'title' => 'Baz',
60
+ 'tasks' => $this->get_tasks(),
61
+ )
62
+ );
63
+
64
+ $this->assertEquals( wpcom_get_registered_tutorial_ids(), array( 'foo', 'bar', 'baz' ) );
65
+ }
66
+
67
+ public function test_tutorial_with_malformed_options_fails() {
68
+ // empty option object
69
+ $foo1 = wpcom_register_tutorial( 'foo', array() );
70
+ $this->assertEmpty( wpcom_get_registered_tutorial_ids() );
71
+ $this->assertFalse( $foo1 );
72
+
73
+ // no title
74
+ $foo2 = wpcom_register_tutorial( 'foo', array( 'tasks' => $this->get_tasks() ) );
75
+ $this->assertEmpty( wpcom_get_registered_tutorial_ids() );
76
+ $this->assertFalse( $foo2 );
77
+
78
+ // no steps
79
+ $foo3 = wpcom_register_tutorial( 'foo', array( 'title' => 'Foo' ) );
80
+ $this->assertEmpty( wpcom_get_registered_tutorial_ids() );
81
+ $this->assertFalse( $foo3 );
82
+
83
+ // empty steps
84
+ $foo4 = wpcom_register_tutorial(
85
+ 'foo',
86
+ array(
87
+ 'title' => 'Foo',
88
+ 'steps' => array(),
89
+ )
90
+ );
91
+ $this->assertEmpty( wpcom_get_registered_tutorial_ids() );
92
+ $this->assertFalse( $foo4 );
93
+ }
94
+
95
+ public function test_persisted_task_statuses() {
96
+ wpcom_register_tutorial(
97
+ 'foo',
98
+ array(
99
+ 'title' => 'Foo',
100
+ 'tasks' => $this->get_tasks(),
101
+ )
102
+ );
103
+ wpcom_register_tutorial(
104
+ 'bar',
105
+ array(
106
+ 'title' => 'Bar',
107
+ 'tasks' => $this->get_tasks(),
108
+ )
109
+ );
110
+
111
+ wp_set_current_user( 1 );
112
+
113
+ $bar_task_1 = $this->get_task_by_id( wpcom_get_tutorial( 'foo' )['tasks'], 'bar' );
114
+ $this->assertEquals( $bar_task_1['status'], 'pending' );
115
+
116
+ wpcom_tutorial_mark_task( 'foo', 'bar', 'skipped' );
117
+ $bar_task_2 = $this->get_task_by_id( wpcom_get_tutorial( 'foo' )['tasks'], 'bar' );
118
+ $this->assertEquals( $bar_task_2['status'], 'skipped' );
119
+
120
+ wpcom_tutorial_mark_task( 'foo', 'bar', 'pending' );
121
+ $bar_task_3 = $this->get_task_by_id( wpcom_get_tutorial( 'foo' )['tasks'], 'bar' );
122
+ $this->assertEquals( $bar_task_3['status'], 'pending' );
123
+
124
+
125
+ wpcom_tutorial_mark_task( 'bar', 'bar', 'complete' );
126
+ $baz_task = $this->get_task_by_id( wpcom_get_tutorial( 'bar' )['tasks'], 'bar' );
127
+ $this->assertEquals( $baz_task['status'], 'complete' );
128
+
129
+ wp_set_current_user( 0 );
130
+ }
131
+
132
+ private function get_task_by_id( $tasks, $id ) {
133
+ $filtered = wp_list_filter( $tasks, [ 'id' => $id ] );
134
+ return empty( $filtered ) ? null : $filtered[0];
135
+ }
136
+
137
+ // boilerplate
138
+ private function get_tasks() {
139
+ return array(
140
+ array(
141
+ 'id' => 'bar',
142
+ 'title' => 'Bar',
143
+ ),
144
+ );
145
+ }
146
+
147
+ private function get_tasks_with_status( $status = 'pending' ) {
148
+ $tasks = $this->get_tasks();
149
+ foreach ( $tasks as $i => $task ) {
150
+ $tasks[ $i ]['status'] = $status;
151
+ }
152
+ return $tasks;
153
+ }
154
+ }
tutorials/tutorials.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tutorials API
4
+ *
5
+ * @package A8C\FSE
6
+ */
7
+
8
+ // Load the class we use. We also need this comment here to mollify PHPCS.
9
+ require_once __DIR__ . '/class-wpcom-tutorials.php';
10
+
11
+ /**
12
+ * Helper function to return a WPCom_Tutorials object
13
+ *
14
+ * @return WPCom_Tutorials instance object.
15
+ */
16
+ function wpcom_tutorials() {
17
+ return WPCom_Tutorials::get_instance();
18
+ }
19
+
20
+ // phpcs:disable Squiz.Commenting.FunctionComment.ParamCommentFullStop
21
+ // ^ it doesn't like the params for $options, despite this being Core style.
22
+ /**
23
+ * Registers a Tutorial.
24
+ *
25
+ * @param string $tutorial_id A unique ID for the Tutorial.
26
+ * @param array $options {
27
+ * Options for the Tutorial with the following shape.
28
+ *
29
+ * @type string $title The displayed title for this Tutorial
30
+ * @type array $tasks {
31
+ * An array of $tasks, each of which has:
32
+ * {
33
+ * @type string $id A unique identifier for the Task
34
+ * @type string $title What is displayed as the Task
35
+ * }
36
+ * }
37
+ * }
38
+ *
39
+ * @return bool True if successfully registered, false if it failed.
40
+ */
41
+ function wpcom_register_tutorial( $tutorial_id, $options ) {
42
+ return wpcom_tutorials()->register( $tutorial_id, $options );
43
+ }
44
+ // phpcs:enable
45
+
46
+ /**
47
+ * Retrieves a Tutorial from the registry.
48
+ *
49
+ * @param string $tutorial_id The ID of the Tutorial to fetch.
50
+ * @return null|array Tutorial associative array if found, null if not.
51
+ */
52
+ function wpcom_get_tutorial( $tutorial_id ) {
53
+ return wpcom_tutorials()->get_tutorial( $tutorial_id );
54
+ }
55
+
56
+ /**
57
+ * Marks a task with a status.
58
+ *
59
+ * @param string $tutorial_id Tutorial ID.
60
+ * @param string $task_id Task ID.
61
+ * @param string $status Status for the task. Allowed: complete|skipped|pending.
62
+ * @return bool True if status is updated, false otherwise.
63
+ */
64
+ function wpcom_tutorial_mark_task( $tutorial_id, $task_id, $status ) {
65
+ return wpcom_tutorials()->mark_task( $tutorial_id, $task_id, $status );
66
+ }
67
+
68
+ /**
69
+ * Retrives all registered Tutorial IDs
70
+ *
71
+ * @return array list of registered Tutorial IDs.
72
+ */
73
+ function wpcom_get_registered_tutorial_ids() {
74
+ return wpcom_tutorials()->get_registered_ids();
75
+ }