<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[4280] sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner: WordCamp Site Cloner: Convert to REST/Backbone to improve scalability</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/4280">4280</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/4280","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>iandunn</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-10-21 16:10:02 +0000 (Fri, 21 Oct 2016)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>WordCamp Site Cloner: Convert to REST/Backbone to improve scalability
See <a href="http://meta.trac.wordpress.org/ticket/1112">#1112</a>
Props prettyboymp</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerincludessitecontrolphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/site-control.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerincludessourcesiteidsettingphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/source-site-id-setting.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessitecontrolphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-control.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerwordcampsiteclonercss">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.css</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerwordcampsiteclonerjs">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.js</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerwordcampsiteclonerphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.php</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessitefiltersphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-filters.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessiteoptionphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-option.php</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerincludessitessectionphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/sites-section.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessitessectionphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/sites-section.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerincludessitecontrolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/site-control.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/site-control.php 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/site-control.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,21 +1,26 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Custom Customizer Control for Search WordCamp sites to clone
+ */
+
</ins><span class="cx" style="display: block; padding: 0 10px"> namespace WordCamp\Site_Cloner;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px"> defined( 'WPINC' ) or die();
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * Custom Customizer Control for a WordCamp site
- */
</del><span class="cx" style="display: block; padding: 0 10px"> class Site_Control extends \WP_Customize_Control {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- public $site_id, $site_name, $screenshot_url, $theme_slug;
- public $settings = 'wcsc_source_site_id';
- public $section = 'wcsc_sites';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function __construct( $manager, $id, $args = array() ) {
+ parent::__construct( $manager, $id, $args );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $this->capability = 'edit_theme_options';
+ $this->section = 'wcsc_sites';
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * Enqueue scripts and styles
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public function enqueue() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_view_templates' ) );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_style( 'wordcamp-site-cloner' );
</span><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_script( 'wordcamp-site-cloner' );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -24,14 +29,14 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * Render the control's content
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public function render_content() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $preview_url = add_query_arg(
- array(
- 'theme' => rawurlencode( $this->theme_slug ),
- 'wcsc_source_site_id' => rawurlencode( $this->site_id ),
- ),
- admin_url( 'customize.php' )
- );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ require_once( dirname( __DIR__ ) . '/templates/site-control.php' );
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- require( dirname( __DIR__ ) . '/templates/site-control.php' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+ * Render the control's Underscores templates
+ */
+ public function print_view_templates() {
+ require_once( dirname( __DIR__ ) . '/templates/site-option.php' );
+ require_once( dirname( __DIR__ ) . '/templates/site-filters.php' );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerincludessitessectionphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/sites-section.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/sites-section.php 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/sites-section.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,19 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-
-namespace WordCamp\Site_Cloner;
-
-defined( 'WPINC' ) or die();
-
-/**
- * Custom Customizer Section for WordCamp sites
- */
-class Sites_Section extends \WP_Customize_Section {
- public $type = 'wcsc-sites';
-
- /**
- * Render the section's content
- */
- protected function render() {
- require_once( dirname( __DIR__ ) . '/templates/sites-section.php' );
- }
-}
</del></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerincludessourcesiteidsettingphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/source-site-id-setting.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/source-site-id-setting.php 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/includes/source-site-id-setting.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,7 +1,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> namespace WordCamp\Site_Cloner;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px"> defined( 'WPINC' ) or die();
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -15,6 +14,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> public $type = 'wcsc-source-site-id';
</span><span class="cx" style="display: block; padding: 0 10px"> public $default = 0;
</span><span class="cx" style="display: block; padding: 0 10px"> public $sanitize_callback = 'absint';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> protected $preview_source_site_id;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,6 +28,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_head', array( $this, 'preview_source_site_css' ), 99 ); // wp_print_styles is too early; the theme's stylesheet would get enqueued later and take precedence
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter( 'get_post_metadata', array( $this, 'preview_jetpack_postmeta' ), 10, 4 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter( 'safecss_skip_stylesheet', array( $this, 'preview_skip_stylesheet' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ // Disable the current site's Custom CSS from being output
+ remove_action( 'wp_head', array( 'Jetpack_Custom_CSS', 'link_tag' ), 101 );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -109,6 +112,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * to the URL.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @param int $source_site_id
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *
+ * @return null
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> protected function update( $source_site_id ) {
</span><span class="cx" style="display: block; padding: 0 10px"> switch_to_blog( $source_site_id );
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessitecontrolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-control.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-control.php 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-control.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,15 +1,23 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php defined( 'WPINC' ) or die(); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<div id="wcsc-site-<?php echo esc_attr( $this->site_id ); ?>" class="wcscSite" data-preview-url="<?php echo esc_url( $preview_url ); ?>">
- <div class="wcsc-site-screenshot">
- <img src="<?php echo esc_url( $this->screenshot_url ); ?>" alt="<?php echo esc_attr( $this->site_name ); ?>" />
- </div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Top level template for the output of the Site Cloner Customizer Control
+ */
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- <h3 class="wcsc-site-name">
- <?php echo esc_html( $this->site_name ); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+namespace WordCamp\Site_Cloner;
+defined( 'WPINC' ) or die();
+
+?>
+
+<div id="wcsc-cloner">
+ <h3>
+ <?php esc_html_e( 'WordCamp Sites', 'wordcamporg' ); ?>
+ <span id="wcsc-sites-count" class="title-count wcsc-sites-count"></span>
</ins><span class="cx" style="display: block; padding: 0 10px"> </h3>
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- <span id="live-preview-label-<?php echo esc_attr( $this->site_id ); ?>" class="wcsc-live-preview-label">
- <?php _e( 'Live Preview', 'wordcamporg' ); ?>
- </span>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <div class="filters"></div>
+
+ <div class="wcsc-search">
+ <ul id="wcsc-results"></ul>
+ </div>
</ins><span class="cx" style="display: block; padding: 0 10px"> </div>
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessitefiltersphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-filters.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-filters.php (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-filters.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,78 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Template for the site filters
+ */
+
+namespace WordCamp\Site_Cloner;
+defined( 'WPINC' ) or die();
+
+?>
+
+<script id="tmpl-wcsc-site-filters" type="text/html">
+ <div class="wcsc-filter">
+ <label for="wcsc-filter-search-input">
+ <span class="customize-control-title">
+ <?php esc_html_e( 'Search', 'wordcamporg' ); ?>
+ </span>
+
+ <div class="customize-control-content">
+ <input type="search" id="wcsc-filter-search-input" class="wcsc-filter-search" />
+ </div>
+ </label>
+ </div>
+
+ <div class="wcsc-filter">
+ <label for="wcsc-filter-theme_slug">
+ <span class="customize-control-title">
+ <?php esc_html_e( 'Theme', 'wordcamporg' ); ?>
+ </span>
+
+ <div class="customize-control-content">
+ <select id="wcsc-filter-theme_slug" data-filter="theme_slug">
+ <option value="">Any</option>
+
+ <# _.each( data.themeOptions, function( themeOption ) { #>
+ <option value="{{themeOption.slug}}">{{themeOption.name}}</option>
+ <# }); #>
+ </select>
+ </div>
+ </label>
+ </div>
+
+ <div class="wcsc-filter">
+ <label for="wcsc-filter-year">
+ <span class="customize-control-title">
+ <?php esc_html_e( 'WordCamp Year', 'wordcamporg' ); ?>
+ </span>
+
+ <div class="customize-control-content">
+ <select id="wcsc-filter-year" data-filter="year">
+ <option value="">Any</option>
+
+ <# _.each( data.yearOptions, function( yearOption ) { #>
+ <option value="{{yearOption}}">{{yearOption}}</option>
+ <# }); #>
+ </select>
+ </div>
+ </label>
+ </div>
+
+ <div class="wcsc-filter">
+ <label for="wcsc-filter-css_preprocessor">
+ <span class="customize-control-title">
+ <?php esc_html_e( 'CSS Preprocessor', 'wordcamporg' ); ?>
+ </span>
+
+ <div class="customize-control-content">
+ <select id="wcsc-filter-css_preprocessor" data-filter="css_preprocessor">
+ <option value="">Any</option>
+
+ <# _.each( data.preprocessorOptions, function( preprocessorOption ) { #>
+ <option value="{{preprocessorOption}}">{{preprocessorOption}}</option>
+ <# }); #>
+ </select>
+ </div>
+ </label>
+ </div>
+</script>
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessiteoptionphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-option.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-option.php (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/site-option.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,34 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Template to display a single Site within the Site Cloner Control
+ */
+
+namespace WordCamp\Site_Cloner;
+defined( 'WPINC' ) or die();
+
+?>
+
+<script id="tmpl-wcsc-site-option" type="text/html">
+ <div class="wcsc-site-screenshot">
+ <img src="{{ data.screenshot_url }}" alt="{{ data.name }}"/>
+ </div>
+
+ <h3 class="wcsc-site-name">
+ {{ data.name }}
+ </h3>
+
+ <# if ( data.active ) { #>
+
+ <span id="live-previewing-{{ data.site_id }}" class="wcsc-previewing-label">
+ <?php _e( 'Viewing', 'wordcamporg' ); ?>
+ </span>
+
+ <# } else { #>
+
+ <span id="live-preview-label-{{ data.site_id }}" class="wcsc-live-preview-label">
+ <?php _e( 'Live Preview', 'wordcamporg' ); ?>
+ </span>
+
+ <# } #>
+</script>
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonertemplatessitessectionphp"></a>
<div class="delfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Deleted: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/sites-section.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/sites-section.php 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/templates/sites-section.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,15 +0,0 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php defined( 'WPINC' ) or die(); ?>
-
-<li id="section-<?php echo esc_attr( $this->id ); ?>" class="accordion-section control-section control-section-<?php echo esc_attr( $this->type ); ?>">
- <h3>
- <?php esc_html_e( 'WordCamp Sites' ); ?>
-
- <span class="title-count wcsc-sites-count">
- <?php echo count( $this->controls ); ?>
- </span>
- </h3>
-
- <div class="wcsc-sites-section-content">
- <ul id="wcsc-sites"></ul>
- </div>
-</li>
</del></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerwordcampsiteclonercss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.css 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.css 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,27 +1,26 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.control-section-wcsc-sites {
- padding: 0 8px;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+.wcsc-filter {
+ margin-bottom: 10px;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- #wcsc-sites {
- overflow: auto;
- }
-
- .wcscSite {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ .wcsc-site {
</ins><span class="cx" style="display: block; padding: 0 10px"> position: relative;
</span><span class="cx" style="display: block; padding: 0 10px"> cursor: pointer;
</span><span class="cx" style="display: block; padding: 0 10px"> border: 1px solid #DEDEDE;
</span><span class="cx" style="display: block; padding: 0 10px"> box-shadow: 0 1px 1px -1px rgba( 0, 0, 0, 0.1 );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ margin-top: 5px;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> .wcsc-site-screenshot {
</span><span class="cx" style="display: block; padding: 0 10px"> transition: opacity 0.2s ease-in-out 0s;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- .wcscSite:hover .wcsc-site-screenshot {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ .wcsc-site:hover .wcsc-site-screenshot {
</ins><span class="cx" style="display: block; padding: 0 10px"> opacity: 0.4;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- .wcsc-live-preview-label {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ .wcsc-live-preview-label,
+ .wcsc-previewing-label {
</ins><span class="cx" style="display: block; padding: 0 10px"> opacity: 0;
</span><span class="cx" style="display: block; padding: 0 10px"> position: absolute;
</span><span class="cx" style="display: block; padding: 0 10px"> top: 35%;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -38,6 +37,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> transition: opacity 0.1s ease-in-out 0s;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- .wcscSite:hover .wcsc-live-preview-label {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ .wcsc-site:hover .wcsc-live-preview-label,
+ .wcsc-site .wcsc-previewing-label {
</ins><span class="cx" style="display: block; padding: 0 10px"> opacity: 1;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerwordcampsiteclonerjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.js 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.js 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,66 +1,478 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-( function( wp, $ ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+( function( wp, $, Backbone, win, settings ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> 'use strict';
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! wp || ! wp.customize ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var api = wp.customize;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wp.customize.WordCamp = wp.customize.WordCamp || {};
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- /**
- * The Clone Another WordCamp panel
- */
- api.panelConstructor.wcscPanel = api.Panel.extend( {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var api = wp.customize,
+ wcsc = api.WordCamp.SiteCloner = {
+ models : {},
+ views : {},
+ collections : {},
+ routers : {},
+ settings : {}
+ };
+
+ wcsc.settings = settings || {};
+
+ // Model for a single site
+ wcsc.models.Site = Backbone.Model.extend( {
+ idAttribute : 'site_id',
+
+ defaults : {
+ 'active' : false
+ }
+ } );
+
+ // Model representing the filter state for searching/filtering sites
+ wcsc.models.SearchFilter = Backbone.Model.extend( {
+ 's' : '',
+ 'theme_slug' : '',
+ 'year' : '',
+ 'css_preprocessor' : ''
+ } );
+
+ // Top level view for the Site Cloner Control
+ wcsc.views.SiteSearch = Backbone.View.extend( {
+ el : '#wcsc-cloner .wcsc-search',
+
+ // Index of the currently viewed page of results
+ page : 0,
+
+ initialize : function( options ) {
+ // Update scroller position
+ _.bindAll( this, 'scroller' );
+
+ // Container that will be scrolled within
+ this.$container = $( '#wcsc-cloner' ).parents( 'ul.accordion-section-content' );
+ // Bind scrolling within the container to check for infinite scroll
+ this.$container.bind( 'scroll', _.throttle( this.scroller, 300 ) );
+
+ // The model and view for filtering the site results
+ this.filterView = new wcsc.views.SearchFilters( {
+ model : this.collection.searchFilter,
+ parent : this
+ } );
+
+ // View for listing the matching sites
+ this.resultsView = new wcsc.views.SearchResults( {
+ collection : this.collection,
+ parent : this
+ } );
+ },
+
+ render : function() {
+ this.filterView.render();
+ this.resultsView.render();
+
+ this.$el.empty().append( this.resultsView.el );
+ },
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Initialize the panel after it's loaded
- *
- * Ideally, the Previewer would be set to the requested site ID during the initial PHP request, rather than
- * loading the host site in the Previewer, and then refreshing it to use the requested site. That became a
- * rabbit hole, though, so it's done this way instead.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Checks if a user has reached the bottom of the list and triggers a scroll event to show more sites if
+ * needed.
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ready : function() {
- var urlParams = getUrlParams( window.location.href );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ scroller : function() {
+ var visibleBottom, threshold, elementHeight, containerHeight, scrollTop;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( urlParams.hasOwnProperty( 'wcsc_source_site_id' ) ) {
- this.expand();
- api( 'wcsc_source_site_id' ).set( urlParams.wcsc_source_site_id );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ scrollTop = this.$container.scrollTop();
+ containerHeight = this.$container.innerHeight();
+ elementHeight = this.$container.get( 0 ).scrollHeight;
+
+ visibleBottom = scrollTop + containerHeight;
+ threshold = Math.round( elementHeight * 0.9 );
+
+ if ( visibleBottom > threshold ) {
+ this.trigger( 'wcsc:scroll' );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> } );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Collection representing the list of cloneable sites
+ wcsc.collections.Sites = Backbone.Collection.extend( {
+ model : wcsc.models.Site,
+ url : wcsc.settings.apiUrl,
+
+ initialize : function( options ) {
+ this.searchFilter = options.searchFilter || {};
+
+ this.listenTo( this.searchFilter, 'change', this.applyFilter );
+ },
+
+ // Filter this collection by the updated searchFilter attributes
+ applyFilter : function() {
+ var filters = this.searchFilter.toJSON(),
+ activeFilters = _.pick( filters, _.identity ),
+ term = '',
+ sites;
+
+ // Nothing actually changed, so don't update the collection
+ if ( _.isEmpty( this.searchFilter.changedAttributes() ) ) {
+ return;
+ }
+
+ // No active filters. Reset to the full list and bail
+ if ( _.isEmpty( activeFilters ) ) {
+ this.resetCanonical();
+ return;
+ }
+
+ this.resetCanonical( { silent: true } );
+
+ // Remove the search query restriction since we already filtered by word matches above
+ if ( activeFilters.s ) {
+ term = activeFilters.s;
+
+ delete activeFilters.s;
+ }
+
+ sites = this.where( activeFilters );
+
+ if ( term ) {
+ sites = this.filterBySearch( sites, term );
+ }
+
+ this.reset( sites );
+ },
+
+ // Internal method for filtering sites by search terms
+ filterBySearch : function( sites, term ) {
+ var match, name;
+
+ // Escape the term string for RegExp meta characters
+ term = term.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' );
+
+ // Consider spaces as word delimiters and match the whole string
+ // so matching terms can be combined
+ term = term.replace( / /g, ')(?=.*' );
+ match = new RegExp( '^(?=.*' + term + ').+', 'i' );
+
+ return _.filter( sites, function( site ) {
+ name = site.get( 'name' ).replace( /(<([^>]+)>)/ig, '' );
+
+ return match.test( name );
+ } );
+ },
+
+ paginate : function( pageIndex ) {
+ var collection = this;
+
+ pageIndex = pageIndex || 0;
+
+ collection = _( collection.rest( 20 * pageIndex ) );
+ collection = _( collection.first( 20 ) );
+
+ return collection;
+ },
+
+ // Resets the site collection dataset to the canonical list originally pulled from the api
+ resetCanonical : function( options ) {
+ var activeSite,
+ activeSiteId = api( 'wcsc_source_site_id' ).get();
+
+ options = options || {};
+
+ this.reset( wcsc.settings.siteData, options );
+
+ // Restore the currently active site
+ if ( activeSiteId ) {
+ activeSite = this.find( { site_id : activeSiteId } );
+
+ if ( 'undefined' !== typeof activeSite ) {
+ activeSite.set( { active: true } );
+ }
+ }
+ }
+ } );
+
+ // View for a single site
+ wcsc.views.Site = Backbone.View.extend( {
+ className : 'wcsc-site',
+ html : wp.template( 'wcsc-site-option' ),
+ touchDrag : false,
+
+ attributes : function() {
+ return {
+ 'id' : 'wcsc-site-' + this.model.get( 'site_id' ),
+ 'data-site-id' : this.model.get( 'site_id' )
+ }
+ },
+
+ events : {
+ 'click' : 'preview',
+ 'keydown' : 'preview',
+ 'touchend' : 'preview',
+ 'touchmove' : 'preventPreview'
+ },
+
+ initialize : function( options ) {
+ this.parent = options.parent;
+
+ this.listenTo( this.model, 'change', this.render );
+ this.render();
+ },
+
+ render : function() {
+ this.$el.html( this.html( this.model.toJSON() ) );
+ },
+
+ preventPreview : function() {
+ this.touchDrag = true;
+ },
+
+ preview : function( event ) {
+ event = event || window.event;
+
+ // Ignore touches caused by scrolling
+ if ( this.touchDrag === true ) {
+ this.touchDrag = false;
+ }
+
+ event.preventDefault();
+
+ this.$el.trigger( 'wcsc:previewSite', this.model );
+ }
+ } );
+
+ // View for the site results list
+ wcsc.views.SearchResults = Backbone.View.extend( {
+ className: 'wcsc-results',
+
+ initialize : function( options ) {
+ var self = this;
+
+ this.parent = options.parent;
+ this.$siteCount = $( '#wcsc-sites-count' );
+
+ // Re-render the view whenever a collection change is complete
+ this.listenTo( this.collection, 'reset', function() {
+ self.parent.page = 0;
+ self.render( this );
+ } );
+
+ this.listenTo( this.parent, 'wcsc:scroll', function() {
+ self.renderSites( self.parent.page );
+ } );
+ },
+
+ render : function() {
+ this.$el.empty();
+ this.renderSites( this.parent.page );
+ this.$siteCount.text( this.collection.length );
+ },
+
+ renderSites : function( page ) {
+ var self = this;
+
+ // Get a collection of just the requested page
+ this.instance = this.collection.paginate( page );
+
+ if ( this.instance.size() === 0 ) {
+ this.parent.trigger( 'wcsc:end' );
+ return;
+ }
+
+ this.instance.each( function( site ) {
+ var siteView = new wcsc.views.Site( {
+ model : site,
+ parent : self
+ } );
+
+ siteView.render();
+
+ self.$el.append( siteView.el );
+ } );
+
+ this.parent.page++;
+ }
+
+ } );
+
+ // View for the search and dropdown filters
+ wcsc.views.SearchFilters = Backbone.View.extend( {
+ el : '#wcsc-cloner .filters',
+ className : 'wscs-filters',
+ html : wp.template( 'wcsc-site-filters' ),
+
+ events : {
+ "input #wcsc-filter-search-input" : "search",
+ "keyup #wcsc-filter-search-input" : "search",
+ "change select" : "applyFilter"
+ },
+
+ initialize : function( options ) {
+ this.parent = options.parent;
+ },
+
+ render : function() {
+ var data = {};
+
+ data.themeOptions = wcsc.settings.themes;
+ data.yearOptions = _.uniq( this.parent.collection.pluck( 'year' ) ).sort();
+ data.preprocessorOptions = _.uniq( this.parent.collection.pluck( 'css_preprocessor' ) ).sort();
+
+ this.$el.html( this.html( data ) );
+
+ this.$searchInput = $( '#wcsc-filter-search-input' );
+ this.$themeFilter = $( '#wcsc-filter-theme_slug' );
+ this.$yearFilter = $( '#wcsc-filter-year' );
+ this.$preprocessorFilter = $( '#wcsc-filter-css_preprocessor' );
+ },
+
+ search : function( event ) {
+ // Clear on escape.
+ if ( event.type === 'keyup' && event.which === 27 ) {
+ event.target.value = '';
+ }
+
+ /**
+ * Since doSearch is debounced, it will only run when user input comes to a rest
+ */
+ this.doSearch( event );
+ },
+
+ doSearch : _.debounce( function( event ) {
+ this.model.set( 's', event.target.value );
+ }, 500 ),
+
+ applyFilter : function( event ) {
+ var $target = $( event.target ),
+ value = $target.val(),
+ filter = $target.data( 'filter' );
+
+ this.model.set( filter, value );
+ },
+
+ // Set the inputs to the set of filters as triggered by the router on initial load
+ setInputs : function( filters ) {
+ this.model.set( filters, { silent : true } );
+
+ this.$searchInput.val( this.model.get( 's' ) );
+ this.$themeFilter.val( this.model.get( 'theme_slug' ) );
+ this.$yearFilter.val( this.model.get( 'year' ) );
+ this.$preprocessorFilter.val( this.model.get( 'css_preprocessor' ) );
+
+ this.model.trigger( 'change', this.model );
+ }
+ } );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Custom control representing a site that can be previewed/imported
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Sets up a listener to store the user's selected filters and search, so that a user's position can be
+ * restored as well as possible after a theme changes causes a full refresh.
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- api.controlConstructor.wcscSite = api.Control.extend( {
- /**
- * Initialize the control after it's loaded
- */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ wcsc.routers.FilterState = Backbone.Router.extend( {
+ routes : {
+ 'wcsc?*filters' : 'applyFilters'
+ },
+
+ initialize : function( options ) {
+ this.parent = options.parent;
+
+ // Any time the collection is reset, we need to update the displayed route
+ this.listenTo( this.parent.view.collection, 'reset', this.updateLocation );
+ },
+
+ // Applies the filters set in the query string to the view
+ applyFilters : function( queryString ) {
+ var filters = deserializeQueryString( queryString );
+
+ this.parent.view.filterView.setInputs( filters );
+ },
+
+ updateLocation : function() {
+ var filters = this.parent.view.collection.searchFilter.toJSON(),
+ activeFilters = _.pick( filters, _.identity ),
+ queryString = $.param( activeFilters );
+
+ this.navigate( 'wcsc?' + queryString );
+ }
+ } );
+
+ // Customizer Control wrapping the site search applet
+ api.controlConstructor.wcscSearch = api.Control.extend( {
</ins><span class="cx" style="display: block; padding: 0 10px"> ready : function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- this.container.on( 'click', '.wcscSite', this.previewSite );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var filter = new wcsc.models.SearchFilter(); // Top level model representing the current filter applied to the collection
+
+ this.siteCollection = new wcsc.collections.Sites( { searchFilter : filter } );
+
+ // Fill the site collection and setup search when complete
+ this.siteCollection.fetch( {
+ success : this.setupSearch.bind( this )
+ } );
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- /**
- * Preview the selected site
- *
- * If the site is using a different theme, then reload the entire Customizer with the theme URL parameter
- * set, so that the Theme Switcher will handle previewing the new theme for us. Otherwise just set the ID
- * to refresh the Previewer with the current theme and the new site's CSS, etc.
- *
- * @param {object} event
- */
- previewSite : function( event ) {
- var previewUrl = $( this ).data( 'preview-url' ),
- previewUrlParams = getUrlParams( previewUrl );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Initialize the site search instance for cloning other sites
+ setupSearch : function() {
+ var currentSite,
+ control = this,
+ urlParams = getUrlParams( win.location.href );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( api( 'wcsc_source_site_id' ).get() == previewUrlParams.wcsc_source_site_id ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Set a canonical array of all sites prior to filtering
+ wcsc.settings.siteData = this.siteCollection.toJSON();
+
+ // If the wcsc_source_site_id is set, it;s most likely from a user previewing a site, so bring them back
+ if ( urlParams.hasOwnProperty( 'wcsc_source_site_id' ) ) {
+ api.section( this.section() ).expand();
+
+ currentSite = this.siteCollection.find( { site_id : urlParams.wcsc_source_site_id } );
+
+ if ( currentSite ) {
+ this.setActiveSite( currentSite );
+ }
+ }
+
+ $( '#wcsc-cloner' ).on( 'wcsc:previewSite', '.wcsc-site', function( event, site ) {
+ control.previewSite( site );
+ } );
+
+ // Setup the top level Site Search View
+ this.view = new wcsc.views.SiteSearch( {
+ parent : this,
+ collection : this.siteCollection
+ } );
+
+ this.view.render();
+
+ // Initialize the router to allow state to be restored after a full refresh
+ wcsc.router = new wcsc.routers.FilterState( { parent : this } );
+ Backbone.history.start();
+ },
+
+ previewSite : function( site ) {
+ var queryString, routerFragment;
+
+ if ( api( 'wcsc_source_site_id' ).get() == site.get( 'site_id' ) ) {
+ // We're already previewing this site
</ins><span class="cx" style="display: block; padding: 0 10px"> return;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( api.settings.theme.stylesheet === previewUrlParams.theme ) {
- api( 'wcsc_source_site_id' ).set( previewUrlParams.wcsc_source_site_id );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( api.settings.theme.stylesheet === site.get( 'theme_slug' ) ) {
+ this.setActiveSite( site );
</ins><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- window.parent.location = previewUrl;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // We have to do a full refresh when changing themes or other controls won't correlate to the current theme.
+ queryString = $.param( {
+ 'theme' : site.get( 'theme_slug' ),
+ 'wcsc_source_site_id' : site.get( 'site_id' )
+ } );
+
+ routerFragment = Backbone.history.getFragment();
+ win.parent.location = wcsc.settings.customizerUrl + '?' + queryString + '#' + routerFragment;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ },
+
+ // Set the active site and update the model to reflect the change
+ setActiveSite : function( site ) {
+ var site_id = site.get( 'site_id' );
+
+ this.siteCollection.each( function( _site ) {
+ _site.set( { active : false } );
+ } );
+
+ site.set( { active : true } );
+ api( 'wcsc_source_site_id' ).set( site.get( 'site_id' ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> } );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -74,26 +486,45 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @returns {object}
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> function getUrlParams( url ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var match, questionMarkIndex, query,
- urlParams = {},
- pl = /\+/g, // Regex for replacing addition symbol with a space
- search = /([^&=]+)=?([^&]*)/g,
- decode = function ( s ) {
- return decodeURIComponent( s.replace( pl, " " ) );
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var questionMarkIndex, query, hashIndex;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Strip hash first
+ hashIndex = url.indexOf( '#' );
+
+ if ( hashIndex > -1 ) {
+ url = url.substring( 0, hashIndex );
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> questionMarkIndex = url.indexOf( '?' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( -1 === questionMarkIndex ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return urlParams;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return {};
</ins><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><span class="cx" style="display: block; padding: 0 10px"> query = url.substring( questionMarkIndex + 1 );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- while ( match = search.exec( query ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return deserializeQueryString( query );
+ }
+
+ /**
+ * Deserialize a query string into an object
+ *
+ * @param queryString
+ * @returns {object}
+ */
+ function deserializeQueryString( queryString ) {
+ var match,
+ urlParams = {},
+ pl = /\+/g, // Regex for replacing addition symbol with a space
+ search = /([^&=]+)=?([^&]*)/g,
+ decode = function( s ) {
+ return decodeURIComponent( s.replace( pl, " " ) );
+ };
+
+ while ( match = search.exec( queryString ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> urlParams[ decode( match[ 1 ] ) ] = decode( match[ 2 ] );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return urlParams;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-} )( window.wp, jQuery );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+})( wp, jQuery, Backbone, window, _wcscSettings );
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcampsiteclonerwordcampsiteclonerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.php 2016-10-21 16:09:56 UTC (rev 4279)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-site-cloner/wordcamp-site-cloner.php 2016-10-21 16:10:02 UTC (rev 4280)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,26 +1,42 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-namespace WordCamp\Site_Cloner;
-
-defined( 'WPINC' ) or die();
-
</del><span class="cx" style="display: block; padding: 0 10px"> /*
</span><span class="cx" style="display: block; padding: 0 10px"> Plugin Name: WordCamp Site Cloner
</span><span class="cx" style="display: block; padding: 0 10px"> Description: Allows organizers to clone another WordCamp's theme and custom CSS as a starting point for their site.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-Version: 0.1
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+Version: 0.2
</ins><span class="cx" style="display: block; padding: 0 10px"> Author: WordCamp.org
</span><span class="cx" style="display: block; padding: 0 10px"> Author URI: http://wordcamp.org
</span><span class="cx" style="display: block; padding: 0 10px"> License: GPLv2 or later
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// todo if Jetpack_Custom_CSS:get_css is callable, register these, otherwise fatal errors
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+namespace WordCamp\Site_Cloner;
+defined( 'WPINC' ) or die();
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-add_action( 'plugins_loaded', __NAMESPACE__ . '\get_wordcamp_sites' );
-add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\register_scripts' );
-add_action( 'admin_menu', __NAMESPACE__ . '\add_submenu_page' );
-add_action( 'customize_register', __NAMESPACE__ . '\register_customizer_components' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+const PRIME_SITES_CRON_ACTION = 'wcsc_prime_sites';
+const WORDCAMP_SITES_TRANSIENT_KEY = 'wcsc_sites';
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Initialization
+ */
+function initialize() {
+ // We rely on the Custom CSS module being available
+ if ( ! class_exists( '\Jetpack' ) ) {
+ return;
+ }
+
+ add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\register_scripts' );
+ add_action( 'admin_menu', __NAMESPACE__ . '\add_submenu_page' );
+ add_action( 'customize_register', __NAMESPACE__ . '\register_customizer_components' );
+ add_action( 'rest_api_init', __NAMESPACE__ . '\register_api_endpoints' );
+ add_action( PRIME_SITES_CRON_ACTION, __NAMESPACE__ . '\prime_wordcamp_sites' );
+
+ if ( ! wp_next_scheduled( PRIME_SITES_CRON_ACTION ) ) {
+ wp_schedule_event( time(), 'daily', PRIME_SITES_CRON_ACTION );
+ }
+}
+add_action( 'plugins_loaded', __NAMESPACE__ . '\initialize' ); // After Jetpack has loaded
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Register scripts and styles
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> function register_scripts() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,19 +44,50 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'wordcamp-site-cloner',
</span><span class="cx" style="display: block; padding: 0 10px"> plugin_dir_url( __FILE__ ) . 'wordcamp-site-cloner.css',
</span><span class="cx" style="display: block; padding: 0 10px"> array(),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 1
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 2
</ins><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> wp_register_script(
</span><span class="cx" style="display: block; padding: 0 10px"> 'wordcamp-site-cloner',
</span><span class="cx" style="display: block; padding: 0 10px"> plugin_dir_url( __FILE__ ) . 'wordcamp-site-cloner.js',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- array( 'jquery', 'customize-controls' ),
- 1,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ array( 'jquery', 'customize-controls', 'wp-backbone' ),
+ 2,
</ins><span class="cx" style="display: block; padding: 0 10px"> true
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ wp_localize_script(
+ 'wordcamp-site-cloner',
+ '_wcscSettings',
+ array(
+ 'apiUrl' => get_rest_url( null, '/wordcamp-site-cloner/v1/sites/' ),
+ 'customizerUrl' => admin_url( 'customize.php' ),
+ 'themes' => get_available_themes(),
+ )
+ );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Get all of the available themes
+ *
+ * @return array
+ */
+function get_available_themes() {
+ /** @var \WP_Theme $theme */
+ $available_themes = array();
+ $raw_themes = wp_get_themes( array( 'allowed' => true ) );
+
+ foreach ( $raw_themes as $theme ) {
+ $theme_name = $theme->display( 'Name' );
+ $available_themes[] = array(
+ 'slug' => $theme->get_stylesheet(),
+ 'name' => $theme_name ?: $theme->get_stylesheet()
+ );
+ }
+
+ return $available_themes;
+}
+
+/**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Add a submenu page
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * This helps organizers to realize that this tool exists, because they otherwise wouldn't see it unless
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -52,7 +99,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> __( 'Clone Another WordCamp', 'wordcamporg' ),
</span><span class="cx" style="display: block; padding: 0 10px"> __( 'Clone Another WordCamp', 'wordcamporg' ),
</span><span class="cx" style="display: block; padding: 0 10px"> 'switch_themes',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'customize.php?autofocus[panel]=wordcamp_site_cloner'
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'customize.php?autofocus[section]=wcsc_sites'
</ins><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -63,122 +110,208 @@
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> function register_customizer_components( $wp_customize ) {
</span><span class="cx" style="display: block; padding: 0 10px"> require_once( __DIR__ . '/includes/source-site-id-setting.php' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- require_once( __DIR__ . '/includes/sites-section.php' );
</del><span class="cx" style="display: block; padding: 0 10px"> require_once( __DIR__ . '/includes/site-control.php' );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $wp_customize->register_control_type( __NAMESPACE__ . '\Site_Control' );
-
</del><span class="cx" style="display: block; padding: 0 10px"> $wp_customize->add_setting( new Source_Site_ID_Setting(
</span><span class="cx" style="display: block; padding: 0 10px"> $wp_customize,
</span><span class="cx" style="display: block; padding: 0 10px"> 'wcsc_source_site_id',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- array()
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ array( 'capability' => 'switch_themes' )
</ins><span class="cx" style="display: block; padding: 0 10px"> ) );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $wp_customize->add_panel(
- 'wordcamp_site_cloner',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $wp_customize->add_section(
+ 'wcsc_sites',
</ins><span class="cx" style="display: block; padding: 0 10px"> array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'type' => 'wcscPanel',
- 'title' => __( 'Clone Another WordCamp', 'wordcamporg' ),
- 'description' => __( "Clone another WordCamp's theme and custom CSS as a starting point for your site.", 'wordcamporg' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'title' => __( 'Clone Another WordCamp', 'wordcamporg' ),
+ 'capability' => 'switch_themes'
</ins><span class="cx" style="display: block; padding: 0 10px"> )
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $wp_customize->add_section( new Sites_Section(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $wp_customize->add_control( new Site_Control(
</ins><span class="cx" style="display: block; padding: 0 10px"> $wp_customize,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'wcsc_sites',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'wcsc_site_search',
</ins><span class="cx" style="display: block; padding: 0 10px"> array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'panel' => 'wordcamp_site_cloner',
- 'title' => __( 'WordCamp Sites', 'wordcamporg' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'type' => 'wcscSearch',
+ 'label' => __( 'Search', 'wordcamporg' ),
+ 'settings' => 'wcsc_source_site_id',
+ 'section' => 'wcsc_sites'
</ins><span class="cx" style="display: block; padding: 0 10px"> )
</span><span class="cx" style="display: block; padding: 0 10px"> ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- foreach( get_wordcamp_sites() as $wordcamp ) {
- if ( get_current_blog_id() == $wordcamp['site_id'] ) {
- continue;
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Register the REST API endpoint for the Customizer to use to retriever the site list
+ */
+function register_api_endpoints() {
+ if ( ! current_user_can( 'switch_themes' ) ) {
+ return;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $wp_customize->add_control( new Site_Control(
- $wp_customize,
- 'wcsc_site_id_' . $wordcamp['site_id'],
- array(
- 'type' => 'wcscSite', // todo should be able to set this in control instead of here, but if do that then control contents aren't rendered
- 'site_id' => $wordcamp['site_id'],
- 'site_name' => $wordcamp['name'],
- 'theme_slug' => $wordcamp['theme_slug'],
- 'screenshot_url' => $wordcamp['screenshot_url'],
- )
- ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // todo - use `permission_callback` instead
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ register_rest_route(
+ 'wordcamp-site-cloner/v1',
+ '/sites',
+ array(
+ 'methods' => 'GET',
+ 'callback' => __NAMESPACE__ . '\sites_endpoint',
+ )
+ );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Get required data for relevant WordCamp sites
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Handle the response for the Sites endpoint
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * This isn't actually used until register_customizer_components(), but it's called during `plugins_loaded` in
- * order to prime the cache. That has to be done before `setup_theme`, because the Theme Switcher will override
- * the current theme when `?theme=` is present in the URL parameters, and it's safer to just avoid that than to
- * muck with the internals and try to reverse it on the fly.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * This always pulls cached data, because Central needs to be the site generating it. See get_wordcamp_sites().
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function get_wordcamp_sites() {
- require_once( WP_PLUGIN_DIR . '/wcpt/wcpt-wordcamp/wordcamp-loader.php' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function sites_endpoint() {
+ $sites = array();
+ $cached_sites = get_site_transient( WORDCAMP_SITES_TRANSIENT_KEY );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // plugins_loaded is runs on every screen, but we only need this when loading the Customizer and Previewer
- if ( 'customize.php' != basename( $_SERVER['SCRIPT_NAME'] ) && empty( $_REQUEST['wp_customize'] ) ) {
- return array();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( $cached_sites ) {
+ unset( $cached_sites[ get_current_blog_id() ] );
+
+ $sites = array_values( $cached_sites );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $transient_key = 'wcsc_sites';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return $sites;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( $sites = get_site_transient( $transient_key ) ) {
- return $sites;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Prime the cache of cloneable WordCamp sites
+ *
+ * This is called via WP Cron.
+ *
+ * @todo - Reintroduce batching from `1112.3.diff` to get more than 500 sites
+ */
+function prime_wordcamp_sites() {
+ // This only needs to run on a single site, then the whole network can use the cached result
+ if ( ! is_main_site() ) {
+ return;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Keep the cache longer than needed, just to be sure that it doesn't expire before the cron job runs again
+ set_site_transient( WORDCAMP_SITES_TRANSIENT_KEY, get_wordcamp_sites(), DAY_IN_SECONDS * 2 );
+}
+
+/**
+ * Get WordCamp sites that are suitable for cloning
+ *
+ * @return array
+ */
+function get_wordcamp_sites() {
+ /*
+ * The post statuses that \WordCamp_Loader::get_public_post_statuses() returns are only created on Central,
+ * because the plugin isn't active on any other sites.
+ */
+ if ( ! is_main_site() ) {
+ return array();
+ }
+
+ if ( ! \Jetpack::is_module_active( 'custom-css' ) ) {
+ \Jetpack::activate_module( 'custom-css', false, false );
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> switch_to_blog( BLOG_ID_CURRENT_SITE ); // central.wordcamp.org
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $sites = array();
- $wordcamps = get_posts( array(
- 'post_type' => 'wordcamp',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $wordcamp_query = new \WP_Query( array(
+ 'post_type' => WCPT_POST_TYPE_ID,
</ins><span class="cx" style="display: block; padding: 0 10px"> 'post_status' => \WordCamp_Loader::get_public_post_statuses(),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'posts_per_page' => 125, // todo temporary workaround until able to add filters to make hundreds of sites manageable
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'posts_per_page' => 500,
</ins><span class="cx" style="display: block; padding: 0 10px"> 'meta_key' => 'Start Date (YYYY-mm-dd)',
</span><span class="cx" style="display: block; padding: 0 10px"> 'orderby' => 'meta_value_num',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'order' => 'DESC',
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> 'meta_query' => array(
</span><span class="cx" style="display: block; padding: 0 10px"> array(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // New sites won't have finished designs, so ignore them
</ins><span class="cx" style="display: block; padding: 0 10px"> 'key' => 'Start Date (YYYY-mm-dd)',
</span><span class="cx" style="display: block; padding: 0 10px"> 'value' => strtotime( 'now - 1 month' ),
</span><span class="cx" style="display: block; padding: 0 10px"> 'compare' => '<'
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ )
</ins><span class="cx" style="display: block; padding: 0 10px"> ),
</span><span class="cx" style="display: block; padding: 0 10px"> ) );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- foreach( $wordcamps as $wordcamp ) {
- $site_id = get_wordcamp_site_id( $wordcamp );
- $site_url = get_post_meta( $wordcamp->ID, 'URL', true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $sites = get_filtered_wordcamp_sites( $wordcamp_query->get_posts() );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( ! $site_id || ! $site_url ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ uasort( $sites, __NAMESPACE__ . '\sort_sites_by_year' );
+
+ restore_current_blog();
+
+ return $sites;
+}
+
+/**
+ * Filter out sites that aren't relevant to the Cloner
+ *
+ * @param array $wordcamps
+ *
+ * @return array
+ */
+function get_filtered_wordcamp_sites( $wordcamps ) {
+ $sites = array();
+
+ foreach ( $wordcamps as $wordcamp ) {
+ $site_id = get_wordcamp_site_id( $wordcamp );
+ $site_url = get_post_meta( $wordcamp->ID, 'URL', true );
+ $start_date = get_post_meta( $wordcamp->ID, 'Start Date (YYYY-mm-dd)', true );
+
+ if ( ! $site_id || ! $site_url || ! $start_date ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> continue;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> switch_to_blog( $site_id );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $sites[] = array(
- 'site_id' => $site_id,
- 'name' => get_wordcamp_name(),
- 'theme_slug' => get_stylesheet(),
- 'screenshot_url' => get_screenshot_url( $site_url ),
- );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /*
+ * Sites with Coming Soon enabled probably don't have a finished design yet, so there's no point in
+ * cloning it.
+ */
+ if ( ! coming_soon_plugin_enabled() ) {
+ $preprocessor = \Jetpack_Custom_CSS::get_preprocessor();
+ $preprocessor = isset( $preprocessor[ 'name' ] ) ? $preprocessor[ 'name' ] : 'none';
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $sites[ $site_id ] = array(
+ 'site_id' => $site_id,
+ 'name' => get_wordcamp_name(),
+ 'theme_slug' => get_stylesheet(),
+ 'screenshot_url' => get_screenshot_url( $site_url ),
+ 'year' => date( 'Y', $start_date ),
+ 'css_preprocessor' => $preprocessor,
+ );
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> restore_current_blog();
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- restore_current_blog();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return $sites;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- set_site_transient( $transient_key, $sites, DAY_IN_SECONDS );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * Determine if the Coming Soon plugin is enabled for the current site
+ *
+ * @return bool
+ */
+function coming_soon_plugin_enabled() {
+ global $WCCSP_Settings;
+ $enabled = false;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return $sites;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! is_callable( 'WCCSP_Settings::get_settings' ) ) {
+ return $enabled;
+ }
+
+ // We may need to instantiate the class if this is the first time calling this function
+ if ( ! is_a( $WCCSP_Settings, 'WCCSP_Settings' ) ) {
+ $WCCSP_Settings = new \WCCSP_Settings();
+ }
+
+ $settings = $WCCSP_Settings->get_settings();
+
+ if ( isset( $settings[ 'enabled' ] ) && 'on' === $settings[ 'enabled' ] ) {
+ $enabled = true;
+ }
+
+ return $enabled;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -195,3 +328,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return apply_filters( 'wcsc_site_screenshot_url', $screenshot_url );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Sort arrays by the year
+ *
+ * @param array $site_a
+ * @param array $site_b
+ *
+ * @return int
+ */
+function sort_sites_by_year( $site_a, $site_b ) {
+ if ( $site_a[ 'year' ] === $site_b[ 'year' ] ) {
+ return 0;
+ }
+
+ return ( $site_a[ 'year' ] < $site_b[ 'year' ] ? 1 : -1 );
+}
</ins></span></pre>
</div>
</div>
</body>
</html>