Skip to content

Instantly share code, notes, and snippets.

@whyisjake
Created March 26, 2014 17:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save whyisjake/9789254 to your computer and use it in GitHub Desktop.
Save whyisjake/9789254 to your computer and use it in GitHub Desktop.
The contribute page diff.
Index: css/style.css
===================================================================
--- css/style.css (revision 141566)
+++ css/style.css (working copy)
@@ -4843,6 +4843,247 @@
border-left-color: #ffffff;
bottom: -10px;
}
+.panel {
+ margin-bottom: 20px;
+ background-color: #ffffff;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+.panel-body {
+ padding: 15px;
+ *zoom: 1;
+}
+.panel-body:before,
+.panel-body:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.panel-body:after {
+ clear: both;
+}
+.panel > .list-group {
+ margin-bottom: 0;
+}
+.panel > .list-group .list-group-item {
+ border-width: 1px 0;
+}
+.panel > .list-group .list-group-item:first-child {
+ -webkit-border-top-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ border-top-right-radius: 0;
+ -webkit-border-top-left-radius: 0;
+ -moz-border-radius-topleft: 0;
+ border-top-left-radius: 0;
+}
+.panel > .list-group .list-group-item:last-child {
+ border-bottom: 0;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+ border-top-width: 0;
+}
+.panel > .table,
+.panel > .table-responsive > .table {
+ margin-bottom: 0;
+}
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive {
+ border-top: 1px solid #dddddd;
+}
+.panel > .table > tbody:first-child th,
+.panel > .table > tbody:first-child td {
+ border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+ border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+}
+.panel > .table-bordered > thead > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:last-child > th,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-bordered > thead > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+ border-bottom: 0;
+}
+.panel > .table-responsive {
+ border: 0;
+ margin-bottom: 0;
+}
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ -webkit-border-top-right-radius: 3px;
+ -moz-border-radius-topright: 3px;
+ border-top-right-radius: 3px;
+ -webkit-border-top-left-radius: 3px;
+ -moz-border-radius-topleft: 3px;
+ border-top-left-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+ color: inherit;
+}
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+ color: inherit;
+}
+.panel-title > a {
+ color: inherit;
+}
+.panel-footer {
+ padding: 10px 15px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #dddddd;
+ -webkit-border-bottom-right-radius: 3px;
+ -moz-border-radius-bottomright: 3px;
+ border-bottom-right-radius: 3px;
+ -webkit-border-bottom-left-radius: 3px;
+ -moz-border-radius-bottomleft: 3px;
+ border-bottom-left-radius: 3px;
+}
+.panel-group .panel {
+ margin-bottom: 0;
+ border-radius: 4px;
+ overflow: hidden;
+}
+.panel-group .panel + .panel {
+ margin-top: 5px;
+}
+.panel-group .panel-heading {
+ border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse .panel-body {
+ border-top: 1px solid #dddddd;
+}
+.panel-group .panel-footer {
+ border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+ border-bottom: 1px solid #dddddd;
+}
+.panel-default {
+ border-color: #dddddd;
+}
+.panel-default > .panel-heading {
+ color: #333333;
+ background-color: #f5f5f5;
+ border-color: #dddddd;
+}
+.panel-default > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #dddddd;
+}
+.panel-default > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #dddddd;
+}
+.panel-primary {
+ border-color: #000000;
+}
+.panel-primary > .panel-heading {
+ color: #ffffff;
+ background-color: #cccccc;
+ border-color: #000000;
+}
+.panel-primary > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #000000;
+}
+.panel-primary > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #000000;
+}
+.panel-success {
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #d6e9c6;
+}
+.panel-success > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #d6e9c6;
+}
+.panel-warning {
+ border-color: #fbeed5;
+}
+.panel-warning > .panel-heading {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #fbeed5;
+}
+.panel-warning > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #fbeed5;
+}
+.panel-warning > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #fbeed5;
+}
+.panel-danger {
+ border-color: #eed3d7;
+}
+.panel-danger > .panel-heading {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+.panel-danger > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #eed3d7;
+}
+.panel-danger > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #eed3d7;
+}
+.panel-info {
+ border-color: #bce8f1;
+}
+.panel-info > .panel-heading {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+.panel-info > .panel-heading + .panel-collapse .panel-body {
+ border-top-color: #bce8f1;
+}
+.panel-info > .panel-footer + .panel-collapse .panel-body {
+ border-bottom-color: #bce8f1;
+}
.thumbnails {
margin-left: -20px;
list-style: none;
@@ -5488,6 +5729,38 @@
background: #E4F4F9;
color: #ed1c24 !important;
}
+input.parsley-error,
+select.parsley-error,
+textarea.parsley-error,
+div.parsley-error.mce-panel {
+ color: #B94A48;
+ background: #F2DEDE;
+ border: 1px solid #EED3D7 !important;
+}
+.parsley-errors-list {
+ margin: 2px 0 3px 0;
+ padding: 0;
+ list-style-type: none;
+ font-size: 0.9em;
+ line-height: 0.9em;
+ opacity: 0;
+ -moz-opacity: 0;
+ -webkit-opacity: 0;
+ color: #B94A48;
+ transition: all 0.3s ease-in;
+ -o-transition: all 0.3s ease-in;
+ -ms-transition: all 0.3s ease-in-;
+ -moz-transition: all 0.3s ease-in;
+ -webkit-transition: all 0.3s ease-in;
+}
+.parsley-errors-list.post_content_errors {
+ position: absolute;
+ bottom: 26px;
+ left: 10px;
+}
+.parsley-errors-list.filled {
+ opacity: 1;
+}
.waist {
background-image: url('../img/blue_bg1.jpg');
border-bottom: 2px solid #DBDBDB;
@@ -6802,6 +7075,11 @@
width: 50%;
float: left;
}
+div.parts.row,
+div.tools.row {
+ width: 100%;
+ float: none;
+}
.description {
text-align: right;
}
@@ -9126,6 +9404,20 @@
.top-navigation-wrapper .nav {
margin-bottom: 0;
}
+.login-wrapper {
+ position: absolute;
+ left: 245px;
+ top: -8px;
+ color: #005d8f;
+ font-size: 18px;
+ font-weight: bold;
+}
+.login-wrapper a {
+ display: inline-block;
+}
+.login-wrapper a:hover {
+ text-decoration: underline !important;
+}
.main-header {
padding: 15px 0 10px;
border-top: 1px solid #ed1c24;
@@ -9188,7 +9480,8 @@
color: #ed1c24;
}
.main-header .additional-content .search-make {
- float: right;
+ position: absolute;
+ left: -695px;
margin: 0;
}
.main-header .additional-content .search-make input[type="text"] {
@@ -9265,7 +9558,7 @@
font-size: 20px !important;
font-weight: bold;
position: relative;
- top: 92px;
+ top: 65px;
}
.primary-navigation ul > li {
margin-right: 18px;
@@ -12049,3 +12342,125 @@
aside.pull-quote.pull-quote-1.pull-left img {
margin-right: 10px;
}
+.page-template-page-contribute-project-php .title {
+ width: 98%;
+ margin-bottom: 10px;
+}
+.page-template-page-contribute-project-php .thumbnail {
+ cursor: pointer;
+}
+.page-template-page-contribute-project-php .thumbnail.add {
+ width: 80px;
+ height: 38px;
+ text-align: center;
+ padding: 25px 5px;
+ opacity: .5;
+ border-style: dashed;
+ font-size: 14px;
+}
+.page-template-page-contribute-project-php #step_content {
+ width: 98%;
+ min-height: 100px;
+}
+.page-template-page-contribute-project-php .wp-core-ui .button {
+ color: #555 !important;
+}
+.page-template-page-contribute-project-php .wp-core-ui .button:hover {
+ background-color: #fafafa !important;
+ color: #222 !important;
+}
+.page-template-page-contribute-project-php .wp-editor-container {
+ border: 1px solid #dedede;
+}
+.page-template-page-contribute-project-php .projects-masthead {
+ padding-top: 0;
+}
+.btn-file {
+ overflow: hidden;
+ position: relative;
+ vertical-align: middle;
+}
+.btn-file > input {
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 0;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ transform: translate(-300px, 0) scale(4);
+ font-size: 23px;
+ height: 100%;
+ direction: ltr;
+ cursor: pointer;
+}
+.fileinput {
+ margin-bottom: 9px;
+ display: inline-block;
+}
+.fileinput .form-control {
+ padding-top: 7px;
+ padding-bottom: 5px;
+ display: inline-block;
+ margin-bottom: 0px;
+ vertical-align: middle;
+ cursor: text;
+}
+.fileinput .thumbnail {
+ overflow: hidden;
+ display: inline-block;
+ margin-bottom: 5px;
+ vertical-align: middle;
+ text-align: center;
+}
+.fileinput .thumbnail > img {
+ max-height: 100%;
+}
+.fileinput .btn {
+ vertical-align: middle;
+}
+.fileinput-exists .fileinput-new,
+.fileinput-new .fileinput-exists {
+ display: none;
+}
+.fileinput-inline .fileinput-controls {
+ display: inline;
+}
+.fileinput-filename {
+ vertical-align: middle;
+ display: inline-block;
+ overflow: hidden;
+}
+.form-control .fileinput-filename {
+ vertical-align: bottom;
+}
+.fileinput-new .input-group .btn-file {
+ border-radius: 0 4px 4px 0;
+}
+.fileinput-new .input-group .btn-file.btn-xs,
+.fileinput-new .input-group .btn-file.btn-sm {
+ border-radius: 0 3px 3px 0;
+}
+.fileinput-new .input-group .btn-file.btn-lg {
+ border-radius: 0 6px 6px 0;
+}
+.form-group.has-warning .fileinput .fileinput-preview {
+ color: #8a6d3b;
+}
+.form-group.has-warning .fileinput .thumbnail {
+ border-color: #faebcc;
+}
+.form-group.has-error .fileinput .fileinput-preview {
+ color: #a94442;
+}
+.form-group.has-error .fileinput .thumbnail {
+ border-color: #ebccd1;
+}
+.form-group.has-success .fileinput .fileinput-preview {
+ color: #3c763d;
+}
+.form-group.has-success .fileinput .thumbnail {
+ border-color: #d6e9c6;
+}
+.input-group-addon:not(:first-child) {
+ border-left: 0;
+}
Index: functions.php
===================================================================
--- functions.php (revision 141566)
+++ functions.php (working copy)
@@ -1,4 +1,4 @@
-<?php
+<?php
/*
@@ -29,6 +29,9 @@
// 2. WordPress.com VIP Hosting Stuff
include_once dirname( __FILE__ ) . '/includes/vip.php';
+// Load Gigya!
+include_once dirname( __FILE__ ) . '/includes/gigya/gigya.php';
+
// 3. NUMBERED PAGE NAVIGATION
include_once dirname( __FILE__ ) . '/includes/pagenavi.php';
@@ -47,7 +50,7 @@
// 8. Page 2 - Custom Post Type
include_once dirname( __FILE__ ) . '/includes/page_2.php';
-// 9. YouTube Embed Function
+// 9. YouTube Embed Function
include_once dirname( __FILE__ ) . '/includes/youtube.php';
// 10. Contribute Function
@@ -156,5 +159,7 @@
// 45. Related Content Blocks
include_once dirname( __FILE__ ) . '/includes/related.php';
+// 45. Contribute Form
+include_once dirname( __FILE__ ) . '/includes/contribute/contribute.php';
?>
\ No newline at end of file
Index: includes/contribute/contribute.php
===================================================================
--- includes/contribute/contribute.php (revision 0)
+++ includes/contribute/contribute.php (working copy)
@@ -0,0 +1,389 @@
+<?php
+
+/**
+ * Contribute!
+ *
+ * A class that will allow for forms to contribute posts.
+ *
+ * @since Quantrons
+ */
+
+/**
+ * The guts.
+ *
+ * This little guy controls and loads all that is Gigya.
+ * The namespace for this class is Make because in the future this will be expanded to other make websites.
+ *
+ * @since Quantrons
+ */
+class Make_Contribute {
+
+ /**
+ * THE CONSTRUCT.
+ *
+ * All Hooks and Filter here.
+ * Anything else that needs to run when the class is instantiated, place them here.
+ * Maybe you'll get a cake if you do.
+ *
+ * @return void
+ * @since Quantrons
+ */
+ public function __construct() {
+ add_action( 'wp_enqueue_scripts', array( $this, 'load_resources' ), 30 );
+
+ // Process our ajax requests. We need ajax processing for both logged in and logged out users.
+ // Since our login may be used by users logged into WordPress, we'll need the second option to run ajax requests.
+ add_action( 'wp_ajax_nopriv_contribute_post', array( $this, 'contribute_post' ) );
+ add_action( 'wp_ajax_contribute_post', array( $this, 'contribute_post' ) );
+
+ // Add the steps ajax actions.
+ add_action( 'wp_ajax_nopriv_add_steps', array( $this, 'add_steps' ) );
+ add_action( 'wp_ajax_add_steps', array( $this, 'add_steps' ) );
+
+ // Add the tools ajax actions.
+ add_action( 'wp_ajax_nopriv_add_tools', array( $this, 'add_tools' ) );
+ add_action( 'wp_ajax_add_tools', array( $this, 'add_tools' ) );
+
+ // Add the parts ajax actions.
+ add_action( 'wp_ajax_nopriv_add_parts', array( $this, 'add_parts' ) );
+ add_action( 'wp_ajax_add_parts', array( $this, 'add_parts' ) );
+
+ // Get the steps for a project.
+ add_action( 'wp_ajax_nopriv_get_steps', array( $this, 'get_steps' ) );
+ add_action( 'wp_ajax_get_steps', array( $this, 'get_steps' ) );
+
+ // Get the steps for a project.
+ add_action( 'wp_ajax_nopriv_get_steps_list', array( $this, 'get_steps_list' ) );
+ add_action( 'wp_ajax_get_steps_list', array( $this, 'get_steps_list' ) );
+ }
+
+ /**
+ * Let's add all of our resouces to make our magic happen.
+ * Any scripts we should include in the footer or else things will conflict due to how we have to load the socialize API file... #facepalm
+ *
+ * @return void
+ * @since Quantrons
+ */
+ public function load_resources() {
+
+ // JavaScript
+ wp_enqueue_script( 'parseley-js', get_stylesheet_directory_uri() . '/js/parsley.min.js', array( 'jquery' ), '2.0', true );
+ wp_enqueue_script( 'bootstrap-file-input', get_stylesheet_directory_uri() . '/js/bootstrap.file-input.min.js', array( 'jquery' ), '1.0', true );
+ wp_enqueue_script( 'make-contribute', get_stylesheet_directory_uri() . '/includes/contribute/js/contribute.js', array( 'jquery' ), '1.0', true );
+ wp_enqueue_script( 'make-contrib-ui', get_stylesheet_directory_uri() . '/includes/contribute/js/contrib-ui.js', array( 'jquery' ), '1.0', true );
+ }
+
+ /**
+ * Uploads images and documents.
+ * @param Integer $post_id The post ID we are adding the image to
+ * @param Array $files An array of files being uploaded (captured via $_FILES)
+ * @return Array
+ */
+ private function upload_files( $post_id, $files ) {
+
+ if ( ! function_exists( 'wp_handle_upload' ) )
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
+
+ if ( ! function_exists( 'wp_crop_image' ) )
+ require_once( ABSPATH . 'wp-admin/includes/image.php' );
+
+ // And array of allowed file types to be uploaded
+ $allowed_file_types = array(
+ 'jpg',
+ 'jpeg',
+ 'png',
+ 'gif',
+ );
+
+ // Setup the image array
+ $images = array();
+
+ // Loop through all of our uploaded files
+ foreach ( $files as $name => $values ) {
+
+ $file_type = wp_check_filetype( $values['name'] );
+ // Ensure the file type being passed matches the field type (ie. photo uploads should only allow photos and documents as documents)
+ if ( ! in_array( $file_type['ext'], $allowed_file_types ) )
+ return;
+
+ $overrides = array( 'test_form' => false );
+ $file = wp_handle_upload( $values, $overrides );
+
+ // Check if there were any errors
+ if ( isset( $file['error'] ) ) {
+ // TODO: update this to trigger a wp_error instead...
+ return $results['error'] = $file['error'];
+ exit();
+ }
+
+ $attachment = array(
+ 'guid' => $file['url'],
+ 'post_mime_type' => $file['type'],
+ 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $values['name'] ) ),
+ 'post_content' => '',
+ 'post_status' => 'inherit'
+ );
+ $attachment_id = wp_insert_attachment( $attachment, $file['file'], $post_id );
+
+ $attachment_data = wp_generate_attachment_metadata( $attachment_id, $values['name'] );
+
+ wp_update_attachment_metadata( $attachment_id, $attachment_data );
+
+ // Attach as a featured image if we are uploading the project photo
+ if ( $name === 'file' )
+ update_post_meta( $post_id, '_thumbnail_id', $attachment_id );
+
+ // Get the upload directory
+ $wp_upload_dir = wp_upload_dir();
+ $thumb = image_make_intermediate_size( $file['file'], 500, 500 );
+
+ // Due to legacy code, we need to pass two empty fields.
+ // TODO: Update the image handling in make_magazine_projects_build_step_data() to allow a varying number of images, fixing the need to pass two empty values.
+ $images[ sanitize_key( $name ) ] = array(
+ esc_url( $file['url'] ),
+ '',
+ '',
+ );
+ }
+
+ return $images;
+ }
+
+
+ /**
+ * Allows us to determine if the contributing author is a WordPress user or Guest Author based on the ID passed and return their username
+ * @return string | false
+ *
+ * @since Robot House
+ */
+ public function get_author_name( $id ) {
+ // Gigya always passes IDs as long strings, if it's an integer, then we have a WP user
+ if ( ctype_digit( $id ) ) { // Ensure the string contains only numbers.....
+ $author_name = get_the_author_meta( 'user_login', absint( $id ) );
+
+ return array( 'post_author' => $id, 'login_name' => $author_name );
+ } else {
+ global $make_gigya;
+
+ // We'll need to check for this gigya user and return their information
+ $guest_author = $make_gigya->search_for_maker_by_id( $id );
+
+ if ( $guest_author ) {
+ return array( 'login_name' => $guest_author[0]->post_name );
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Build a row of photos based on uploaded images.
+ */
+ public function image_rows( $id ) {
+ $output = '';
+ $media = get_attached_media( 'image', $id );
+ $rows = array_chunk( $media, 4 );
+ foreach ( $rows as $images ) {
+ $output .= '<div class="row">';
+ foreach ($images as $image) {
+ $output .= '<div class="span2">';
+ $output .= '<img src="' . esc_url( wpcom_vip_get_resized_remote_image_url( $image->guid, '130', '170' ) ) . '" alt="' . esc_attr( $image->post_title ) . '">';
+ $output .= '</div>';
+ }
+ $output .= '</div>';
+ }
+ return $output;
+ }
+
+ /**
+ * Take the form data, and add a post/project.
+ *
+ * @return json
+ *
+ * @since Quantrons
+ */
+ public function contribute_post() {
+ global $coauthors_plus;
+
+ // Check our nonce and make sure it's correct
+ if ( ! wp_verify_nonce( $_POST['contribute_post'], 'contribute_post_nonce' ) )
+ die( 'We weren\'t able to verify that nonce...' );
+
+ // Get the author ID
+ $author_name = $this->get_author_name( $_POST['user_id'] );
+
+ // Make sure an author was returned
+ if ( ! $author_name )
+ die( json_encode( 'ERROR: AUTHOR NOT FOUND' ) );
+
+ $allowed_post_types = array(
+ 'post',
+ 'projects'
+ );
+
+ // Setup the post variables yo.
+ $post = array(
+ 'post_status' => 'draft',
+ 'post_title' => ( isset( $_POST['post_title'] ) ) ? sanitize_text_field( $_POST['post_title'] ) : '',
+ 'post_name' => ( isset( $_POST['post_title'] ) ) ? sanitize_title( $_POST['post_title'] ) : '',
+ 'post_content' => ( isset( $_POST['post_content'] ) ) ? wp_kses_post( $_POST['post_content'] ) : '',
+ 'post_category' => ( isset( $_POST['cat'] ) ) ? array( absint( $_POST['cat'] ) ) : '',
+ 'post_type' => ( isset( $_POST['post_type'] ) && in_array( $_POST['post_type'], $allowed_post_types ) ) ? sanitize_text_field( $_POST['post_type'] ) : 'post',
+ 'post_author' => ( isset( $author_id['post_author'] ) ) ? absint( $author_id['post_author'] ) : 604631,
+ );
+
+ // Insert the post
+ $pid = wp_insert_post( $post );
+
+ // Add to CoAuthors Plus (for all users, not just Guest Authors)
+ $author_set = $coauthors_plus->add_coauthors( absint( $pid ), array( $author_name['login_name'] ) );
+
+ // Upload the files
+ $this->upload_files( $pid, $_FILES );
+
+ // Get the newly created post
+ $post = get_post( $pid );
+
+ $post->media = $this->image_rows( $pid );
+
+ // Send back the Post as JSON
+ die( json_encode( $post ) );
+
+ }
+
+ /**
+ * Take the form data, and add a post/project.
+ *
+ * @return json
+ *
+ * @since Quantrons
+ */
+ public function add_steps() {
+
+ ////////////////////
+ // Check our nonce and make sure it's correct
+ if ( ! wp_verify_nonce( $_POST['contribute_steps_nonce'], 'contribute_steps_nonce' ) )
+ die( 'We weren\'t able to verify that nonce...' );
+
+ ////////////////////
+ // Upload the files
+ $files = $this->upload_files( absint( $_POST['post_ID'] ), $_FILES );
+
+ ////////////////////
+ // Merge the files array and the $_POST array.
+ $merged = array_merge( $_POST, $files );
+
+ //////////////////////////
+ // STEPS
+ $step_object = make_magazine_projects_build_step_data( $merged );
+
+ // Update our post meta for Steps if any exist
+ update_post_meta( absint( $_POST['post_ID'] ), 'Steps', $step_object );
+
+ // Send back the
+ die( json_encode( array( 'post_id' => $_POST['post_ID'] ) ) );
+
+ }
+
+ public function add_tools() {
+
+ ////////////////////
+ // Check our nonce and make sure it's correct
+ if ( ! wp_verify_nonce( $_POST['contribute_tools'], 'contribute_tools' ) )
+ die( 'We weren\'t able to verify that nonce...' );
+
+ ////////////////////
+ // Build the tools object
+ $tools_object = make_magazine_projects_build_tools_data( $_POST );
+
+ ////////////////////
+ // Update our post meta for Steps. Unlike Steps and Tools, we want one meta key.
+ update_post_meta( absint( absint( $_POST['post_ID'] ) ), 'Tools', $tools_object );
+
+ ////////////////////
+ // Let's get the tools out of the database.
+ $tools = get_post_meta( absint( $_POST['post_ID'] ), 'Tools' );
+
+ ////////////////////
+ // Send back the tools object
+ die( make_projects_tools( $tools ) );
+
+ }
+
+ public function add_parts() {
+
+ ////////////////////
+ // Check our nonce and make sure it's correct
+ if ( ! wp_verify_nonce( $_POST['contribute_parts'], 'contribute_parts' ) )
+ die( 'We weren\'t able to verify that nonce...' );
+
+ ///////////////////////
+ // PARTS
+ $parts = make_magazine_projects_build_parts_data( $_POST );
+
+ $meta_obj = array();
+ foreach ( $parts as $part ) {
+ $meta_obj[] = add_post_meta( absint( $_POST['post_ID'] ), 'parts', $part );
+ }
+
+ $parts = get_post_meta( absint( $_POST['post_ID'] ), 'parts' );
+
+ ////////////////////
+ // Send back the tools object
+ die( make_projects_parts( $parts ) );
+
+ }
+
+ /**
+ * Get the steps HTML
+ */
+ public function get_steps() {
+
+ ////////////////////
+ // Check our nonce and make sure it's correct
+ if ( ! wp_verify_nonce( $_POST['get_steps'], 'get_steps' ) )
+ die( 'We weren\'t able to verify that nonce...' );
+
+ ///////////////////////
+ // Get the steps.
+ $steps = get_post_custom_values( 'Steps', absint( $_POST['post_ID'] ) );
+
+ ///////////////////////
+ // HTMLify the steps.
+ make_projects_steps( $steps );
+
+ ////////////////////
+ // We are done here right?
+ die();
+
+ }
+
+ /**
+ * Get the steps HTML
+ */
+ public function get_steps_list() {
+
+ ////////////////////
+ // Check our nonce and make sure it's correct
+ if ( ! wp_verify_nonce( $_POST['get_steps'], 'get_steps' ) )
+ die( 'We weren\'t able to verify that nonce...' );
+
+ ///////////////////////
+ // Get the steps.
+ $steps = get_post_custom_values( 'Steps', absint( $_POST['post_ID'] ) );
+
+ ///////////////////////
+ // HTMLify the steps.
+ make_projects_steps_list( $steps );
+
+ ////////////////////
+ // Done here right?
+ die();
+
+ }
+
+
+
+}
+
+$make_contribute = new Make_Contribute();
Index: includes/contribute/js/contrib-ui.js
===================================================================
--- includes/contribute/js/contrib-ui.js (revision 0)
+++ includes/contribute/js/contrib-ui.js (working copy)
@@ -0,0 +1,207 @@
+/**
+ * This file handles all the UI stuff like adding new fields
+ */
+
+jQuery( document ).ready( function( $ ) {
+ // Trigger the step addition when we click the "Add Step" button
+ $( '.btn.add-step' ).click( function( e ) {
+ e.preventDefault();
+
+ make_contribute_add_field( 'steps' );
+ });
+
+ // Trigger the parts addition when we click the "Add Parts" button
+ $( '.btn.add-part' ).click( function( e ) {
+ e.preventDefault();
+
+ make_contribute_add_field( 'parts' );
+ });
+
+ // Trigger the tools addition when we click the "Add Parts" button
+ $( '.btn.add-tool' ).click( function( e ) {
+ e.preventDefault();
+
+ make_contribute_add_field( 'tools' );
+ });
+});
+
+
+/**
+ * Adds new elements of our contributor form to the page
+ * @param strong fields the type of field we are dealing with
+ * @return void
+ */
+function make_contribute_add_field( fields ) {
+ // Count the number of fields we have and increment
+ var count = jQuery( 'input[name="total-' + fields + '"]' ).val();
+ count++;
+
+ // Get the template
+ var template = jQuery( '#' + fields + '-template' ).html();
+
+ // Run a find and replace on the template to add our field count variable
+ temp = template.replace( new RegExp( '##count##', 'g' ), count );
+
+ // Append the new template to our list of items
+ jQuery( '.' + fields + '-list' ).append( temp );
+
+ // Update our item count
+ jQuery( 'input[name="total-' + fields + '"]' ).val( count );
+
+ // Make sure we trigger the removal event of the field after its been added
+ make_contribute_remove_field( fields );
+}
+
+
+/**
+ * Removes an element from the contribute form
+ * @param string fields the type of field we are dealing with
+ * @return void
+ */
+function make_contribute_remove_field( fields ) {
+
+ // The field variable is passed as a plural, let's remove make singular
+ field = fields.substring( 0, fields.length - 1 );
+
+ // Trigger the field removal
+ jQuery( '.btn.remove-' + field ).click( function( e ) {
+ e.preventDefault();
+
+ // Remove the element
+ jQuery( this ).parents( '.' + field + '.row' ).remove();
+
+ // Make sure we reiterate over our steps and update their count. This will allow users to remove steps in-between steps
+ make_contribute_get_update_fields( fields );
+ });
+}
+
+
+/**
+ * A controller function to determine what function we need to use based on the form field type
+ * @param string fields The type of field we are dealing with
+ * @return void
+ */
+function make_contribute_get_update_fields( fields ) {
+ if ( fields === 'steps' ) {
+ make_contribute_update_steps();
+ } else if ( fields === 'parts' ) {
+ make_contribute_update_parts();
+ } else if ( fields === 'tools' ) {
+ make_contribute_update_tools();
+ }
+
+}
+
+
+/**
+ * Updates the step form fields to ensure they are all labeled with the right number
+ * @return void
+ */
+function make_contribute_update_steps() {
+ var i = 1;
+ jQuery( '.step.row' ).each( function() {
+ var step = jQuery(this);
+
+ // Update the step number title
+ step.find( '.step-title' ).html( 'Step ' + i );
+
+ // Update the step number
+ step.find( 'input[type="hidden"].step-number' ).attr({
+ 'name' : 'step-number-' + i,
+ 'value' : i
+ });
+
+ // Update the step image
+ step.find( 'input[type="file"].step-file' ).attr( 'name', 'step-images-' + i + '[]' );
+
+ // Update the step title
+ step.find( 'input[type="text"].title' ).attr( 'name', 'step-title-' + i );
+
+ // Update the step lines
+ step.find( 'textarea.step_content' ).attr( 'name', 'step-lines-' + i + '[]' );
+
+ i++;
+ });
+
+ // Update the total step count
+ jQuery( '#add-steps' ).find( 'input[type="hidden"][name="total-steps"]' ).val( i - 1 );
+}
+
+
+/**
+ * Updates the part form fields to ensure they are all labeled with the right number
+ * @return void
+ */
+function make_contribute_update_parts() {
+ var i = 1;
+ jQuery( '.part.row' ).each( function() {
+ var part = jQuery(this);
+
+ // Update the part number title
+ part.find( '.part-title' ).html( 'Part ' + i );
+
+ // Update the part number
+ part.find( 'input[type="hidden"].part-number' ).attr({
+ 'name' : 'part-number-' + i,
+ 'value' : i
+ });
+
+ // Update the parts notes count
+ part.find( 'input[type="hidden"].parts-notes' ).attr( 'name', 'parts-notes-' + i );
+
+ // Update the part name
+ part.find( 'input[type="text"].parts-name' ).attr( 'name', 'parts-name-' + i );
+
+ // Update the parts quantity
+ part.find( 'input[type="number"].parts-qty' ).attr( 'name', 'parts-qty-' + i );
+
+ // Update the parts url
+ part.find( 'input[type="url"].parts-url' ).attr( 'name', 'parts-url-' + i );
+
+ // Update the parts type
+ part.find( 'input[type="text"].parts-type' ).attr( 'name', 'parts-type-' + i );
+
+ i++;
+ });
+
+ // Update the total step count
+ jQuery( '#add-parts' ).find( 'input[type="hidden"][name="total-parts"]' ).val( i - 1 );
+}
+
+
+/**
+ * Updates the tools form fields to ensure they are all labeled with the right number
+ * @return void
+ */
+function make_contribute_update_tools() {
+ var i = 1;
+ jQuery( '.tool.row' ).each( function() {
+ var tool = jQuery(this);
+
+ // Update the tools number title
+ tool.find( '.tool-title' ).html( 'Tool ' + i );
+
+ // Update the tools number
+ tool.find( 'input[type="hidden"].tools-number' ).attr({
+ 'name' : 'tool-number-' + i,
+ 'value' : i
+ });
+
+ // Update the tools thumb name
+ tool.find( 'input[type="hidden"].tools-thumb' ).attr( 'name', 'tools-thumb-' + i );
+
+ // Update the tool notes name
+ tool.find( 'input[type="hidden"].tools-notes' ).attr( 'name', 'tools-notes-' + i );
+
+ // Update the tool name
+ tool.find( 'input[type="text"].tools-name' ).attr( 'name', 'tools-name-' + i );
+
+ // Update the tool url
+ tool.find( 'input[type="url"].tools-url' ).attr( 'name', 'tools-url-' + i );
+
+ i++;
+ });
+
+ // Update the total step count
+ jQuery( '#add-tools' ).find( 'input[type="hidden"][name="total-tools"]' ).val( i - 1 );
+}
\ No newline at end of file
Index: includes/contribute/js/contribute.js
===================================================================
--- includes/contribute/js/contribute.js (revision 0)
+++ includes/contribute/js/contribute.js (working copy)
@@ -0,0 +1,381 @@
+jQuery( document ).ready( function( $ ) {
+
+ // Load the nifty file input styling for Bootstrap
+ $('.file-inputs').bootstrapFileInput();
+
+ // Let's hide all of the steps.
+ $( '.contribute-form-steps, .contribute-form-parts, .contribute-form-tools' ).hide();
+
+ // Init our form validation
+ $( '.validate-form' ).parsley();
+
+ // On post creation, we need a way to tell if we are creating a project or post.
+ // The below will help us set a variable accessed in the submission of the form.
+ var make_contribute_post_type = '';
+ $( '.submit-review' ).click( function() {
+ make_contribute_post_type = $( this ).data( 'type' );
+ });
+
+ // Handle the AJAX for saving the first stage of the post. The rest will be over Backbone.
+ $( '#add-post-content' ).submit( function( e ) {
+
+ // Prevent the button from sending the form.
+ e.preventDefault();
+
+ // Validate that we our form has passed our preliminary check.
+ var check_form = $( this ).parsley( 'validate' );
+ if ( ! check_form.validationResult )
+ return;
+
+ // Disable the inputs.
+ make_contribute_input_disabler( 'contribute-form' );
+
+ // Hide the form
+ make_contribute_close_forms();
+
+ // Add the loading bar
+ make_contribute_loading_screen();
+
+ // Let's hide this, and bring it back when we have something to put in it.
+ $( '.parts-tools').hide();
+
+ // Save the form, pushing the data back.
+ tinyMCE.triggerSave();
+
+ // Setup the form.
+ var form = $( 'contribute-form' );
+
+ var data = new FormData( form );
+ jQuery.each( $( '#file' )[0].files, function( i, file ) {
+ // Inject the files
+ data.append( 'file-' + i, file );
+ });
+
+ // Append all of the other field.s
+ data.append( 'contribute_post', $( '.contribute-form #contribute_post' ).val() );
+ data.append( 'post_title', $( '.contribute-form #post_title' ).val() );
+ data.append( 'user_id', $( '.contribute-form #user_id' ).val() );
+ data.append( 'post_content', tinyMCE.activeEditor.getContent() );
+ data.append( 'cat', $( '.contribute-form #cat' ).val() );
+ data.append( 'post_type', make_contribute_post_type );
+ data.append( 'post_author', $( '.user_id' ).val() );
+ data.append( 'action', 'contribute_post' );
+
+ // Send off the AJAX request.
+ $.ajax({
+ url: make_gigya.ajax,
+ data: data,
+ cache: false,
+ contentType: false,
+ processData: false,
+ type: 'POST',
+ success: function( data ){
+ post_obj = JSON.parse( data );
+ make_contribute_post_filler( post_obj );
+
+ if ( make_contribute_post_type === 'projects' ) {
+ $( '.contribute-form-steps' ).slideDown();
+ $( '.post_ID' ).each( function() {
+ $( this ).val( post_obj.ID );
+ });
+ } else {
+ $( '.content-wrapper' ).append( '<div class="row"><div class="span12"><h3>Thanks for submitting a post! We\'ll review your contribution shortly.</h3></div></div>' );
+ }
+
+ make_contribute_remove_progress_bar();
+ }
+ });
+ });
+
+
+ // Save the steps.
+ $( '.submit-steps' ).on( 'click', function( e ) {
+
+ // Prevent the button from triggering
+ e.preventDefault();
+
+ // Disable the form inputs
+ make_contribute_input_disabler( 'contribute-form-steps' );
+
+ // Hide the steps.
+ make_contribute_close_forms();
+
+ // Added this for Cole...
+ make_contribute_loading_screen();
+
+ // Let's get the steps initialized.
+ var form = $( 'contribute-form-steps' );
+
+ // Grab all of the inputs.
+ var the_files = $( '.contribute-form-steps :file' );
+ var inputs = $( '.contribute-form-steps input:not(:file), .contribute-form-steps textarea' );
+
+ // New FormData
+ var data = new FormData( form );
+
+ // Setup the form object, just kinda playing with this as a source of data.
+ var form_obj = {};
+
+ // Add the add_steps action to the object.
+ form_obj.action = 'add_steps';
+
+ // Append each of the images to the object, giving each a name.
+ jQuery.each( the_files, function( i, file_obj ) {
+ jQuery.each( file_obj.files, function( key, file ) {
+ form_obj['step-images-' + ( i + 1 )] = file;
+ data.append( 'step-images-' + ( i + 1 ), file );
+ });
+ });
+
+ // Loop through all of the inputs, with the exception of the file ones, and add the to the form_object, and then to the data object.
+ inputs.each( function() {
+ form_obj[ this.name ] = $( this ).val();
+ data.append( this.name, $( this ).val() );
+ });
+
+ // Append the action to the data object.
+ data.append( 'action', 'add_steps' );
+
+ // Ajax request.
+ $.ajax({
+ url: make_gigya.ajax,
+ data: data,
+ cache: false,
+ contentType: false,
+ processData: false,
+ type: 'POST',
+ success: function( response ){
+ response = JSON.parse( response );
+ make_contribute_close_forms();
+ make_contribute_display_steps( response.post_id );
+ make_contribute_remove_progress_bar();
+ }
+ });
+ });
+
+ // Save the parts data
+ $( '.submit-parts' ).on( 'click', function( e ) {
+
+ // Prevent the button from trggering
+ e.preventDefault();
+
+ // Disable the inputs.
+ make_contribute_input_disabler( 'contribute-form-parts' );
+
+ // Hide the form
+ make_contribute_close_forms();
+
+ // Add the loading bar.
+ make_contribute_loading_screen();
+
+ // Let's start gathering values.
+ var inputs = $( '.contribute-form-parts :input' );
+
+ // Create the form array.
+ var form = {};
+ inputs.each( function() {
+ form[ this.name ] = $( this ).val();
+ });
+
+ // Add the action here.
+ form.action = 'add_parts';
+
+ // Make the ajax request with the form data.
+ $.ajax({
+ url: make_gigya.ajax,
+ data: form,
+ type: 'POST',
+ success: function( data ){
+ make_contribute_remove_progress_bar();
+ $( '.parts-tools' ).show();
+ $( '.parts-pane' ).empty();
+ $( '.parts-pane' ).html( data );
+ $( '.contribute-form-tools' ).slideDown();
+ }
+ });
+ });
+
+ // Save all of the tools data.
+ $( '.submit-tools' ).on( 'click', function( e ) {
+
+ // Prevent the button from triggering
+ e.preventDefault();
+
+ // Disable the inputs.
+ make_contribute_input_disabler( 'contribute-form-tools' );
+
+ // Grab all of the inputs
+ var inputs = $( '.contribute-form-tools :input' );
+
+ // Hide the form
+ make_contribute_close_forms();
+
+ // Add the loading bar.
+ make_contribute_loading_screen();
+
+ // Grab all of the form data.
+ var form = {};
+ inputs.each( function() {
+ form[ this.name ] = $( this ).val();
+ });
+
+ form.action = 'add_tools';
+
+ // Make the ajax request with the form data.
+ $.ajax({
+ url: make_gigya.ajax,
+ data: form,
+ type: 'POST',
+ success: function( data ){
+ make_contribute_remove_progress_bar();
+ $( '.tools-pane' ).empty();
+ $( '.tools-pane' ).html( data );
+ $( '#contribute-form-wrapper' ).html( '<h2>Thanks for your project submission!</h2><p>We\'ll review your project and contact you shortly</p>' );
+ }
+ });
+ });
+
+});
+
+
+/**
+ * Displays the steps
+ * @param int post_id The post ID we are going to be updating the form fields to
+ * @return void
+ */
+function make_contribute_display_steps( post_id ) {
+ var inputs = jQuery( '.contribute-form-get-steps :input' );
+
+ var form = {
+ action : 'get_steps',
+ post_ID: post_id,
+ };
+
+ inputs.each( function() {
+ form[ this.name ] = jQuery( this ).val();
+ });
+
+ // Make the ajax request with the form data.
+ jQuery.ajax({
+ url: make_gigya.ajax,
+ data: form,
+ type: 'POST',
+ success: function( data ){
+ jQuery( '.saving-progress' ).html('');
+ jQuery( '.steps-output' ).html( data );
+ }
+ });
+
+ form.action = '';
+ form.action = 'get_steps_list';
+
+ // Make the ajax request with the form data.
+ jQuery.ajax({
+ url: make_gigya.ajax,
+ data: form,
+ type: 'POST',
+ success: function( data ){
+ // Output the steps.
+ jQuery( '.steps-list-output' ).html( data );
+ // Display the parts form.
+ jQuery( '.contribute-form-parts' ).slideDown();
+ }
+ });
+
+}
+
+/**
+ * Take the saved data, and display it on the page.
+ * @param obj data The data being passed back from a post save so we can inject it into the preview window
+ * @return void
+ */
+function make_contribute_post_filler( data ) {
+ jQuery( '.post-title' ).html( data.post_title );
+ jQuery( '.post-content' ).html( data.post_content );
+ jQuery( '.post-content' ).append( data.media );
+}
+
+
+/**
+ * When the forms get saved, we'll disable all inputs
+ * @param string form The form name we wish to disable
+ * @return void
+ */
+function make_contribute_input_disabler( form ) {
+ // Grab the inputs
+ var inputs = jQuery( '.' + form + ' :input' );
+
+ // Disable them all.
+ inputs.each( function() {
+ jQuery( this ).prop( 'disabled', true );
+ });
+}
+
+
+/**
+ * Allows us to assign a Gigya ID so we can assign the coauthor to the contribute form
+ *
+ * @since
+ */
+function make_contribute_add_gigya_id( uid ) {
+ jQuery( 'input#user_id[type="hidden"]' ).val( uid );
+}
+
+
+/**
+ * Add some nifty loading text that is nerdy and fun
+ */
+function make_contribute_loading_screen() {
+ jQuery( '.post-holder' ).fadeIn();
+
+ var selector = '.saving-progress';
+ var time = 1500;
+ var text = [
+ '', // Pass an empty variable here as our random number goes from 1-10 and 0 will never be called
+ 'Adjusting tension bolts',
+ 'Calculating feeds & speeds',
+ 'Preheating print gun',
+ 'Zeroing out CNC Machine...',
+ 'Waiting for glue to dry',
+ 'Energizing primary coil...',
+ 'Reticulating splines',
+ 'Rendering mesh',
+ 'Slicing object layer',
+ 'Doing science'
+ ];
+ // Randomly get our text on each call (does it 1 - 10)
+ var index = Math.floor( ( Math.random() * 10 ) + 1 );
+
+ jQuery( selector ).html( '<h3 class="loading-text" style="text-align:center">' + text[ index ] + '</h3><div class="progress progress-striped active"><div class="bar" style="width: 100%;"></div></div>' );
+
+ // Change the loading text every 5 seconds
+ var interval_id = setInterval( function() {
+ // Reset the Index on each new interval
+ index = Math.floor( ( Math.random() * 10 ) + 1 );
+
+ // Only run as long as the loading text is present
+ if ( jQuery( '.loading-text' ).length === 1 ) {
+ jQuery( '.post-content' ).find( '.loading-text' ).text( text[ index ] + '...' );
+ } else {
+ clearInterval( interval_id );
+ }
+ }, time );
+}
+
+
+/**
+ * Removes the saving progress bar. Wunderbar!
+ * @return voide
+ */
+function make_contribute_remove_progress_bar() {
+ jQuery( '.saving-progress' ).html( '' );
+}
+
+
+/**
+ * Any time we save a form, we want to force all form fields to close while saving.
+ * @return void
+ */
+function make_contribute_close_forms() {
+ jQuery( '.contribute-form, .contribute-form-tools, .contribute-form-parts, .contribute-form-steps' ).slideUp();
+}
\ No newline at end of file
Index: includes/gigya/css/login.css
===================================================================
--- includes/gigya/css/login.css (revision 0)
+++ includes/gigya/css/login.css (working copy)
@@ -0,0 +1,252 @@
+.authentication { display:none; }
+.login-required {
+ height:300px;
+}
+.gigya-screen *, div.gigya-screen, .gigya-screen span, .gigya-screen a:hover, .gigya-screen a:visited, .gigya-screen a:link, .gigya-screen a:active {
+ border: none;
+ padding: 0px;
+ margin: 0px;
+ color: inherit;
+ text-decoration: none;
+ width: auto;
+ float: none;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ font-family: arial;
+ font-size: 12px;
+ color: #333333;
+ background: none;
+ text-align: left;
+ font-family: arial;
+}
+.gigya-screen a, .gigya-screen a:visited, .gigya-screen a:link, .gigya-screen a:active {
+ color: #0098ca;
+ text-decoration: underline;
+}
+.gigya-screen-set .gigya-screen {
+ margin: 0 auto;
+}
+.gigya-screen h2 {
+ font-size:16px;
+ color:#333;
+ font-weight: normal;
+}
+.gigya-screen a:hover {
+ text-decoration: none;
+}
+.gigya-screen h1 {
+ border-bottom: 1px solid #dbdbdb;
+ font-size: 16px;
+ font-family: Arial;
+ font-weight: bold;
+ color: #333;
+ padding-bottom: 5px;
+ margin-bottom: 45px;
+}
+.gigya-screen .gigya-layout-row {
+ width:100%;
+ zoom:1;
+ float:left;
+ display:block;
+}
+.gigya-screen .gigya-layout-cell {
+ float: left;
+ width: 50%;
+ margin-top: 1px;
+}
+.gigya-screen .gigya-layout-cell-right {
+ float: right;
+ width: 50%;
+}
+.gigya-screen select {
+ margin: 5px 0px;
+ padding: 5px;
+ border-radius: 5px;
+ background-color: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+}
+.gigya-screen option {
+ padding: 5px;
+}
+.gigya-screen option:hover {
+ background-color: #3593C1;
+}
+.gigya-screen .gigya-label {
+ display: block;
+ font-weight: bold;
+ font-size: 12px;
+}
+.gigya-screen .gigya-required {
+ color: red;
+ font-size: 12px;
+ font-weight: bold;
+ margin-left: 5px;
+}
+.gigya-input-text, .gigya-input-password, .gigya-textarea {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ width:100%;
+ margin: 5px 0px;
+ padding: 0 5px;
+ border-radius: 4px;
+ background-color: #FFFFFF;
+ border: 1px solid #CCCCCC;
+ color: #555555;
+ outline: none;
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05);
+ -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05);
+ -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05);
+}
+.gigya-textarea {
+ padding: 5px;
+}
+.gigya-input-text, .gigya-input-password, .gigya-screen select, .gigya-input-submit, .gigya-input-button {
+ height: 28px;
+}
+.gigya-input-text:focus, .gigya-input-password:focus, .gigya-textarea:focus, .gigya-screen select:focus {
+ border: 1px solid #87B9EA;
+ outline: none;
+}
+.gigya-input-text:focus, .gigya-input-password:focus, .gigya-textarea:focus {
+ box-shadow: 0 0 3px #50BADE, inset 0 1px 3px rgba(0, 0, 0, .05);
+ -webkit-box-shadow: 0 0 3px #50BADE, inset 0 1px 3px rgba(0, 0, 0, .05);
+ -moz-box-shadow: 0 0 3px #50BADE, inset 0 1px 3px rgba(0, 0, 0, .05);
+}
+.gigya-input-submit, .gigya-input-button {
+ text-align:center;
+ border: 1px solid #CECECE;
+ cursor: pointer;
+ padding: 0 12px;
+ color: #333333;
+ font-weight: bold;
+ border-radius: 4px;
+ line-height: 26px;
+ background: #F2F2F2;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#FFFFFF", endColorstr="#F2F2F2");
+ background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#F2F2F2));
+ background: -moz-linear-gradient(top, #FFFFFF, #F2F2F2);
+}
+.gigya-input-submit:hover, .gigya-input-button:hover {
+ background: #FFFFFF;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#F2F2F2", endColorstr="#FFFFFF");
+ background: -webkit-gradient(linear, left top, left bottom, from(#F2F2F2), to(#FFFFFF));
+ background: -moz-linear-gradient(top, #F2F2F2, #FFFFFF);
+}
+.gigya-input-radio, .gigya-input-checkbox {
+ margin-right: 5px;
+ vertical-align: bottom;
+ height: 14px;
+ width: 14px;
+}
+input.gigya-input-text, input.gigya-input-password {
+ display: block;
+}
+.gigya-screen .gigya-composite-control {
+ padding: 5px 13px 5px 10px;
+}
+.gigya-screen .gigya-composite-control-submit, .gigya-screen .gigya-composite-control-button, .gigya-screen .gigya-composite-control-form-error {
+ padding-bottom:5px;
+ padding-right:13px;
+}
+.gigya-composite-control-multi-choice .gigya-multi-choice-item, .gigya-composite-control-checkboxes .gigya-checkboxes-item {
+ padding-top:5px;
+}
+.gigya-composite-control-form-error {
+ padding-bottom:5px;
+ padding-right:5px;
+}
+.gigya-screen .gigya-composite-control-submit {
+ text-align: right;
+}
+.gigya-screen .gigya-composite-control-label {
+ display:block;
+}
+.gigya-clear {
+ clear: both;
+ overflow: hidden;
+ font-size:0px;
+}
+.gigya-composite-control-radio label {
+ font-weight: normal;
+}
+.gigya-composite-control-checkbox label {
+ font-weight: normal;
+}
+.gigya-screen .gigya-composite-control-checkbox {
+ padding: 5px 10px;
+}
+.gigya-screen .gigya-composite-control-checkbox label {
+ display: inline;
+ text-indent: 10px;
+ margin-left: 0px;
+}
+.gigya-screen .gigya-message {
+ font-size:14px;
+ color:#333;
+ display: block;
+ text-align: center;
+}
+.gigya-screen .gigya-error-display {
+ display:block;
+ visibility:hidden;
+}
+.gigya-screen .gigya-error-display-active {
+ display:block;
+ visibility:visible;
+}
+.gigya-screen .gigya-error-msg {
+ line-height: 14px;
+ color: #dd4b39;
+ font-size: 11px;
+ display:block;
+ font-weight:normal;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.gigya-screen .gigya-form-error-msg {
+ _height:26px;
+ min-height: 26px;
+ line-height: 14px;
+ color: #dd4b39;
+ font-size: 11px;
+ text-align: center;
+ margin-top: 1px;
+ border: 1px solid #FFDCCE;
+ display: block;
+ background: #FFE7E1;
+ font-weight:normal;
+ padding:5px;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.gigya-screen a.gigya-composite-control-link {
+ display:block;
+ padding-left:13px;
+}
+.gigya-screen .gigya-composite-control a.gigya-composite-control-link {
+ display:inline;
+ padding:0px;
+}
+.gigya-screen .gigya-composite-control-checkbox .gigya-label, .gigya-screen .gigya-composite-control-radio .gigya-label {
+ font-weight:normal;
+}
+.gigya-layout-footer {
+ text-align:right;
+ clear:both;
+}
+#gigya-login-screen .gigya-composite-control-social-login, #gigya-register-screen .gigya-composite-control-social-login {
+ border-right: 1px solid #CCC;
+ height: 125px;
+ width:285px;
+ padding: 13px 38px 0 0px;
+}
+#gigya-register-screen .gigya-composite-control-social-login {
+ height: 240px;
+}
\ No newline at end of file
Index: includes/gigya/gigya.php
===================================================================
--- includes/gigya/gigya.php (revision 0)
+++ includes/gigya/gigya.php (working copy)
@@ -0,0 +1,371 @@
+<?php
+
+/**
+ * Gigya!
+ *
+ * A class that will integrate all the social login fun stuff good times.
+ *
+ * Since Gigya is a third party system, we use their SDK to login users there and store information.
+ * All we need to do is log them into Gigya, and when all is successful and logged in, we'll create them
+ * a guest author account.
+ *
+ * This DOES NOT integrate into the WordPress Users database, nor can it due to VIP's global tables.
+ * To get around that, we'll process users into Guest Authors storing some custom information such as
+ * the Gigya UID and other stuff.
+ *
+ * @since SPRINT_NAME
+ */
+
+/**
+ * Set up some Constants
+ */
+// The main URL to the plugin directory
+define( 'MAKE_GIGYA_URL', get_template_directory_uri() . '/includes/gigya' );
+
+// Plugin version
+define( 'MAKE_GIGYA_VERSION', '0.1' );
+
+// Gigya Public Key
+define( 'MAKE_GIGYA_PUBLIC_KEY', sanitize_text_field( get_option( 'make_gigya_public_key' ) ) );
+
+// Gigya Private Key
+define( 'MAKE_GIGYA_PRIVATE_KEY', sanitize_text_field( get_option( 'make_gigya_private_key' ) ) );
+
+
+/**
+ * Load the Gigya Socialize SDK
+ *
+ * @since SPRINT_NAME
+ */
+if ( ! class_exists( 'GSRequest' ) )
+ include_once( 'includes/GSSDK.php' );
+
+/**
+ * Load our admin settings displayed in Settings > General
+ *
+ * @since SPRINT_NAME
+ */
+include_once( 'includes/settings.php' );
+
+// Set a default timezone
+date_default_timezone_set( 'America/Los_Angeles' );
+
+/**
+ * The guts.
+ *
+ * This little guy controls and loads all that is Gigya.
+ * The namespace for this class is Make because in the future this will be expanded to other make websites.
+ *
+ * @since SPRINT_NAME
+ */
+class Make_Gigya {
+
+ /**
+ * THE CONSTRUCT.
+ *
+ * All Hooks and Filter here.
+ * Anything else that needs to run when the class is instantiated, place them here.
+ * Maybe you'll get a cake if you do.
+ *
+ * @return void
+ * @since SPRINT_NAME
+ */
+ public function __construct() {
+
+ // Load our Gigya Social SDK and configurations
+ add_action( 'wp_head', array( $this, 'socialize_api' ), 999 );
+
+ // Load our resources
+ add_action( 'wp_enqueue_scripts', array( $this, 'load_resources' ), 30 );
+
+ // Process our ajax requests. We need ajax processing for both logged in and logged out users.
+ // Since our login may be used by users logged into WordPress, we'll need the second option to run ajax requests.
+ add_action( 'wp_ajax_nopriv_make_login_user', array( $this, 'user_login' ) );
+ add_action( 'wp_ajax_make_login_user', array( $this, 'user_login' ) );
+ }
+
+
+ /**
+ * Spits out the Gigya API for the socialize features.
+ * Sadly, we have to manually echo this to wp_head() because Gigya requires the socialize.js API key to be passed with options wrapped in the same script tag... lame sauce.
+ *
+ * Well only enable Facebook, Twitter and Google+ as social media providers, we'll also tell Gigya to end the users session with their service after 24 hours.
+ *
+ * @return html
+ * @since SPRINT_NAME
+ */
+ public function socialize_api() { ?>
+ <script src="http://cdn.gigya.com/JS/socialize.js?apikey=<?php echo urlencode( MAKE_GIGYA_PUBLIC_KEY ); ?>">{ enabledProviders: 'facebook,twitter,googleplus', sessionExpiration: 86400 }</script>
+ <?php }
+
+
+ /**
+ * Let's add all of our resouces to make our magic happen.
+ * Any scripts we should include in the footer or else things will conflict due to how we have to load the socialize API file... #facepalm
+ *
+ * @return void
+ * @since SPRINT_NAME
+ */
+ public function load_resources() {
+ // CSS
+ wp_enqueue_style( 'make-login', MAKE_GIGYA_URL . '/css/login.css', null, MAKE_GIGYA_VERSION );
+
+ // JavaScript
+ wp_enqueue_script( 'make-login', MAKE_GIGYA_URL . '/js/login.js', array( 'jquery' ), MAKE_GIGYA_VERSION, true );
+ wp_localize_script( 'make-login', 'make_gigya', array(
+ 'ajax' => esc_url( admin_url( 'admin-ajax.php' ) ),
+ 'loading' => 'Loading',
+ 'secure_it' => wp_create_nonce( 'ajax-nonce' ),
+ 'root_path' => esc_url( home_url( '/' ) ),
+ 'loggedin' => ( is_user_logged_in() ) ? 'true' : 'false',
+ ) );
+ }
+
+
+ /**
+ * Process' our Gigya interactions with the database. This method will take the info processed from the Gigya JS API and pass it through to either login or log out.
+ * These users are created and managed through the Tools > Guest Authors.
+ * @return json
+ *
+ * @since SPRINT_NAME
+ */
+ public function user_login() {
+
+ // Check our nonce and make sure it's correct
+ check_ajax_referer( 'ajax-nonce', 'nonce' );
+
+ $uid = $_POST['object']['UID'];
+
+ // Make sure some required fields are being passed first for security reasons
+ if ( isset( $_POST['request'] ) && $_POST['request'] == 'login' && wp_verify_nonce( $_POST['nonce'], 'ajax-nonce' ) ) {
+
+ // Before we continue we must verify this request is even a valid request from Gigya
+ if ( $this->verify_gigya_user( $uid, $_POST['object']['signatureTimestamp'], $_POST['object']['UIDSignature'] ) ) {
+
+ // Search for a maker and return them or else false.
+ $users = $this->search_for_maker( $uid, $_POST['object']['profile']['email'] );
+
+ // Check if a user already exists, if not we'll create one.
+ if ( $users ) {
+
+ // Check if the user existed and needs to have a Gigya ID added to the post meta
+ if ( isset( $users['add_guid'] ) && $users['add_guid'] ) {
+ update_post_meta( absint( $users[0]->ID ), 'cap-guid', sanitize_text_field( $uid ) );
+ }
+
+ // mark when the user logged in
+ update_post_meta( absint( $users[0]->ID ), 'cap-last_login', date( 'm/d/Y g:i:s a', time() ) );
+
+ $results = array(
+ 'loggedin' => true,
+ 'message' => 'Login Successful!',
+ 'maker' => absint( $users[0]->ID ),
+ );
+
+ // User didn't exist, let's make one.
+ } else {
+ // Pass our User Info sent from Gigya
+ $user = ( is_array( $_POST['object']['profile'] ) ) ? $_POST['object']['profile'] : '';
+
+ // Create the maker and return their ID
+ $maker_id = $this->create_maker( $user, $uid );
+
+ // Report our status to pass back to the modal window
+ if ( is_wp_error( $maker_id ) ) {
+ $results = array(
+ 'loggedin' => false,
+ 'message' => 'A user account could not be created. Please try again.',
+ 'user' => absint( $maker_id ),
+ );
+ } else {
+ $results = array(
+ 'loggedin' => true,
+ 'message' => 'User account created and logged in!',
+ 'maker' => absint( $maker_id ),
+ );
+ }
+ }
+ } else {
+ $results = array(
+ 'loggedin' => false,
+ 'message' => 'Request could not be validated. Please try again.',
+ );
+ }
+ } else {
+ $results = array(
+ 'loggedin' => false,
+ 'message' => 'Missing required parameters',
+ );
+ }
+
+ // Return our results and handle them in the Ajax callback
+ die( json_encode( $results ) );
+ }
+
+
+ /**
+ * Searches the Makers lists and locates a usr based on their UID
+ * @param string $uid The user ID from Gigya
+ * @return object
+ *
+ * @since SPRINT_NAME
+ */
+ private function search_for_maker( $uid, $email ) {
+ // Stick a hashed version of our usr ID for wp cache
+ $user_hash = md5( sanitize_text_field( $uid ) );
+
+ // Check if our makers are already cached.
+ $users = wp_cache_get( 'mf_user_' . $user_hash );
+
+ if ( $users == false ) {
+ $maker_guid_query = array(
+ 'post_type' => 'guest-author',
+ 'meta_key' => 'cap-guid',
+ 'meta_value' => sanitize_text_field( $uid ),
+ );
+ $users = new WP_Query( $maker_guid_query );
+
+ // If a user was not found with a Gigya ID, let's check for an email
+ if ( empty ( $users->posts ) ) {
+ $maker_email_query = array(
+ 'post_type' => 'guest-author',
+ 'meta_key' => 'cap-user_email',
+ 'meta_value' => sanitize_email( $email ),
+ );
+ $users = new WP_Query( $maker_email_query );
+
+ if ( ! empty( $users->posts ) )
+ $found_with_email = true;
+ }
+
+ // Save the results to the cache
+ wp_cache_set( 'mf_user_' . $user_hash, $users, '', 86400 ); // Since we are caching each user, might as well hold onto it for 24 hours.
+
+ if ( isset( $found_with_email ) && $found_with_email )
+ $users->posts['add_guid'] = true;
+ }
+
+ return $users->posts;
+ }
+
+
+ /**
+ * Searches the Makers lists and locates a usr based on their UID. While this is similar to search_for_maker(), this has a different use case.
+ * We need this function for checking against already logged in users. search_for_maker() method is used primarily for logging in and pairing Gigya to an account.
+ * This method will specifically find an existing user by Gigya ID and return their profile object
+ * @param string $uid The user ID from Gigya
+ * @return object
+ *
+ * @since SPRINT_NAME
+ */
+ public function search_for_maker_by_id( $uid ) {
+ // Stick a hashed version of our usr ID for wp cache
+ $user_hash = md5( sanitize_text_field( $uid ) );
+
+ // Check if our makers are already cached.
+ $user = wp_cache_get( 'mf_user_' . $user_hash );
+
+ if ( $user == false ) {
+ $maker_guid_query = array(
+ 'post_type' => 'guest-author',
+ 'meta_key' => 'cap-guid',
+ 'meta_value' => sanitize_text_field( $uid ),
+ );
+ $user = new WP_Query( $maker_guid_query );
+
+ // Save the results to the cache
+ wp_cache_set( 'mf_user_' . $user_hash, $user, '', 86400 ); // Since we are caching each user, might as well hold onto it for 24 hours.
+ }
+
+ return $user->posts;
+ }
+
+
+ /**
+ * Creates a new maker in the makers listings and returns the makers ID
+ * @param array $user The data passed from Gigya for use in the maker creation
+ * @return integer
+ */
+ private function create_maker( $user, $uid ) {
+
+ // Handle our user name
+ if ( ! empty( $user['firstName'] ) && ! empty( $user['lastName'] ) ) {
+ $user_name = $user['firstName'] . ' ' . $user['lastName'];
+ } elseif ( ! empty( $user['firstName'] ) && empty( $user['lastName'] ) ) {
+ $user_name = $user['firstName'];
+ } elseif ( empty( $user['firstName'] ) && empty( $user['lastName'] ) && ! empty( $user['nickname'] ) ) {
+ $user_name = $user['nickname'];
+ } else {
+ $user_name = 'Undefined Username';
+ }
+
+ // Our user doesn't exist, that means we need to sync them up, create a maker account and log them in.
+ $maker = array(
+ 'post_title' => sanitize_text_field( $user_name ),
+ 'post_status' => 'publish',
+ 'post_type' => 'guest-author',
+ 'post_name' => 'cap-' . sanitize_title( $user_name ),
+ );
+ $maker_id = wp_insert_post( $maker );
+
+ // If an error happens, we want to report that back before running any post meta updates.
+ if ( is_wp_error( $maker_id ) )
+ return 0;
+
+ // We'll want to add some custom fields. Let's do that.
+ // ****************************************************
+ // Date Created && last logged in
+ update_post_meta( absint( $maker_id ), 'cap-created', date( 'm/d/Y g:i:s a', time() ) );
+ update_post_meta( absint( $maker_id ), 'cap-last_login', date( 'm/d/Y g:i:s a', time() ) );
+
+ // Add the maker Display Name
+ $display_name = ( isset( $user_name ) && ! empty( $user_name ) ) ? $user_name : '';
+ update_post_meta( absint( $maker_id ), 'cap-display_name', sanitize_text_field( $display_name ) );
+
+ // Add the makers unique slug
+ $slug = preg_replace( '/[^A-Za-z0-9-]+/', '-', sanitize_text_field( $display_name ) );
+ update_post_meta( absint( $maker_id ), 'cap-user_login', sanitize_title_with_dashes( $slug ) );
+
+ // Add the makers first name
+ $first_name = ( isset( $user['firstName'] ) && ! empty( $user['firstName'] ) ) ? $user['firstName'] : '';
+ update_post_meta( absint( $maker_id ), 'cap-first_name', sanitize_text_field( $first_name ) );
+
+ // Add the makers last name
+ $last_name = ( isset( $user['lastName'] ) && ! empty( $user['lastName'] ) ) ? $user['lastName'] : '';
+ update_post_meta( absint( $maker_id ), 'cap-last_name', sanitize_text_field( $user['lastName'] ) );
+
+ // Add the makers email
+ $email = ( isset( $user['email'] ) && ! empty( $user['email'] ) ) ? $user['email'] : '';
+ update_post_meta( absint( $maker_id ), 'cap-user_email', sanitize_email( $email ) );
+
+ // Add the maker photo
+ $user_photo = ( isset( $user['photoURL'] ) && ! empty( $user['photoURL'] ) ) ? $user['photoURL'] : '';
+ update_post_meta( absint( $maker_id ), 'cap-photo_url', esc_url( $user_photo ) );
+
+ // Add the Maker Gigya ID
+ update_post_meta( absint( $maker_id ), 'cap-guid', sanitize_text_field( $uid ) );
+
+ return $maker_id;
+ }
+
+
+ /**
+ * We want to verify our Gigya interactions are valid.
+ * Since all interactions are via the JavaScript API, we'll need to verify these via AJAX
+ * @return json
+ *
+ * @since SPRINT_NAME
+ */
+ public function verify_gigya_user( $uid, $timestamp, $sig) {
+
+ // Validate the signature is authentic
+ $valid = SigUtils::validateUserSignature( sanitize_text_field( $uid ), absint( $timestamp ), MAKE_GIGYA_PRIVATE_KEY, sanitize_text_field( $sig ) );
+
+ if ( $valid ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+$make_gigya = new Make_Gigya();
Index: includes/gigya/images/default-profile-image.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: includes/gigya/images/default-profile-image.jpg
===================================================================
--- includes/gigya/images/default-profile-image.jpg (revision 141566)
+++ includes/gigya/images/default-profile-image.jpg (working copy)
Property changes on: includes/gigya/images/default-profile-image.jpg
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: includes/gigya/includes/GSSDK.php
===================================================================
--- includes/gigya/includes/GSSDK.php (revision 0)
+++ includes/gigya/includes/GSSDK.php (working copy)
@@ -0,0 +1,1083 @@
+<?php
+ /*
+* Copyright (C) 2013 Gigya, Inc.
+* Version 2.15.4
+*
+*
+* Gigya PHP SDK
+* @author Shachar Bar-David
+*/
+
+if (!function_exists('curl_init')) {
+ throw new Exception('Gigya.Socialize needs the CURL PHP extension.');
+}
+if (!function_exists('json_decode')) {
+ throw new Exception('Gigya.Socialize needs the JSON PHP extension.');
+}
+
+
+/**
+ * Gigya Socialize Exception
+ *
+ */
+class GSException extends Exception{
+
+ public $errorMessage;
+}
+
+/**
+ * Gigya Socialize Key Not Found Exception
+ *
+ */
+class GSKeyNotFoundException extends GSException{
+
+ public function __construct($key){
+ $this->errorMessage = "GSObject does not contain a value for key ".$key;
+ }
+}
+
+
+/**
+ * A Request to Gigya Socialize API
+ *
+ */
+
+class GSRequest {
+ private static $cafile;
+ const DEFAULT_API_DOMAIN = "gigya.com";
+ const version = "2.15.4";
+
+ private $host;
+ private $domain;
+ private $path;
+ private $traceLog = array();
+ protected $method;
+ private $proxy;
+ private $proxyType = CURLPROXY_HTTP;
+ private $proxyUserPass = ":";
+ private $curlArray = array();
+
+ private $apiKey;
+ private $userKey;
+ private $secretKey;
+ private $params; //GSObject
+ private $useHTTPS;
+ private $apiDomain = self::DEFAULT_API_DOMAIN;
+
+
+ /**
+ * Constructs a request using an apiKey and secretKey.
+ * You must provide a user ID (UID) of the tage user.
+ * Suitable for calling our old REST API
+ * @param apiKey
+ * @param secretKey
+ * @param apiMethod the api method (including namespace) to call. for example: socialize.getUserInfo
+ * If namespaces is not supplied "socialize" is assumed
+ * @param params the request parameters
+ * @param useHTTPS useHTTPS set this to true if you want to use HTTPS.
+ * @param userKey userKey A key of an administrative user with extra permissions.
+ * If this parameter is provided, then the secretKey parameter is assumed to be the admin user's secret key and not the site's secret key.
+ */
+ public function __construct($apiKey, $secretKey, $apiMethod, $params = null, $useHTTPS = false, $userKey = null )
+ {
+ if (!isset($apiMethod) || strlen($apiMethod)==0)
+ return;
+
+ if (substr($apiMethod,0,1) == "/")
+ $apiMethod = substr($apiMethod,1);
+
+ if (strrpos($apiMethod,".")==0)
+ {
+ $this->domain = "socialize.gigya.com";
+ $this->path = "/socialize." . $apiMethod;
+ } else
+ {
+ $tokens = explode(".",$apiMethod);
+ $this->domain = $tokens[0].".gigya.com";
+ $this->path = "/".$apiMethod;
+ }
+
+ $this->method = $apiMethod;
+
+ if (empty($params))
+ $this->params = new GSObject();
+ else
+ $this->params = clone $params;
+
+ // use "_host" to override domain, if available
+ $this->domain = $this->params->getString("_host", $this->domain);
+
+ $this->useHTTPS = $useHTTPS;
+
+ $this->apiKey = $apiKey;
+ $this->secretKey = $secretKey;
+ $this->userKey = $userKey;
+
+ $this->traceField("apiMethod",$apiMethod);
+ $this->traceField("apiKey",$apiKey);
+ }
+
+ public function setParam($param, $val) {
+ $this->params->put($param, $val);
+ }
+
+ public function getParams()
+ {
+ return $this->params;
+ }
+
+ /**
+ * Sets the domain used for making API calls. This method provides the option to override the default domain "gigya.com" and specify an alternative data center to be used.
+ * Parameters:
+ * $apiDomain - the domain of the data center to be used. For example: "eu1.gigya.com" for Europe data center.
+ */
+ public function setAPIDomain($apiDomain)
+ {
+ if(!isset($apiDomain) || strlen($apiDomain)==0)
+ $this->apiDomain = self::DEFAULT_API_DOMAIN;
+ else
+ $this->apiDomain = $apiDomain;
+ }
+
+ public static function setCAFile($filename)
+ {
+ GSRequest::$cafile = $filename;
+ }
+
+ public function setProxy($proxy, $proxyUserPass=":", $proxyType=CURLPROXY_HTTP)
+ {
+ $this->proxy = $proxy;
+ $this->proxyType = $proxyType;
+ $this->proxyUserPass = $proxyUserPass;
+ $this->traceField("proxy",$proxy);
+ $this->traceField("proxyType",$proxyType);
+ $this->traceField("proxyUserPass",$proxyUserPass);
+ }
+
+ public function setCurlOptionsArray($curlArray)
+ {
+ $this->curlArray = $curlArray;
+ $this->traceField("curlArray", $curlArray);
+ }
+
+ /**
+ * Send the request synchronously
+ */
+ public function send($timeout=null)
+ {
+ $format = $this->params->getString("format",null);
+
+ if (!strrpos($this->method, ".")) {
+ $this->host = "socialize.".$this->apiDomain;
+ $this->path = "/socialize.".$this->method;
+ } else {
+ $tokens = explode( ".", $this->method );
+ $this->host = $tokens[0].".".$this->apiDomain;
+ $this->path = "/".$this->method;
+ }
+
+ //set json as default format.
+ if (empty($format))
+ {
+ $format = "json";
+ $this->setParam("format", $format);
+ }
+ if(!empty($timeout))
+ {
+ $this->traceField("timeout",$timeout);
+ }
+
+ if (empty($this->method) || (empty($this->apiKey) and empty($this->userKey)) )
+ {
+ return new GSResponse($this->method,null,$this->params,400002,null,$this->traceLog);
+ }
+
+ try
+ {
+ $this->setParam("httpStatusCodes", "false");
+
+ $this->traceField("userKey", $this->userKey);
+ $this->traceField("apiKey", $this->apiKey);
+ $this->traceField("apiMethod", $this->method);
+ $this->traceField("params",$this->params);
+ $this->traceField("useHTTPS", $this->useHTTPS);
+
+ $responseStr = $this->sendRequest("POST", $this->host, $this->path, $this->params, $this->apiKey, $this->secretKey, $this->useHTTPS,$timeout, $this->userKey);
+
+ return new GSResponse($this->method,$responseStr,null,0,null,$this->traceLog);
+ }
+ catch (Exception $ex) {
+ $errcode = 500000;
+ $errMsg = $ex->getMessage();
+ $length = strlen("Operation timed out");
+ if((substr($ex->getMessage(), 0, $length) === "Operation timed out"))
+ {
+ $errcode = 504002;
+ $errMsg = "Request Timeout";
+ }
+
+ return new GSResponse($this->method,null,$this->params,$errcode,$errMsg, $this->traceLog);
+ }
+ }
+
+ private function sendRequest($method,$domain,$path,$params,$token,$secret,$useHTTPS=false,$timeout=null,$userKey=null)
+ {
+ $params->put("sdk", "php_".GSRequest::version);
+ //prepare query params
+ $protocol = $useHTTPS || empty($secret) ? "https" : "http";
+ $resourceURI = $protocol."://".$domain.$path;
+
+ //UTC timestamp.
+ $timestamp = (string) time();
+
+ //timestamp in milliseconds
+ $nonce = ((string)SigUtils::currentTimeMillis()).rand();
+ $httpMethod = "POST";
+
+ if ($userKey)
+ {
+ $params->put("userKey", $userKey);
+ }
+
+ if (!empty($secret))
+ {
+ $params->put("apiKey", $token);
+
+ $params->put("timestamp", $timestamp);
+ $params->put("nonce", $nonce);
+
+ //signature
+ $signature = self::getOAuth1Signature($secret, $httpMethod, $resourceURI, $useHTTPS, $params);
+ $params->put("sig", $signature);
+ }
+ else {
+ $params->put("oauth_token", $token);
+ }
+
+ //get rest response.
+ $res = $this->curl($resourceURI, $params, $timeout);
+ return $res;
+ }
+
+
+ private function curl($url, $params, $timeout=null)
+ {
+ foreach($params->getKeys() as $key)
+ {
+ $value = $params->getString($key);
+ $postData[$key] = $value;
+ }
+
+ $qs = http_build_query($postData);
+ $this->traceField("URL",$url);
+ $this->traceField("postData",$qs);
+
+ /* POST */
+ $defaults = array(
+ CURLOPT_URL => $url,
+ CURLOPT_POST=>1,
+ CURLOPT_HEADER => 1,
+ CURLOPT_POSTFIELDS=>$qs,
+ CURLOPT_HTTPHEADER => array( 'Expect:'),
+ CURLOPT_RETURNTRANSFER => TRUE,
+ CURLOPT_SSL_VERIFYPEER => TRUE,
+ CURLOPT_SSL_VERIFYHOST => 2,
+ CURLOPT_CAINFO => GSRequest::$cafile ,
+ CURLOPT_PROXY => $this->proxy,
+ CURLOPT_PROXYTYPE => $this->proxyType,
+ CURLOPT_PROXYUSERPWD => $this->proxyUserPass,
+ CURLOPT_TIMEOUT_MS => $timeout
+ );
+
+ $ch = curl_init();
+ $mergedCurlArray = ($this->curlArray + $defaults);
+
+ curl_setopt_array($ch, $mergedCurlArray);
+
+ if(!$result = curl_exec($ch))
+ {
+ $err = curl_error($ch) ;
+ throw new Exception($err);
+ }
+
+ curl_close($ch);
+
+ list($header, $body) = explode("\r\n\r\n", $result, 2);
+ $headers = explode("\r\n", $header);
+ foreach($headers as $value)
+ {
+ $kvp = explode(":", $value);
+ if($kvp[0] == "x-server")
+ {
+ $this->traceField("server",$kvp[1]);
+ break;
+ }
+ }
+
+ return $body;
+ }
+
+ /**
+ * Converts a GSObject to a query string
+ * @param params
+ * @return
+ */
+ public static function buildQS($params)
+ {
+ $val;
+ $ret = "";
+ foreach($params->getKeys() as $key)
+ {
+ $val = $params->getString($key);
+ if (isset($val))
+ {
+ $ret .="$key=".urlencode($val);
+ }
+ $ret .='&';
+ }
+
+ $ret = rtrim($ret, "&");
+
+ return $ret;
+ }
+
+ private static function getOAuth1Signature($key, $httpMethod, $url, $isSecureConnection, $requestParams)
+ {
+ // Create the BaseString.
+ $baseString = self::calcOAuth1BaseString($httpMethod, $url, $isSecureConnection, $requestParams);
+ return SigUtils::calcSignature($baseString,$key);
+ }
+
+ private static function calcOAuth1BaseString($httpMethod, $url, $isSecureConnection, $requestParams)
+ {
+
+
+ $normalizedUrl = "";
+ $u = parse_url($url);
+ $protocol = strtolower($u["scheme"]);
+
+ if(array_key_exists('port',$u))
+ {
+ $port = $u['port'];
+ }
+ else
+ $port = null;
+
+ $normalizedUrl .= $protocol."://";
+ $normalizedUrl .= strtolower($u["host"]);
+
+ if ( $port != "" && (($protocol=="http" && $port!=80) || ($protocol=="https" && $port!=443)))
+ {
+ $normalizedUrl .= ':'.$port;
+ }
+ $normalizedUrl .= $u["path"];
+
+ // Create a sorted list of query parameters
+ $amp = "";
+ $queryString = "";
+ $keys = $requestParams->getKeys();
+ sort($keys);
+ foreach($keys as $key)
+ {
+ $value = $requestParams->getString($key);
+ if ($value !== false && $value != "0" && empty($value))
+ {
+ $value = "";
+ }
+
+ //curl is sending 1 and 0 when the value is boolean.
+ //so in order to create a valid signature we're changing false to 0 and true to 1.
+ if($value === false)$value = "0";
+ if($value === true)$value = "1";
+ $queryString .= $amp.$key."=".self::UrlEncode($value);
+ $amp = "&";
+ }
+
+ // Construct the base string from the HTTP method, the URL and the parameters
+ $baseString = strtoupper($httpMethod)."&".self::UrlEncode($normalizedUrl)."&".self::UrlEncode($queryString);
+ return $baseString;
+
+ }
+
+ public static function UrlEncode($value)
+ {
+ if ($value === false)
+ {
+ return $value;
+ }
+ else
+ {
+ return str_replace('%7E', '~', rawurlencode($value));
+ }
+ }
+
+ private function traceField($name,$value)
+ {
+ array_push($this->traceLog,$name."=". print_r($value, true));
+ }
+
+}
+
+
+/**
+ * Wraps the server's response.
+ * If the request was sent with the format set to "xml", the getData() will return null and you should use getResponseText() instead.
+ * We only parse response text into GSObject if request format is set "json" which is the default.
+ *
+ */
+class GSResponse
+{
+ private $errorCode = 0;
+ private $errorMessage = null;
+ private $rawData = "";
+ private $data; //GSObject
+ private static $errorMsgDic;
+ private $params = null;
+ private $method = null;
+ private $traceLog = null;
+
+ public static function Init(){
+ self::$errorMsgDic = new GSObject();
+ self::$errorMsgDic->put(400002, "Required parameter is missing");
+ self::$errorMsgDic->put(500000, "General server error");
+ }
+
+
+ public function getErrorCode()
+ {
+ return $this->errorCode;
+ }
+
+ public function getErrorMessage() {
+ if (isset($this->errorMessage))
+ return $this->errorMessage;
+ else
+ {
+
+ if ($this->errorCode==0 || !self::$errorMsgDic->containsKey((int)$this->errorCode))
+ return "";
+ else
+ return self::$errorMsgDic->getString($this->errorCode);
+ }
+ }
+
+ public function getResponseText()
+ {
+ return $this->rawData;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /* GET BOOLEAN */
+ public function getBool($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return $this->data->getBool($key,$defaultValue);
+ }
+
+ /* GET INTEGER */
+ public function getInt($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return $this->data->getInt($key,$defaultValue);
+ }
+
+ /* GET LONG */
+ public function getLong($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return $this->data->getLong($key,$defaultValue);
+ }
+ /* GET INTEGER */
+ public function getDouble($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return $this->data->getDouble($key,$defaultValue);
+ }
+
+ /* GET STRING */
+ public function getString($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return $this->data->getString($key,$defaultValue);
+ }
+
+ /* GET GSOBJECT */
+ public function getObject($key)
+ {
+ return $this->data->getObject($key);
+ }
+
+ /* GET GSOBJECT[] */
+ public function getArray($key)
+ {
+ return $this->data->getArray($key);
+ }
+
+
+ /* C'tor */
+ public function __construct($method,$responseText=null,$params=null,$errorCode=null,$errorMessage=null,$traceLog=null)
+ {
+
+ $this->traceLog = $traceLog;
+ $this->method = $method;
+ if(empty($params))
+ $this->params = new GSObject();
+ else
+ $this->params=$params;
+
+ if(!empty($responseText))
+ {
+ $this->rawData = $responseText;
+ if(strpos(ltrim($responseText),"{") !== false)
+ {
+
+ $this->data = new GSObject($responseText);
+ if(isset($this->data))
+ {
+ if ($this->data->containsKey("errorCode"))
+ {
+ $this->errorCode = $this->data->getInt("errorCode");
+ }
+ if ($this->data->containsKey("errorMessage"))
+ {
+ $this->errorMessage = $this->data->getString("errorMessage");
+ }
+ }
+ }
+ else
+ {
+ $matches= array();
+ preg_match("~<errorCode\s*>([^<]+)~", $this->rawData, $matches);
+ if(sizeof($matches)>0)
+ {
+ $errCodeStr = $matches[1];
+ if ($errCodeStr!=null)
+ {
+ $this->errorCode = (int)$errCodeStr;
+
+ $matches= array();
+ preg_match("~<errorMessage\s*>([^<]+)~", $this->rawData, $matches);
+ if(sizeof($matches)>0){
+ $this->errorMessage = $matches[1];
+ }
+ }
+ }
+ }
+
+ }
+ else{
+
+ $this->errorCode = $errorCode;
+ $this->errorMessage = $errorMessage != null ? $errorMessage : self::getErrorMessage();
+ $this->populateClientResponseText();
+ }
+ }
+
+ private function populateClientResponseText()
+ {
+ if ($this->params->getString("format","json"))
+ {
+ $this->rawData = "{errorCode:" . $this->errorCode . ",errorMessage:\"" . $this->errorMessage . "\"}";
+ }
+ else
+ {
+ $sb = array(
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ ,"<".$this->method."Response xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:com:gigya:api http://socialize-api.gigya.com/schema\" xmlns=\"urn:com:gigya:api\">"
+ ,"<errorCode>".$this->errorCode."</errorCode>"
+ ,"<errorMessage>".$this->errorMessage."</errorMessager>"
+ ,"</".$this->method."Response>"
+ );
+
+
+ $this->rawData = implode("\r\n",$sb);
+ }
+ }
+
+ public function getLog()
+ {
+ return implode("\r\n",$this->traceLog);
+ }
+
+
+ public function __toString()
+ {
+ $sb = "";
+ $sb .= "\terrorCode:";
+ $sb .= $this->errorCode;
+ $sb .= "\n\terrorMessage:";
+ $sb .= $this->errorMessage;
+ $sb .= "\n\tdata:";
+ $sb .= $this->data;
+ return $sb;
+ }
+}
+GSResponse::Init();
+
+
+/**
+ * Used for passing parameters when issueing requests e.g. GSRequest.send
+ * As well as returning response data e.g. GSResponse.getData
+* @version 1.0
+*/
+
+class GSObject {
+ private $map;
+
+ /* PUBLIC INTERFACE */
+ /**
+ * Construct a GSObject from json string, throws excpetion.
+ * @param json the json formatted string
+ * @throws Exception if unable to parse json
+ */
+ public function __construct($json=null)
+ {
+ $this->map = array();
+ if(!empty($json)){
+
+ //parse json string.
+ if(gettype($json) == 'string')
+ {
+ $obj = json_decode($json,false);
+
+
+ if($obj == null){
+ throw new GSException();
+ }
+ }
+ else
+ {
+ $obj = $json;
+ }
+
+ self::processJsonObject($obj,$this);
+ }
+ }
+
+ public function serialize()
+ {
+ $arr = Array();
+ if(empty($this->map))return $arr;
+
+ $arr = $this->serializeGSObject($this);
+
+ return $arr;
+ }
+
+ public static function serializeGSObject($gsd)
+ {
+ $arr = Array();
+ foreach ($gsd->map as $name=>$value) {
+
+ $val = GSObject::serializeValue($value);
+ $arr[$name] = $val;
+ }
+ return $arr;
+ }
+
+
+
+ public static function serializeValue($value)
+ {
+
+ //GSDictionary
+ if($value instanceof GSObject){
+ return GSObject::serializeGSObject($value);
+ }
+
+ //array
+ else if($value instanceof GSArray){
+ return GSArray::serializeGSArray($value);
+ }
+
+ //else just add
+ else{
+ return $value;
+ }
+ }
+
+
+ /* Put */
+ const DEFAULT_VALUE = '@@EMPTY@@';
+
+ public function put($key,$value)
+ {
+ $this->map[$key] = $value;
+ }
+
+
+ private function get($key,$defaultValue)
+ {
+ if (array_key_exists($key, $this->map)) {
+ return $this->map[$key];
+ }
+
+ if($defaultValue !== GSObject::DEFAULT_VALUE)
+ {
+ return $defaultValue;
+ }
+ throw new GSKeyNotFoundException($key);
+ }
+
+ /* GET BOOLEAN */
+ public function getBool($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return (bool)$this->get($key,$defaultValue);
+ }
+
+ /* GET INTEGER */
+ public function getInt($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return (int)$this->get($key,$defaultValue);
+ }
+
+ /* GET LONG */
+ public function getLong($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return (float)$this->get($key,$defaultValue);
+ }
+
+ /* GET DOUBLE */
+ public function getDouble($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ return (double)$this->get($key,$defaultValue);
+ }
+
+ /* GET STRING */
+ public function getString($key, $defaultValue=GSObject::DEFAULT_VALUE)
+ {
+ $obj = $this->get($key,$defaultValue);
+ return (string)$obj;
+ }
+
+ /* GET GSOBJECT */
+ public function getObject($key)
+ {
+ return (object)$this->get($key,null);
+ }
+
+ /* GET GSOBJECT[] */
+ public function getArray($key)
+ {
+ return $this->get($key,null);
+ }
+
+ /**
+ * Parse parameters from URL into the dictionary
+ * @param url
+ */
+ public function parseURL($url)
+ {
+ try {
+ $u = parse_url($url);
+ if(isset($u["query"]))
+ $this->parseQueryString($u["query"]);
+ if(isset($u["fragment"]))
+ $this->parseQueryString($u["fragment"]);
+ } catch (Exception $e) {
+ }
+ }
+
+
+ /**
+ * Parse parameters from query string
+ * @param qs
+ */
+ public function parseQueryString($qs)
+ {
+ if (!isset($qs)) return;
+ parse_str($qs, $this->map);
+ }
+
+ public function containsKey($key)
+ {
+ return array_key_exists($key, $this->map);
+ }
+
+ public function remove($key)
+ {
+ unset($this->map[$key]);
+ }
+
+ public function clear()
+ {
+ unset($this->map);
+ $this->map = array();
+ }
+
+ public function getKeys(){
+ return array_keys($this->map);
+ }
+
+ public function __toString() {
+ return $this->toJsonString();
+ }
+
+ public function toString() {
+ return $this->toJsonString();
+ }
+
+ public function toJsonString()
+ {
+ try {
+ return json_encode($this->serialize());
+ } catch (Exception $e)
+ {
+ return null;
+ }
+ }
+
+ private static function processJsonObject($jo, $parentObj)
+ {
+ if(!empty($jo))
+ foreach ($jo as $name=>$value) {
+
+
+ //array
+ if(is_array(($value)))
+ {
+ $parentObj->put($name, new GSArray($value));
+ }
+ //object
+ elseif (is_object($value))
+ {
+
+ $childObj = new GSObject();
+ $parentObj->put($name, $childObj);
+ self::processJsonObject($value, $childObj);
+
+
+ }
+ //primitive
+ else{
+ $parentObj->put($name, $value);
+ }
+ }
+
+ return $parentObj;
+
+ }
+
+}
+
+
+class GSArray{
+ private $map;
+ const NO_INDEX_EX = "GSArray does not contain a value at index ";
+
+ public function __construct($value=null)
+ {
+ $this->map = array();
+ if(!empty($value)){
+ $obj = $value;
+
+ //parse json string.
+ if(gettype($value) == 'string')
+ {
+ $obj = json_decode($value,false);
+
+ if($obj == null){
+ throw new GSException();
+ }
+ }
+
+ $this->processJsonObject($obj,$this);
+ }
+ }
+
+ private static function processJsonObject($value,$gsarr)
+ {
+ if(!empty($value)){
+ foreach($value as $val)
+ {
+ if ($val == null)
+ {
+ $gsarr->add($val);
+ }
+ elseif (is_object($val))
+ {
+ $gsobj = new GSObject($val);
+ $gsarr->add($gsobj);
+ }
+ else if(is_array(($val)))
+ {
+ $newGsarr = new GSArray($val);
+ $gsarr->add($newGsarr);
+ }
+ else
+ {
+ $gsarr->add($val);
+ }
+ }
+ }
+ }
+
+ public function add($value){
+ array_push($this->map, $value);
+ }
+
+ public function getString($inx){
+ $obj = $this->map[$inx];
+ if ($obj === null)
+ return null;
+ else
+ return strval($obj);
+ }
+
+ public function getBool($inx){
+ $obj = $this->map[$inx];
+ if ($obj === null)
+ throw new Exception(GSArray::NO_INDEX_EX+$inx);
+
+ if (is_bool($obj))
+ {
+ return (Boolean)$obj;
+ } else
+ {
+ $val = strtolower(strval($obj));
+ return $val == "true" || $val == "1";
+ }
+ }
+
+ public function getInt($inx){
+
+ $obj = $this->map[$inx];
+ if ($obj === null)
+ throw new Exception(GSArray::NO_INDEX_EX+$inx);
+
+ if (is_int($obj))
+ {
+ return (int)$obj;
+ } else
+ {
+ return intval($this->getString($inx));
+ }
+ }
+
+ public function getLong($inx){
+ $obj = $this->map[$inx];
+ if ($obj === null)
+ throw new Exception(GSArray::NO_INDEX_EX+$inx);
+
+ if (is_float($obj))
+ {
+ return (float)$obj;
+ } else
+ {
+ return floatval($this->getString($inx));
+ }
+ }
+
+ public function getDouble($inx){
+ $obj = $this->map[$inx];
+ if ($obj === null)
+ throw new Exception(GSArray::NO_INDEX_EX+$inx);
+
+ if (is_double($obj))
+ {
+ return (double)$obj;
+ } else
+ {
+ return doubleval($this->getString($inx));
+ }
+ }
+
+ public function getObject($inx){
+ return $this->map[$inx];
+ }
+
+ public function getArray($inx){
+ return $this->map[$inx];
+ }
+
+ public function length(){
+ return sizeof($this->map);
+ }
+
+
+ public function __toString() {
+ return $this->toJsonString();
+ }
+
+ public function toString() {
+ return $this->toJsonString();
+ }
+
+ public function toJsonString()
+ {
+ try {
+ return json_encode($this->serialize());
+ } catch (Exception $e)
+ {
+ return null;
+ }
+ }
+ public function serialize()
+ {
+ $arr = Array();
+ if(empty($this->map))return $arr;
+
+ $arr = GSArray::serializeGSArray($this);
+
+ return $arr;
+ }
+
+ public static function serializeGSArray($gsarr){
+ $arr = Array();
+ for($i=0; $i < $gsarr->length(); $i++) {
+
+ $val = $gsarr->getObject($i);
+ $val = GSObject::serializeValue($val);
+ array_push($arr,$val);
+ }
+ return $arr;
+ }
+}
+
+
+
+class SigUtils
+{
+ public static function validateUserSignature($UID, $timestamp, $secret, $signature)
+ {
+ $baseString = $timestamp."_".$UID;
+ $expectedSig = self::calcSignature($baseString, $secret);
+ return $expectedSig == $signature;
+ }
+
+ public static function validateFriendSignature($UID, $timestamp, $friendUID, $secret, $signature)
+ {
+ $baseString = $timestamp."_".$friendUID."_".$UID;
+ $expectedSig = self::calcSignature($baseString, $secret);
+ return $expectedSig == $signature;
+ }
+
+ static function currentTimeMillis()
+ {
+ // get utc time in ms
+ list( $msecs, $uts ) = explode( ' ', microtime());
+ return floor(($uts+$msecs)*1000);
+ }
+
+ public static function getDynamicSessionSignature($glt_cookie, $timeoutInSeconds, $secret)
+ {
+ // cookie format:
+ // <expiration time in unix time format>_BASE64(HMACSHA1(secret key, <login token>_<expiration time in unix time format>))
+ $expirationTimeUnixMS = (SigUtils::currentTimeMillis()/1000) + $timeoutInSeconds;
+ $expirationTimeUnix = (string)floor($expirationTimeUnixMS);
+ $unsignedExpString = $glt_cookie . "_" . $expirationTimeUnix;
+ $signedExpString = SigUtils::calcSignature($unsignedExpString, $secret); // sign the base string using the secret key
+
+ $ret = $expirationTimeUnix . '_' . $signedExpString; // define the cookie value
+
+ return $ret;
+ }
+
+ static function calcSignature($baseString,$key)
+ {
+ $baseString = utf8_encode($baseString);
+ $rawHmac = hash_hmac("sha1", utf8_encode($baseString), base64_decode($key), true);
+ $signature = base64_encode($rawHmac);
+ return $signature;
+ }
+}
+?>
Index: includes/gigya/includes/settings.php
===================================================================
--- includes/gigya/includes/settings.php (revision 0)
+++ includes/gigya/includes/settings.php (working copy)
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * This file contains all the fun stuff that is added to the WordPress Settings screen
+ * This will allow us to store and pass our Gigya public and private keys (which we don't we available publicly, so we'll save them to the database)
+ *
+ * @since HAL 9000
+ */
+
+
+/**
+ * Adds a new section on to Settings > General screen
+ * This section will allow us to add the fields needed for passing our Gigya public and private keys and store them in the DB.
+ * @return void
+ *
+ * @since HAL 9000
+ */
+function make_gigya_init_settings() {
+
+ // Define our settings section
+ add_settings_section( 'make_gigya_settings_section', 'Gigya Settings', 'make_gigya_settings_description', 'general' );
+
+ // Define our actual settings and asign them to the settings section
+ add_settings_field( 'make_gigya_public_key', 'API Key', 'make_gigya_text_field', 'general', 'make_gigya_settings_section', array(
+ 'name' => 'make_gigya_public_key',
+ 'id' => 'make_gigya_public_key',
+ ) );
+ add_settings_field( 'make_gigya_private_key', 'Secret Key', 'make_gigya_text_field', 'general', 'make_gigya_settings_section', array(
+ 'name' => 'make_gigya_private_key',
+ 'id' => 'make_gigya_private_key',
+ ) );
+
+ // Now we need to register our settings
+ register_setting( 'general', 'make_gigya_public_key', 'make_gigya_sanitize_input' );
+ register_setting( 'general', 'make_gigya_private_key', 'make_gigya_sanitize_input' );
+}
+add_action( 'admin_init', 'make_gigya_init_settings' );
+
+
+/**
+ * The callback function to the settings section set in make_gigya_init_settings()
+ * @return string
+ *
+ * @since HAL 9000
+ */
+function make_gigya_settings_description() {
+ echo '<p>The place where all the cool kids store their Gigya API keys :D</p>';
+}
+
+
+/**
+ * A generic function that will output a text field
+ * To customize, add a name and id to the add_settings_field array arguments
+ * @param array $args The arguments passed from add_settings_field()
+ * @return string
+ *
+ * @since HAL 9000
+ */
+function make_gigya_text_field( $args ) {
+ $value = get_option( esc_attr( $args['name'] ) );
+
+ echo '<input type="text" name="' . esc_attr( $args['name'] ) . '" id="' . esc_attr( $args['id'] ) . '" class="regular-text" value="' . ( ! empty( $value ) ? esc_attr( $value ) : '' ) . '" />';
+}
+
+
+/**
+ * Sanitizes user input in the gigya public and private keys
+ * @param string $input The value passed through the form
+ * @return string
+ *
+ * @since HAL 9000
+ */
+function make_gigya_sanitize_input( $input ) {
+ $input = sanitize_text_field( $input );
+
+ return $input;
+}
\ No newline at end of file
Index: includes/gigya/js/login.js
===================================================================
--- includes/gigya/js/login.js (revision 0)
+++ includes/gigya/js/login.js (working copy)
@@ -0,0 +1,180 @@
+/**
+ * This script contains all the JavaScript that controls or interfaces with the socialize features of Gigya (AKA Facebook, Twitter, etc etc logins)
+ * To hit our deadline for Maker Faire Bay Area, we need to rely on the JavaScript SDK from Gigya.
+ * A server-side SDK is preferred, but a new spec and time is needed to be done to do so.
+ *
+ * @since SPRINT_NAME
+ */
+
+// Set debugging mode.
+var gigya_debug = true;
+
+jQuery( document ).ready(function() {
+
+ // Return our makers information. If a session is not found, Gigya will report back with an error and handled in the callback.
+ gigya.accounts.getAccountInfo({ callback: make_is_logged_in });
+
+ // Listen for a click event to open the login screen
+ jQuery( document ).on( 'click', '.user-creds.signin', function( e ) {
+ e.preventDefault();
+
+ gigya.accounts.showScreenSet({
+ screenSet: 'Login-web',
+ mobileScreenSet: 'Login-mobile'
+ });
+ });
+
+ // Listen for a click event to open the register screen
+ jQuery( document ).on( 'click', '.user-creds.join', function( e ) {
+ e.preventDefault();
+
+ gigya.accounts.showScreenSet({
+ screenSet: 'makezine-login',
+ mobileScreenSet: 'makezine-mobile-login',
+ startScreen: 'gigya-register-screen'
+ });
+ });
+
+ // Check that we aren't dealing with a logged in WP user, if not, log them out of Gigya.
+ if ( make_gigya.loggedin ) {
+ jQuery( document ).on( 'click', '.user-creds.signout', function( e ) {
+ e.preventDefault();
+
+ if ( gigya_debug )
+ console.log( 'Logout Started' );
+
+ gigya.accounts.logout();
+ });
+ }
+});
+
+
+/**
+ * The Gigya service generates several global application events for various situations that are driven by user interactions.
+ * Global application events are fired whenever the event to which they refer occurs, regardless of what was the action that triggered the event.
+ * This method allows setting event handlers for each of the supported global events.
+ * @url http://developers.gigya.com/020_Client_API/020_Accounts/accounts.addEventHandlers
+ *
+ * @since SPRINT_NAME
+ */
+gigya.accounts.addEventHandlers({ //
+ onLogin: make_on_login,
+ onLogout: make_on_logout
+});
+
+
+/**
+ * Event handler of socialize.
+ * http://developers.gigya.com/020_Client_API/010_Socialize/socialize.addEventHandlers#section_1
+ *
+ * NOTE: It is important to use the REST API for logging in or registering users http://developers.gigya.com/037_API_reference/010_Socialize
+ *
+ * @param object eventObj The event object?
+ * @since SPRINT_NAME
+ */
+function make_on_login( eventObj ) {
+ if ( gigya_debug )
+ console.log( 'Logged in to Gigya!' );
+
+ // Send our data via Ajax to the server to verify if the user is a returning user or a new one and create their profile.
+ jQuery.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: make_gigya.ajax,
+ xhrFields: {
+ withCredentials: true
+ },
+ data: {
+ 'action' : 'make_login_user', // Calls our wp_ajax_nopriv_make_ajax_login or wp_ajax_make_ajax_login actions
+ 'request' : 'login',
+ 'object' : eventObj,
+ 'nonce' : make_gigya.secure_it
+ },
+ success: function( results ) {
+ if ( gigya_debug )
+ console.log( results.message );
+
+ // Check that everything went well
+ if ( results.loggedin === true ) {
+ document.location = make_gigya.root_path + 'contribute';
+ } else {
+ // We may have logged into Gigya, but something happened on our end. Let's correct Gigya.
+ gigya.accounts.logout();
+
+ alert( 'Something went wrong and we couldn\'t log you in. Please try again.' );
+ }
+
+ },
+ error: function( jqXHR, textStatus, errorThrown ) {
+ if ( gigya_debug ) {
+ console.log( 'ERROR' );
+ console.log( textStatus );
+ console.log( errorThrown );
+ }
+ },
+ complete: function( jqXHR, textStatus ) {
+ if ( gigya_debug )
+ console.log( 'Login Complete.' );
+ }
+ });
+}
+
+
+/**
+ * onLogout Event handler
+ * After we have successfully logged out, we'll redirect to the homepage.
+ *
+ * @since SPRINT_NAME
+ */
+function make_on_logout() {
+ if ( gigya_debug )
+ console.log( 'User logged out' );
+
+ // Redirect back to the homepage
+ document.location = make_gigya.root_path;
+}
+
+
+/**
+ * Checks if gigya returned a user account and verifies the signature for additional security
+ * @param object maker The object returned by gigya.accounts.getAccountInfo()
+ * @return mixed
+ *
+ * @since SPRINT_NAME
+ */
+function make_is_logged_in( maker ) {
+ if ( gigya_debug ) {
+ console.log( maker );
+
+ if ( make_gigya.loggedin === 'false' ) {
+ console.log( 'WP not logged in' );
+ } else {
+ console.log( 'WP logged in' );
+ }
+ }
+
+ if ( make_gigya.loggedin === 'true' || maker.errorCode === 0 ) {
+ if ( gigya_debug )
+ console.log( 'User Logged In.' );
+
+ // We only want to provide a sign out feature for Gigya users
+ var signout = ( make_gigya.loggedin === 'false' ) ? '<a href="#signout" class="user-creds signout">Sign Out</a> / ' : '';
+
+ jQuery( '.main-header' ).find( '.row' ).append( '<div class="login-wrapper">' + signout + '<a href="' + make_gigya.root_path + 'contribute" class="user-creds profile">Contribute</a></div>' );
+
+ // Display our content
+ jQuery( '.container.authentication' ).show();
+
+ // Append the Gigya UID to the contribute form
+ if ( make_gigya.loggedin === 'false' )
+ make_contribute_add_gigya_id( maker.UID );
+ } else {
+ if ( gigya_debug )
+ console.log( 'User Not Logged In.' );
+
+ // Add our login/register links
+ jQuery( '.main-header' ).find( '.row' ).append( '<div class="login-wrapper"><a href="#signin" class="user-creds signin">Sign In</a> / <a href="#join" class="user-creds join">Join</a></div>' );
+
+ jQuery( '.container.authentication' ).html( '<div class="row"><div class="span12 login-required"><h2>You must be logged in to access this area. Please <a href="#signin" class="user-creds signin">Sign In</a> or <a href="#join" class="user-creds join">Join</a>.</h2></div></div>' ).show();
+ }
+}
Index: includes/maker-profile/includes/meta-boxes.php
===================================================================
--- includes/maker-profile/includes/meta-boxes.php (revision 0)
+++ includes/maker-profile/includes/meta-boxes.php (working copy)
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * While guest-authors is defiend by CoAuthors Plus, we need to integrate some extra info
+ * To better cater to our custom user login interface.
+ *
+ * @since SPRINT_NAME
+ */
+
+/**
+ * Add custom meta boxes to the edit screen of Guest Authors
+ * @return void
+ *
+ * @since SPRINT_NAME
+ */
+function make_guest_author_meta_boxes() {
+ add_meta_box( 'make_ga_custom_fieldds', 'Additional Info', 'make_guest_authors_custom_fields', 'guest-author', 'normal', 'low' );
+}
+add_action( 'add_meta_boxes', 'make_guest_author_meta_boxes' );
+
+
+/**
+ * Displays random, additional information for makers accounts
+ * IE. Gigya ID, Last Logged in, Date Created on Gigya, etc etc
+ * @param object $guest_authors Post object
+ * @return html
+ *
+ * @since SPRINT_NAME
+ */
+function make_guest_authors_custom_fields( $guest_author ) {
+ $guid = get_post_meta( absint( $guest_author->ID ), 'cap-guid', true );
+ $last_login = get_post_meta( absint( $guest_author->ID ), 'cap-last_login', true );
+ $created = get_post_meta( absint( $guest_author->ID ), 'cap-created', true ); ?>
+ <table class="form-table">
+ <tbody>
+ <tr>
+ <th><label for="cap-guid">Gigya ID</label></th>
+ <td><input type="text" name="cap-guid" value="<?php echo ( ! empty( $guid ) ) ? sanitize_text_field( $guid ) : ''; ?>" class="regular-text"></td>
+ </tr>
+ <tr>
+ <th><label for="cap-last_login">Last Login</label></th>
+ <td><input type="text" name="cap-last_login" value="<?php echo ( ! empty( $last_login ) ) ? sanitize_text_field( $last_login ) : ''; ?>" class="regular-text" disabled="disabled"></td>
+ </tr>
+ <tr>
+ <th><label for="cap-created">Date Created (via Gigya)</label></th>
+ <td><input type="text" name="cap-created" value="<?php echo ( ! empty( $created ) ) ? sanitize_text_field( $created ) : ''; ?>" class="regular-text" disabled="disabled"></td>
+ </tr>
+ </tbody>
+ </table>
+ <?php wp_nonce_field( 'save_guest_author_custom_fields', 'ga_custom_fields' );
+}
+
+
+/**
+ * Save our custom Guest Author meta boxes
+ * @param integer $id The post ID
+ * @return void
+ *
+ * @since SPRINT_NAME
+ */
+function make_guest_authors_save( $id ) {
+
+ // Make sure we are only saving Guest Authors
+ if ( get_post_type() == 'guest-author' )
+ return;
+
+ if ( ! isset( $_POST['ga_custom_fields'] ) || ! wp_verify_nonce( $_POST['ga_custom_fields'], 'save_guest_author_custom_fields' ) )
+ return;
+
+ if ( isset( $_POST['cap-guid'] ) )
+ update_post_meta( absint( $id ), 'cap-guid', sanitize_text_field( $_POST['cap-guid'] ) );
+
+ // We never want to allow updating of last login and creation dates manually, only systematically.
+}
+add_action( 'save_post', 'make_guest_authors_save' );
\ No newline at end of file
Index: includes/maker-profile/maker-profile.php
===================================================================
--- includes/maker-profile/maker-profile.php (revision 0)
+++ includes/maker-profile/maker-profile.php (working copy)
@@ -0,0 +1,6 @@
+<?php
+
+
+// TODO - Continue building the user profile interface
+
+include_once( 'includes/meta-boxes.php' );
\ No newline at end of file
Index: includes/post-types/newsletter.php
===================================================================
--- includes/post-types/newsletter.php (revision 141566)
+++ includes/post-types/newsletter.php (working copy)
@@ -107,10 +107,10 @@
$dropdown_args['post_status'] = array('publish', 'draft', 'pending', 'future', 'private');
return $dropdown_args;
}
-
-add_filter( 'page_attributes_dropdown_pages_args', 'make_page_attributes_metabox_add_parents', 10, 2 );
+
+add_filter( 'page_attributes_dropdown_pages_args', 'make_page_attributes_metabox_add_parents', 10, 2 );
add_filter( 'quick_edit_dropdown_pages_args', 'make_page_attributes_metabox_add_parents', 10);
-
+
/**
* Add (status) to titles in page parent dropdowns
*
@@ -124,9 +124,9 @@
$title .= " ($status)";
return $title;
}
-
+
add_filter( 'list_pages', 'make_page_parent_status_filter', 10, 2);
-
+
/**
* Filter pages metabox on menu admin screen to include all built-in statuses.
*
@@ -137,17 +137,17 @@
if ( is_admin() ) {
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
- if ( 'nav-menus' == $screen->base )
- $query->set( 'post_status', 'publish,private,future,pending,draft' );
+ // if ( 'nav-menus' == $screen->base )
+ // $query->set( 'post_status', 'publish,private,future,pending,draft' );
}
}
return $query;
}
-
+
add_filter('pre_get_posts', 'make_private_page_query_filter');
-
+
/**
- * Filter lists of pages to include privately published ones.
+ * Filter lists of pages to include privately published ones.
* This a duplicate of wp_list_pages() except we change post_status in the default args, and we return without echoing.
*
* @param string $output Original output of wp_list_pages(), which we will overwrite.
@@ -156,38 +156,38 @@
*/
function make_wp_list_pages_with_private($output, $args) {
$defaults = array( 'post_status' => 'publish,private' ); // other defaults already parsed in wp_list_pages()
-
+
$r = wp_parse_args( $args, $defaults );
extract( $r, EXTR_SKIP );
-
+
$output = '';
$current_page = 0;
-
+
// sanitize, mostly to keep spaces out
$r['exclude'] = preg_replace('/[^0-9,]/', '', $r['exclude']);
-
+
// Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array)
$exclude_array = ( $r['exclude'] ) ? explode(',', $r['exclude']) : array();
$r['exclude'] = implode( ',', apply_filters('wp_list_pages_excludes', $exclude_array) );
-
+
// Query pages.
$r['hierarchical'] = 0;
$pages = get_pages($r);
-
+
if ( !empty($pages) ) {
if ( $r['title_li'] )
$output .= '<li class="pagenav">' . $r['title_li'] . '<ul>';
-
+
global $wp_query;
if ( is_page() || is_attachment() || $wp_query->is_posts_page )
$current_page = $wp_query->get_queried_object_id();
$output .= walk_page_tree($pages, $r['depth'], $current_page, $r);
-
+
if ( $r['title_li'] )
$output .= '</ul></li>';
}
-
+
return $output;
}
-
+
add_filter('wp_list_pages', 'make_wp_list_pages_with_private', 1, 2);
Index: includes/projects-cpt.php
===================================================================
--- includes/projects-cpt.php (revision 141566)
+++ includes/projects-cpt.php (working copy)
@@ -5,7 +5,7 @@
* @package makeblog
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @author Jake Spurlock <jspurlock@makermedia.com>
- *
+ *
*/
add_action( 'init', 'register_cpt_project' );
@@ -19,7 +19,7 @@
add_rewrite_rule( 'projects/([^/]*)/([^/]*)/?$','index.php?projects=$matches[2]','top' );
- $labels = array(
+ $labels = array(
'name' => _x( 'Projects', 'Project' ),
'singular_name' => _x( 'Project', 'Project' ),
'add_new' => _x( 'Add New', 'Project' ),
@@ -34,7 +34,7 @@
'menu_name' => _x( 'Projects', 'Project' ),
);
- $args = array(
+ $args = array(
'labels' => $labels,
'hierarchical' => true,
'description' => 'MAKE magazine Projects will be stored here. Goal is to build the back archive of all issues and Projects.',
@@ -68,7 +68,7 @@
'Image' => array(),
'TimeRequired' => array(),
'PageNumber' => array(),
- 'Conclusion' => array(
+ 'Conclusion' => array(
'type' => 'textarea',
'label' => 'Projects Conclusion',
),
@@ -86,7 +86,7 @@
* Generate the TOC for projects.
*
* @deprecated February 2013. The make_magazine_toc has been made more flexible to allow for any post type.
- *
+ *
*/
function make_magazine_projects_toc() {
global $post;
@@ -95,7 +95,7 @@
'no_found_rows' => true,
'post_type' => 'projects'
);
-
+
if($post->post_parent == 0) {
echo '<h3>Projects</h3>';
}
@@ -124,10 +124,10 @@
</div>
<div class="clear"></div>
-
+
<hr />
- </Project>
+ </Project>
<?php endwhile;
@@ -140,9 +140,9 @@
/**
* Add the parent selector to assign to a project to a volume.
- *
+ *
*/
-function make_projects_add_meta_box() {
+function make_projects_add_meta_box() {
add_meta_box('volume-parent', 'Magazine Volume', 'make_magazine_parent_page', 'projects', 'side', 'high');
}
@@ -150,9 +150,9 @@
/**
* Remove the existing parent selector meta box.
- *
+ *
*/
-function make_projects_remove_parent_meta_box() {
+function make_projects_remove_parent_meta_box() {
remove_meta_box('pageparentdiv', 'projects', 'normal');
}
@@ -176,11 +176,11 @@
/**
* Register the flags taxonomy
- *
+ *
*/
function make_register_taxonomy_flags() {
- $labels = array(
+ $labels = array(
'name' => _x( 'Flags', 'flags' ),
'singular_name' => _x( 'Flag', 'flags' ),
'search_items' => _x( 'Search Flags', 'flags' ),
@@ -198,7 +198,7 @@
'menu_name' => _x( 'Flags', 'flags' ),
);
- $args = array(
+ $args = array(
'labels' => $labels,
'public' => true,
'show_in_nav_menus' => true,
@@ -217,11 +217,11 @@
/**
* Add the difficulty taxonomy
- *
+ *
*/
function make_register_taxonomy_difficulty() {
- $labels = array(
+ $labels = array(
'name' => _x( 'Difficulties', 'difficulty' ),
'singular_name' => _x( 'Difficulty', 'difficulty' ),
'search_items' => _x( 'Search Difficulties', 'difficulty' ),
@@ -239,7 +239,7 @@
'menu_name' => _x( 'Difficulties', 'difficulty' ),
);
- $args = array(
+ $args = array(
'labels' => $labels,
'public' => true,
'show_in_nav_menus' => true,
@@ -257,11 +257,11 @@
/**
* Add the tools taxonomy
- *
+ *
*/
function register_taxonomy_tools() {
- $labels = array(
+ $labels = array(
'name' => _x( 'Tools', 'tools' ),
'singular_name' => _x( 'Tool', 'tools' ),
'search_items' => _x( 'Search Tools', 'tools' ),
@@ -279,7 +279,7 @@
'menu_name' => _x( 'Tools', 'tools' ),
);
- $args = array(
+ $args = array(
'labels' => $labels,
'public' => true,
'show_in_nav_menus' => true,
@@ -297,11 +297,11 @@
/**
* Add the parts taxonomy
- *
+ *
*/
function register_taxonomy_parts() {
- $labels = array(
+ $labels = array(
'name' => _x( 'Parts', 'parts' ),
'singular_name' => _x( 'Part', 'parts' ),
'search_items' => _x( 'Search Parts', 'parts' ),
@@ -319,7 +319,7 @@
'menu_name' => _x( 'Parts', 'parts' ),
);
- $args = array(
+ $args = array(
'labels' => $labels,
'public' => true,
'show_in_nav_menus' => true,
@@ -338,11 +338,11 @@
/**
* Add the types taxonomy
- *
+ *
*/
function register_taxonomy_types() {
- $labels = array(
+ $labels = array(
'name' => _x( 'Types', 'types' ),
'singular_name' => _x( 'Type', 'types' ),
'search_items' => _x( 'Search Types', 'types' ),
@@ -360,7 +360,7 @@
'menu_name' => _x( 'Types', 'types' ),
);
- $args = array(
+ $args = array(
'labels' => $labels,
'public' => true,
'show_in_nav_menus' => true,
@@ -376,19 +376,19 @@
/**
* Simple grid for the projects landing page.
- *
+ *
*/
function make_projects_grid( $label, $posts, $taxonomy, $terms ) {
$output = '<div class="' . $label . '">
<div class="page-header">
-
+
<h3>' . $label . '</h3>
-
+
</div>
-
-
+
+
<div class="row-fluid">';
$args = array(
@@ -415,7 +415,7 @@
} elseif ( $posts == 6 ) {
$output .= '<div class="span2">';
}
-
+
$url = get_post_custom_values('Image');
//$url = esc_url($url);
$output .= '<img src="' . wpcom_vip_get_resized_remote_image_url( $url[0] , 293, 200 ) . '" alt="' . esc_attr( get_the_title() ) . '" />';
@@ -424,7 +424,7 @@
$output .= esc_html( $description[0] );
$output .= '</div></div>';
endwhile;
-
+
$output .= '</div>';
$output .= '</div><!--' . $label . '-->';
@@ -433,7 +433,7 @@
/**
* The steps thumbmails for the projects pages.
- *
+ *
*/
function make_projects_steps_nav( $steps ) {
$steps = unserialize($steps[0]);
@@ -455,7 +455,7 @@
echo '<img src="' . get_stylesheet_directory_uri() . '/img/placeholder.jpg" alt="No Image" class="' . esc_attr( $step->number ) . '" />';
}
echo '<h4 class="red">Step #' . esc_html( $step->number ) . '</h4>';
- echo '</div>';
+ echo '</div>';
}
echo '</div>';
}
@@ -464,7 +464,7 @@
/**
* Spit out the steps as a list.
- *
+ *
*/
function make_projects_steps_list( $steps ) {
$steps = unserialize($steps[0]);
@@ -479,8 +479,8 @@
} else {
echo '<a>' . esc_html( $step->number ) . ". " . esc_html( wp_trim_words( stripslashes( $step->lines[0]->text ), 5, '...' ) ) . '</a>';
}
-
- echo '</li>';
+
+ echo '</li>';
}
echo '</ul></div>';
}
@@ -503,10 +503,12 @@
$replace = 'make-images.s3.amazonaws.com/';
if ( $haystack == 'http://cacher.dozuki.net/static/images/make/guide/NoImageMP_96x72.gif' or empty( $haystack ) ) {
return $haystack;
- }
+ }
$str = str_replace( $needle, $replace, $haystack);
- if ( strpos($str, 'make-images') !== true ) {
- return $str . '.jpg';
+ $allowed = array('gif','png' ,'jpg');
+ $ext = pathinfo( $str, PATHINFO_EXTENSION );
+ if( ! in_array( $ext, $allowed ) ) {
+ return $str . '.jpg';
} else {
return $str;
}
@@ -514,7 +516,7 @@
/**
* Full content of the steps.
- *
+ *
*/
function make_projects_steps( $steps, $print = false ) {
$steps = unserialize($steps[0]);
@@ -554,7 +556,7 @@
echo '';
}
echo '</span>';
-
+
$images = $step->images;
if ( isset( $images[0]->text ) ) {
if ( function_exists( 'wpcom_vip_get_resized_remote_image_url' ) ) {
@@ -573,11 +575,15 @@
echo '</span><!--.row-->';
}
$lines = $step->lines;
- echo '<ul>';
- foreach ($lines as $line) {
- echo '<li>' . wp_kses_post( stripslashes( $line->text ) ) . '</li>';
+ if ( isset( $lines[1] ) ) {
+ echo '<ul>';
+ foreach ($lines as $line) {
+ echo '<li>' . wp_kses_post( stripslashes( $line->text ) ) . '</li>';
+ }
+ echo '</ul>';
+ } else {
+ echo Markdown( wp_kses_post( $lines[0]->text ) );
}
- echo '</ul>';
echo '</div><!--.right_column-->';
}
}
@@ -617,7 +623,7 @@
}
$output .= '</a> ';
-
+
$output .= ' <span class="muted">';
$output .= wp_kses_post( $notes );
$output .= '</span>';
Index: includes/projects-manager.php
===================================================================
--- includes/projects-manager.php (revision 141566)
+++ includes/projects-manager.php (working copy)
@@ -59,7 +59,7 @@
* This function is built to be used by any multidimensional array
* @param array REQUIRED $array The array of steps, parts or tools to sort by.
* @param string REQUIRED $sort_field The field in the array you wish to sort by. TODO: Make this happen.
- * @return array
+ * @return array
*
* @version 1.1
* @since GLaDOS
@@ -455,9 +455,9 @@
// Set our images array and contain each image as an object in the Steps object
$int = 0;
-
+
foreach( $data[ 'step-images-' . $i ] as $image ) {
-
+
$image_url = ( ! empty( $image ) ) ? esc_url_raw( $image ) : '';
$step['images'][ $int ] = (object) array(
'imageid' => absint( $data['post_ID'] ),
@@ -611,7 +611,7 @@
///////////////////////
// PARTS
$parts = make_magazine_projects_build_parts_data( $_POST );
-
+
foreach ( $parts as $part ) {
add_post_meta( absint( $post_id ), 'parts', $part );
}
@@ -629,7 +629,7 @@
/**
* Autosave our post meta.
- * Since the core of our projects
+ * Since the core of our projects
* @return void
*
* @since Iron Giant
@@ -646,7 +646,7 @@
//////////////////////////
// STEPS
$step_object = make_magazine_projects_build_step_data( $_POST );
-
+
// Update our post meta for Steps if any exist
update_post_meta( absint( $_POST['post_ID'] ), 'Steps', $step_object );
@@ -654,7 +654,7 @@
///////////////////////
// PARTS
$parts = make_magazine_projects_build_parts_data( $_POST );
-
+
foreach ( $parts as $part ) {
add_post_meta( absint( absint( $_POST['post_ID'] ) ), 'parts', $part );
}
Index: includes/theme_stuff.php
===================================================================
--- includes/theme_stuff.php (revision 141680)
+++ includes/theme_stuff.php (working copy)
@@ -280,7 +280,8 @@
wp_enqueue_script( 'make-projects', get_stylesheet_directory_uri() . '/js/projects.js', array( 'jquery' ), false, true );
wp_enqueue_script( 'make-header', get_stylesheet_directory_uri() . '/js/header.js', array( 'jquery' ), false, true );
wp_enqueue_script( 'make-oembed', get_stylesheet_directory_uri() . '/js/jquery.oembed.js', array( 'jquery' ) );
-
+ // File Input
+ wp_enqueue_script( 'make-fileinput', get_stylesheet_directory_uri() . '/js/fileinput.js', array( 'jquery' ) );
// display our map sort plugin for Maker Camp
if ( is_page( 315793 ) )
wp_enqueue_script( 'make-sort-table', get_stylesheet_directory_uri() . '/js/jquery.tablesorter.min.js', array( 'jquery' ), false, true );
@@ -1811,4 +1812,4 @@
}
-add_action( 'category_top', 'make_get_banner_to_category_page' );
\ No newline at end of file
+add_action( 'category_top', 'make_get_banner_to_category_page' );
Index: js/bootstrap.file-input.min.js
===================================================================
--- js/bootstrap.file-input.min.js (revision 0)
+++ js/bootstrap.file-input.min.js (working copy)
@@ -0,0 +1 @@
+jQuery(function($){$.fn.bootstrapFileInput=function(){this.each(function(e,t){var i=$(t);if("undefined"==typeof i.attr("data-bfi-disabled")){var n="Browse";"undefined"!=typeof i.attr("title")&&(n=i.attr("title"));var p="";i.attr("class")&&(p=" "+i.attr("class")),i.wrap('<a class="file-input-wrapper btn btn-default '+p+'"></a>').parent().prepend(n)}}).promise().done(function(){$(".file-input-wrapper").mousemove(function(e){var t,i,n,p,a,r,f,l;i=$(this),t=i.find("input"),n=i.offset().left,p=i.offset().top,a=t.width(),r=t.height(),f=e.pageX,l=e.pageY,moveInputX=f-n-a+20,moveInputY=l-p-r/2,t.css({left:moveInputX,top:moveInputY})}),$("body").on("change",".file-input-wrapper input[type=file]",function(){var e;e=$(this).val(),$(this).parent().next(".file-input-name").remove(),e=$(this).prop("files")&&$(this).prop("files").length>1?$(this)[0].files.length+" files":e.substring(e.lastIndexOf("\\")+1,e.length),$(this).parent().after('<span class="file-input-name">'+e+"</span>")})})};var e="<style>.file-input-wrapper { overflow: hidden; position: relative; cursor: pointer; z-index: 1; }.file-input-wrapper input[type=file], .file-input-wrapper input[type=file]:focus, .file-input-wrapper input[type=file]:hover { position: absolute; top: 0; left: 0; cursor: pointer; opacity: 0; filter: alpha(opacity=0); z-index: 99; outline: 0; }.file-input-name { margin-left: 8px; }</style>";$("link[rel=stylesheet]").eq(0).before(e)});
\ No newline at end of file
Index: js/fileinput.js
===================================================================
--- js/fileinput.js (revision 0)
+++ js/fileinput.js (working copy)
@@ -0,0 +1,192 @@
+/* ===========================================================
+ * Bootstrap: fileinput.js v3.1.0
+ * http://jasny.github.com/bootstrap/javascript/#fileinput
+ * ===========================================================
+ * Copyright 2012-2014 Arnold Daniels
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
++function ($) { "use strict";
+
+ var isIE = window.navigator.appName == 'Microsoft Internet Explorer';
+ // FILEUPLOAD PUBLIC CLASS DEFINITION
+ // =================================
+
+ var Fileinput = function (element, options) {
+ this.$element = $(element);
+
+ this.$input = this.$element.find(':file');
+ if (this.$input.length === 0) return;
+
+ this.name = this.$input.attr('name') || options.name;
+
+ this.$hidden = this.$element.find('input[type=hidden][name="' + this.name + '"]');
+ if (this.$hidden.length === 0) {
+ this.$hidden = $('<input type="hidden" />');
+ this.$element.prepend(this.$hidden);
+ }
+
+ this.$preview = this.$element.find('.fileinput-preview');
+ var height = this.$preview.css('height');
+ if (this.$preview.css('display') != 'inline' && height != '0px' && height != 'none') this.$preview.css('line-height', height);
+
+ this.original = {
+ exists: this.$element.hasClass('fileinput-exists'),
+ preview: this.$preview.html(),
+ hiddenVal: this.$hidden.val()
+ };
+
+ this.listen();
+ };
+
+ Fileinput.prototype.listen = function() {
+ this.$input.on('change.bs.fileinput', $.proxy(this.change, this));
+ $(this.$input[0].form).on('reset.bs.fileinput', $.proxy(this.reset, this));
+
+ this.$element.find('[data-trigger="fileinput"]').on('click.bs.fileinput', $.proxy(this.trigger, this));
+ this.$element.find('[data-dismiss="fileinput"]').on('click.bs.fileinput', $.proxy(this.clear, this));
+ },
+
+ Fileinput.prototype.change = function(e) {
+ if (e.target.files === undefined) e.target.files = e.target && e.target.value ? [ {name: e.target.value.replace(/^.+\\/, '')} ] : [];
+ if (e.target.files.length === 0) return;
+
+ this.$hidden.val('');
+ this.$hidden.attr('name', '');
+ this.$input.attr('name', this.name);
+
+ var file = e.target.files[0];
+
+ if (this.$preview.length > 0 && (typeof file.type !== "undefined" ? file.type.match('image.*') : file.name.match(/\.(gif|png|jpe?g)$/i)) && typeof FileReader !== "undefined") {
+ var reader = new FileReader();
+ var preview = this.$preview;
+ var element = this.$element;
+
+ reader.onload = function(re) {
+ var $img = $('<img>'); // .attr('src', re.target.result)
+ $img[0].src = re.target.result;
+ e.target.files[0].result = re.target.result;
+
+ element.find('.fileinput-filename').text(file.name);
+
+ // if parent has max-height, using `(max-)height: 100%` on child doesn't take padding and border into account
+ if (preview.css('max-height') != 'none') $img.css('max-height', parseInt(preview.css('max-height'), 10) - parseInt(preview.css('padding-top'), 10) - parseInt(preview.css('padding-bottom'), 10) - parseInt(preview.css('border-top'), 10) - parseInt(preview.css('border-bottom'), 10));
+
+ preview.html($img);
+ element.addClass('fileinput-exists').removeClass('fileinput-new');
+
+ element.trigger('change.bs.fileinput', e.target.files);
+ element.trigger('image_added');;
+
+ };
+
+ reader.readAsDataURL(file);
+ } else {
+ this.$element.find('.fileinput-filename').text(file.name);
+ this.$preview.text(file.name);
+
+ this.$element.addClass('fileinput-exists').removeClass('fileinput-new');
+
+ this.$element.trigger('change.bs.fileinput');
+ }
+ },
+
+ Fileinput.prototype.clear = function(e) {
+ if (e) e.preventDefault();
+
+ this.$hidden.val('');
+ this.$hidden.attr('name', this.name);
+ this.$input.attr('name', '');
+
+ //ie8+ doesn't support changing the value of input with type=file so clone instead
+ if (isIE) {
+ var inputClone = this.$input.clone(true);
+ this.$input.after(inputClone);
+ this.$input.remove();
+ this.$input = inputClone;
+ } else {
+ this.$input.val('');
+ }
+
+ this.$preview.html('');
+ this.$element.find('.fileinput-filename').text('');
+ this.$element.addClass('fileinput-new').removeClass('fileinput-exists');
+
+ if (e !== false) {
+ this.$input.trigger('change');
+ this.$element.trigger('clear.bs.fileinput');
+ }
+ },
+
+ Fileinput.prototype.reset = function() {
+ this.clear(false);
+
+ this.$hidden.val(this.original.hiddenVal);
+ this.$preview.html(this.original.preview);
+ this.$element.find('.fileinput-filename').text('');
+
+ if (this.original.exists) this.$element.addClass('fileinput-exists').removeClass('fileinput-new');
+ else this.$element.addClass('fileinput-new').removeClass('fileinput-exists');
+
+ this.$element.trigger('reset.bs.fileinput');
+ },
+
+ Fileinput.prototype.trigger = function(e) {
+ this.$input.trigger('click');
+ e.preventDefault();
+ };
+
+
+ // FILEUPLOAD PLUGIN DEFINITION
+ // ===========================
+
+ var old = $.fn.fileinput;
+
+ $.fn.fileinput = function (options) {
+ return this.each(function () {
+ var $this = $(this),
+ data = $this.data('fileinput');
+ if (!data) $this.data('fileinput', (data = new Fileinput(this, options)));
+ if (typeof options == 'string') data[options]();
+ });
+ };
+
+ $.fn.fileinput.Constructor = Fileinput;
+
+
+ // FILEINPUT NO CONFLICT
+ // ====================
+
+ $.fn.fileinput.noConflict = function () {
+ $.fn.fileinput = old;
+ return this;
+ };
+
+
+ // FILEUPLOAD DATA-API
+ // ==================
+
+ $(document).on('click.fileinput.data-api', '[data-provides="fileinput"]', function (e) {
+ var $this = $(this);
+ if ($this.data('fileinput')) return;
+ $this.fileinput($this.data());
+
+ var $target = $(e.target).closest('[data-dismiss="fileinput"],[data-trigger="fileinput"]');
+ if ($target.length > 0) {
+ e.preventDefault();
+ $target.trigger('click.bs.fileinput');
+ }
+ });
+
+}(window.jQuery);
\ No newline at end of file
Index: js/parsley.min.js
===================================================================
--- js/parsley.min.js (revision 0)
+++ js/parsley.min.js (working copy)
@@ -0,0 +1,10 @@
+/*!
+* Parsleyjs
+* Guillaume Potier - <guillaume@wisembly.com>
+* Version 2.0.0-rc4 - built Sun Mar 23 2014 14:09:58
+* MIT Licensed
+* Form Validation
+*
+*/
+!function(a){var b={attr:function(a,b,c){var d,e={},f=new RegExp("^"+b,"i");if("undefined"==typeof a||"undefined"==typeof a[0])return{};for(var g in a[0].attributes)if(d=a[0].attributes[g],"undefined"!=typeof d&&null!==d&&d.specified&&f.test(d.name)){if("undefined"!=typeof c&&new RegExp(c+"$","i").test(d.name))return!0;e[this.camelize(d.name.replace(b,""))]=this.deserializeValue(d.value)}return"undefined"==typeof c?e:!1},setAttr:function(a,b,c,d){a[0].setAttribute(this.dasherize(b+c),String(d))},get:function(a,b){for(var c=0,d=(b||"").split(".");this.isObject(a)||this.isArray(a);)if(a=a[d[c++]],c===d.length)return a;return void 0},hash:function(a){return String(Math.random()).substring(2,a?a+2:9)},isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)},isObject:function(a){return a===Object(a)},deserializeValue:function(b){var c;try{return b?"true"==b||("false"==b?!1:"null"==b?null:isNaN(c=Number(b))?/^[\[\{]/.test(b)?a.parseJSON(b):b:c):b}catch(d){return b}},camelize:function(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})},dasherize:function(a){return a.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}},c={namespace:"data-parsley-",inputs:"input, textarea, select",excluded:"input[type=button], input[type=submit], input[type=reset]",priorityEnabled:!0,uiEnabled:!0,validationThreshold:3,focus:"first",trigger:!1,errorClass:"parsley-error",successClass:"parsley-success",classHandler:function(){},errorsContainer:function(){},errorsWrapper:'<ul class="parsley-errors-list"></ul>',errorTemplate:"<li></li>"},d=function(){};d.prototype={asyncSupport:!1,actualizeOptions:function(){return this.options=this.parsleyInstance.OptionsFactory.get(this),this},validateThroughValidator:function(a,b,c){return window.ParsleyValidator.validate.apply(window.ParsleyValidator,[a,b,c])},subscribe:function(b,c){return a.listenTo(this,b.toLowerCase(),c),this},unsubscribe:function(b){return a.unsubscribeTo(this,b.toLowerCase()),this},reset:function(){if("ParsleyForm"!==this.__class__)return a.emit("parsley:field:reset",this);for(var b=0;b<this.fields.length;b++)a.emit("parsley:field:reset",this.fields[b]);a.emit("parsley:form:reset",this)},destroy:function(){if("ParsleyForm"!==this.__class__)return a.emit("parsley:field:destroy",this),void this.$element.removeData("Parsley");for(var b=0;b<this.fields.length;b++)this.fields[b].destroy();a.emit("parsley:form:destroy",this),this.$element.removeData("Parsley")}},function(a){var b=function(a){return this.__class__="Validator",this.__version__="0.5.8",this.options=a||{},this.bindingKey=this.options.bindingKey||"_validatorjsConstraint",this};b.prototype={constructor:b,validate:function(a,b,c){if("string"!=typeof a&&"object"!=typeof a)throw new Error("You must validate an object or a string");return"string"==typeof a||g(a)?this._validateString(a,b,c):this.isBinded(a)?this._validateBindedObject(a,b):this._validateObject(a,b,c)},bind:function(a,b){if("object"!=typeof a)throw new Error("Must bind a Constraint to an object");return a[this.bindingKey]=new c(b),this},unbind:function(a){return"undefined"==typeof a._validatorjsConstraint?this:(delete a[this.bindingKey],this)},isBinded:function(a){return"undefined"!=typeof a[this.bindingKey]},getBinded:function(a){return this.isBinded(a)?a[this.bindingKey]:null},_validateString:function(a,b,c){var f,h=[];g(b)||(b=[b]);for(var i=0;i<b.length;i++){if(!(b[i]instanceof e))throw new Error("You must give an Assert or an Asserts array to validate a string");f=b[i].check(a,c),f instanceof d&&h.push(f)}return h.length?h:!0},_validateObject:function(a,b,d){if("object"!=typeof b)throw new Error("You must give a constraint to validate an object");return b instanceof c?b.check(a,d):new c(b).check(a,d)},_validateBindedObject:function(a,b){return a[this.bindingKey].check(a,b)}},b.errorCode={must_be_a_string:"must_be_a_string",must_be_an_array:"must_be_an_array",must_be_a_number:"must_be_a_number",must_be_a_string_or_array:"must_be_a_string_or_array"};var c=function(a,b){if(this.__class__="Constraint",this.options=b||{},this.nodes={},a)try{this._bootstrap(a)}catch(c){throw new Error("Should give a valid mapping object to Constraint",c,a)}return this};c.prototype={constructor:c,check:function(a,b){var c,d={};for(var h in this.options.strict?this.nodes:a)if(this.options.strict?this.has(h,a):this.has(h))c=this._check(h,a[h],b),(g(c)&&c.length>0||!g(c)&&!f(c))&&(d[h]=c);else if(this.options.strict)try{(new e).HaveProperty(h).validate(a)}catch(i){d[h]=i}return f(d)?!0:d},add:function(a,b){if(b instanceof e||g(b)&&b[0]instanceof e)return this.nodes[a]=b,this;if("object"==typeof b&&!g(b))return this.nodes[a]=b instanceof c?b:new c(b),this;throw new Error("Should give an Assert, an Asserts array, a Constraint",b)},has:function(a,b){return b="undefined"!=typeof b?b:this.nodes,"undefined"!=typeof b[a]},get:function(a,b){return this.has(a)?this.nodes[a]:b||null},remove:function(a){var b=[];for(var c in this.nodes)c!==a&&(b[c]=this.nodes[c]);return this.nodes=b,this},_bootstrap:function(a){if(a instanceof c)return this.nodes=a.nodes;for(var b in a)this.add(b,a[b])},_check:function(a,b,d){if(this.nodes[a]instanceof e)return this._checkAsserts(b,[this.nodes[a]],d);if(g(this.nodes[a]))return this._checkAsserts(b,this.nodes[a],d);if(this.nodes[a]instanceof c)return this.nodes[a].check(b,d);throw new Error("Invalid node",this.nodes[a])},_checkAsserts:function(a,b,c){for(var d,e=[],f=0;f<b.length;f++)d=b[f].check(a,c),"undefined"!=typeof d&&!0!==d&&e.push(d);return e}};var d=function(a,b,c){if(this.__class__="Violation",!(a instanceof e))throw new Error("Should give an assertion implementing the Assert interface");this.assert=a,this.value=b,"undefined"!=typeof c&&(this.violation=c)};d.prototype={show:function(){var a={assert:this.assert.__class__,value:this.value};return this.violation&&(a.violation=this.violation),a},__toString:function(){return"undefined"!=typeof this.violation&&(this.violation='", '+this.getViolation().constraint+" expected was "+this.getViolation().expected),this.assert.__class__+' assert failed for "'+this.value+this.violation||""},getViolation:function(){var a,b;for(a in this.violation)b=this.violation[a];return{constraint:a,expected:b}}};var e=function(a){return this.__class__="Assert",this.__parentClass__=this.__class__,this.groups=[],"undefined"!=typeof a&&this.addGroup(a),this};e.prototype={construct:e,check:function(a,b){if(!(b&&!this.hasGroup(b)||!b&&this.hasGroups()))try{return this.validate(a,b)}catch(c){return c}},hasGroup:function(a){return g(a)?this.hasOneOf(a):"Any"===a?!0:this.hasGroups()?-1!==this.groups.indexOf(a):"Default"===a},hasOneOf:function(a){for(var b=0;b<a.length;b++)if(this.hasGroup(a[b]))return!0;return!1},hasGroups:function(){return this.groups.length>0},addGroup:function(a){return g(a)?this.addGroups(a):(this.hasGroup(a)||this.groups.push(a),this)},removeGroup:function(a){for(var b=[],c=0;c<this.groups.length;c++)a!==this.groups[c]&&b.push(this.groups[c]);return this.groups=b,this},addGroups:function(a){for(var b=0;b<a.length;b++)this.addGroup(a[b]);return this},HaveProperty:function(a){return this.__class__="HaveProperty",this.node=a,this.validate=function(a){if("undefined"==typeof a[this.node])throw new d(this,a,{value:this.node});return!0},this},Blank:function(){return this.__class__="Blank",this.validate=function(a){if("string"!=typeof a)throw new d(this,a,{value:b.errorCode.must_be_a_string});if(""!==a.replace(/^\s+/g,"").replace(/\s+$/g,""))throw new d(this,a);return!0},this},Callback:function(a){if(this.__class__="Callback",this.arguments=Array.prototype.slice.call(arguments),1===this.arguments.length?this.arguments=[]:this.arguments.splice(0,1),"function"!=typeof a)throw new Error("Callback must be instanciated with a function");return this.fn=a,this.validate=function(a){var b=this.fn.apply(this,[a].concat(this.arguments));if(!0!==b)throw new d(this,a,{result:b});return!0},this},Choice:function(a){if(this.__class__="Choice",!g(a)&&"function"!=typeof a)throw new Error("Choice must be instanciated with an array or a function");return this.list=a,this.validate=function(a){for(var b="function"==typeof this.list?this.list():this.list,c=0;c<b.length;c++)if(a===b[c])return!0;throw new d(this,a,{choices:b})},this},Collection:function(a){return this.__class__="Collection",this.constraint="undefined"!=typeof a?new c(a):!1,this.validate=function(a,c){var e,h=new b,i=0,j={},k=this.groups.length?this.groups:c;if(!g(a))throw new d(this,array,{value:b.errorCode.must_be_an_array});for(var l=0;l<a.length;l++)e=this.constraint?h.validate(a[l],this.constraint,k):h.validate(a[l],k),f(e)||(j[i]=e),i++;return f(j)?!0:j},this},Count:function(a){return this.__class__="Count",this.count=a,this.validate=function(a){if(!g(a))throw new d(this,a,{value:b.errorCode.must_be_an_array});var c="function"==typeof this.count?this.count(a):this.count;if(isNaN(Number(c)))throw new Error("Count must be a valid interger",c);if(c!==a.length)throw new d(this,a,{count:c});return!0},this},Email:function(){return this.__class__="Email",this.validate=function(a){var c=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;if("string"!=typeof a)throw new d(this,a,{value:b.errorCode.must_be_a_string});if(!c.test(a))throw new d(this,a);return!0},this},Eql:function(a){if(this.__class__="Eql","undefined"==typeof a)throw new Error("Equal must be instanciated with an Array or an Object");return this.eql=a,this.validate=function(a){var b="function"==typeof this.eql?this.eql(a):this.eql;if(!h.eql(b,a))throw new d(this,a,{eql:b});return!0},this},EqualTo:function(a){if(this.__class__="EqualTo","undefined"==typeof a)throw new Error("EqualTo must be instanciated with a value or a function");return this.reference=a,this.validate=function(a){var b="function"==typeof this.reference?this.reference(a):this.reference;if(b!==a)throw new d(this,a,{value:b});return!0},this},GreaterThan:function(a){if(this.__class__="GreaterThan","undefined"==typeof a)throw new Error("Should give a threshold value");return this.threshold=a,this.validate=function(a){if(""===a||isNaN(Number(a)))throw new d(this,a,{value:b.errorCode.must_be_a_number});if(this.threshold>=a)throw new d(this,a,{threshold:this.threshold});return!0},this},GreaterThanOrEqual:function(a){if(this.__class__="GreaterThanOrEqual","undefined"==typeof a)throw new Error("Should give a threshold value");return this.threshold=a,this.validate=function(a){if(""===a||isNaN(Number(a)))throw new d(this,a,{value:b.errorCode.must_be_a_number});if(this.threshold>a)throw new d(this,a,{threshold:this.threshold});return!0},this},InstanceOf:function(a){if(this.__class__="InstanceOf","undefined"==typeof a)throw new Error("InstanceOf must be instanciated with a value");return this.classRef=a,this.validate=function(a){if(!0!=a instanceof this.classRef)throw new d(this,a,{classRef:this.classRef});return!0},this},IPv4:function(){return this.__class__="IPv4",this.validate=function(a){var c=/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;if("string"!=typeof a)throw new d(this,a,{value:b.errorCode.must_be_a_string});if(!c.test(a))throw new d(this,a);return!0},this},Length:function(a){if(this.__class__="Length",!a.min&&!a.max)throw new Error("Lenth assert must be instanciated with a { min: x, max: y } object");return this.min=a.min,this.max=a.max,this.validate=function(a){if("string"!=typeof a&&!g(a))throw new d(this,a,{value:b.errorCode.must_be_a_string_or_array});if("undefined"!=typeof this.min&&this.min===this.max&&a.length!==this.min)throw new d(this,a,{min:this.min,max:this.max});if("undefined"!=typeof this.max&&a.length>this.max)throw new d(this,a,{max:this.max});if("undefined"!=typeof this.min&&a.length<this.min)throw new d(this,a,{min:this.min});return!0},this},LessThan:function(a){if(this.__class__="LessThan","undefined"==typeof a)throw new Error("Should give a threshold value");return this.threshold=a,this.validate=function(a){if(""===a||isNaN(Number(a)))throw new d(this,a,{value:b.errorCode.must_be_a_number});if(this.threshold<=a)throw new d(this,a,{threshold:this.threshold});return!0},this},LessThanOrEqual:function(a){if(this.__class__="LessThanOrEqual","undefined"==typeof a)throw new Error("Should give a threshold value");return this.threshold=a,this.validate=function(a){if(""===a||isNaN(Number(a)))throw new d(this,a,{value:b.errorCode.must_be_a_number});if(this.threshold<a)throw new d(this,a,{threshold:this.threshold});return!0},this},Mac:function(){return this.__class__="Mac",this.validate=function(a){var c=/^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$/i;if("string"!=typeof a)throw new d(this,a,{value:b.errorCode.must_be_a_string});if(!c.test(a))throw new d(this,a);return!0},this},NotNull:function(){return this.__class__="NotNull",this.validate=function(a){if(null===a||"undefined"==typeof a)throw new d(this,a);return!0},this},NotBlank:function(){return this.__class__="NotBlank",this.validate=function(a){if("string"!=typeof a)throw new d(this,a,{value:b.errorCode.must_be_a_string});if(""===a.replace(/^\s+/g,"").replace(/\s+$/g,""))throw new d(this,a);return!0},this},Null:function(){return this.__class__="Null",this.validate=function(a){if(null!==a)throw new d(this,a);return!0},this},Range:function(a,b){if(this.__class__="Range","undefined"==typeof a||"undefined"==typeof b)throw new Error("Range assert expects min and max values");return this.min=a,this.max=b,this.validate=function(a){try{return"string"==typeof a&&isNaN(Number(a))||g(a)?(new e).Length({min:this.min,max:this.max}).validate(a):(new e).GreaterThanOrEqual(this.min).validate(a)&&(new e).LessThanOrEqual(this.max).validate(a),!0}catch(b){throw new d(this,a,b.violation)}return!0},this},Regexp:function(a,c){if(this.__class__="Regexp","undefined"==typeof a)throw new Error("You must give a regexp");return this.regexp=a,this.flag=c||"",this.validate=function(a){if("string"!=typeof a)throw new d(this,a,{value:b.errorCode.must_be_a_string});if(!new RegExp(this.regexp,this.flag).test(a))throw new d(this,a,{regexp:this.regexp,flag:this.flag});return!0},this},Required:function(){return this.__class__="Required",this.validate=function(a){if("undefined"==typeof a)throw new d(this,a);try{"string"==typeof a?(new e).NotNull().validate(a)&&(new e).NotBlank().validate(a):!0===g(a)&&(new e).Length({min:1}).validate(a)}catch(b){throw new d(this,a)}return!0},this},Unique:function(a){return this.__class__="Unique","object"==typeof a&&(this.key=a.key),this.validate=function(a){var c,e=[];if(!g(a))throw new d(this,a,{value:b.errorCode.must_be_an_array});for(var f=0;f<a.length;f++)if(c="object"==typeof a[f]?a[f][this.key]:a[f],"undefined"!=typeof c){if(-1!==e.indexOf(c))throw new d(this,a,{value:c});e.push(c)}return!0},this}},a.Assert=e,a.Validator=b,a.Violation=d,a.Constraint=c,Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>1&&(d=Number(arguments[1]),d!=d?d=0:0!==d&&1/0!=d&&d!=-1/0&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1});var f=function(a){for(var b in a)return!1;return!0},g=function(a){return"[object Array]"===Object.prototype.toString.call(a)},h={eql:function(a,b){if(a===b)return!0;if("undefined"!=typeof Buffer&&Buffer.isBuffer(a)&&Buffer.isBuffer(b)){if(a.length!==b.length)return!1;for(var c=0;c<a.length;c++)if(a[c]!==b[c])return!1;return!0}return a instanceof Date&&b instanceof Date?a.getTime()===b.getTime():"object"!=typeof a&&"object"!=typeof b?a==b:this.objEquiv(a,b)},isUndefinedOrNull:function(a){return null===a||"undefined"==typeof a},isArguments:function(a){return"[object Arguments]"==Object.prototype.toString.call(a)},keys:function(a){if(Object.keys)return Object.keys(a);var b=[];for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&b.push(c);return b},objEquiv:function(a,b){if(this.isUndefinedOrNull(a)||this.isUndefinedOrNull(b))return!1;if(a.prototype!==b.prototype)return!1;if(this.isArguments(a))return this.isArguments(b)?eql(pSlice.call(a),pSlice.call(b)):!1;try{var c,d,e=this.keys(a),f=this.keys(b);if(e.length!==f.length)return!1;for(e.sort(),f.sort(),d=e.length-1;d>=0;d--)if(e[d]!=f[d])return!1;for(d=e.length-1;d>=0;d--)if(c=e[d],!this.eql(a[c],b[c]))return!1;return!0}catch(g){return!1}}};"function"==typeof define&&define.amd&&define("validator",[],function(){return a})}("undefined"==typeof exports?this["undefined"!=typeof validatorjs_ns?validatorjs_ns:"Validator"]={}:exports);var e=function(a,b){this.__class__="ParsleyValidator",this.Validator=Validator,this.locale="en",this.init(a||{},b||{})};e.prototype={init:function(b,c){this.catalog=c;for(var d in b)this.addValidator(d,b[d].fn,b[d].priority);a.emit("parsley:validator:init")},setLocale:function(a){if("undefined"==typeof this.catalog[a])throw new Error(a+" is not available in the catalog");return this.locale=a,this},addCatalog:function(a,b,c){return"object"==typeof b&&(this.catalog[a]=b),!0===c?this.setLocale(a):this},addMessage:function(a,b,c){void 0===typeof this.catalog[a]&&(this.catalog[a]={}),this.catalog[a][b]=c},validate:function(){return(new this.Validator.Validator).validate.apply(new Validator.Validator,arguments)},addValidator:function(b,c,d){return this.validators[b]=function(b){return a.extend((new Validator.Assert).Callback(c,b),{priority:d})},this},updateValidator:function(a,b,c){return addValidator(a,b,c)},removeValidator:function(a){return delete this.validators[a],this},getErrorMessage:function(a){var b;return b="type"===a.name?window.ParsleyConfig.i18n[this.locale][a.name][a.requirements]:this.formatMesssage(window.ParsleyConfig.i18n[this.locale][a.name],a.requirements),""!==b?b:window.ParsleyConfig.i18n[this.locale].defaultMessage},formatMesssage:function(a,b){if("object"==typeof b){for(var c in b)a=this.formatMesssage(a,b[c]);return a}return"string"==typeof a?a.replace(new RegExp("%s","i"),b):""},validators:{notblank:function(){return a.extend((new Validator.Assert).NotBlank(),{priority:2})},required:function(){return a.extend((new Validator.Assert).Required(),{priority:512})},type:function(b){var c;switch(b){case"email":c=(new Validator.Assert).Email();break;case"number":c=(new Validator.Assert).Regexp("^-?(?:\\d+|\\d{1,3}(?:,\\d{3})+)?(?:\\.\\d+)?$");break;case"integer":c=(new Validator.Assert).Regexp("^-?\\d+$");break;case"digits":c=(new Validator.Assert).Regexp("^\\d+$");break;case"alphanum":c=(new Validator.Assert).Regexp("^\\w+$","i");break;case"url":c=(new Validator.Assert).Regexp("(https?:\\/\\/)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)","i");break;default:throw new Error("validator type `"+b+"` is not supported")}return a.extend(c,{priority:256})},pattern:function(b){var c="";return/^\/.*\/(?:[gimy]*)$/.test(b)&&(c=b.replace(/.*\/([gimy]*)$/,"$1"),b=b.replace(new RegExp("^/(.*?)/"+c+"$"),"$1")),a.extend((new Validator.Assert).Regexp(b,c),{priority:64})},minlength:function(b){return a.extend((new Validator.Assert).Length({min:b}),{priority:30,requirementsTransformer:function(){return"string"!=typeof b||isNaN(b)?b:parseInt(b,10)}})},maxlength:function(b){return a.extend((new Validator.Assert).Length({max:b}),{priority:30,requirementsTransformer:function(){return"string"!=typeof b||isNaN(b)?b:parseInt(b,10)}})},length:function(b){return a.extend((new Validator.Assert).Length({min:b[0],max:b[1]}),{priority:32})},mincheck:function(a){return this.minlength(a)},maxcheck:function(a){return this.maxlength(a)},check:function(a){return this.length(a)},min:function(b){return a.extend((new Validator.Assert).GreaterThanOrEqual(b),{priority:30,requirementsTransformer:function(){return"string"!=typeof b||isNaN(b)?b:parseInt(b,10)}})},max:function(b){return a.extend((new Validator.Assert).LessThanOrEqual(b),{priority:30,requirementsTransformer:function(){return"string"!=typeof b||isNaN(b)?b:parseInt(b,10)}})},range:function(b){return a.extend((new Validator.Assert).Range(b[0],b[1]),{priority:32,requirementsTransformer:function(){for(var a=0;a<b.length;a++)b[a]="string"!=typeof b[a]||isNaN(b[a])?b[a]:parseInt(b[a],10);return b}})},equalto:function(b){return a.extend((new Validator.Assert).EqualTo(b),{priority:256,requirementsTransformer:function(){return a(b).length?a(b).val():b}})}}};var f=function(){this.__class__="ParsleyUI"};f.prototype={listen:function(){return a.listen("parsley:form:init",this,this.setupForm),a.listen("parsley:field:init",this,this.setupField),a.listen("parsley:field:validated",this,this.reflow),a.listen("parsley:form:validated",this,this.focus),a.listen("parsley:field:reset",this,this.reset),a.listen("parsley:form:destroy",this,this.destroy),a.listen("parsley:field:destroy",this,this.destroy),this},reflow:function(a){if("undefined"!=typeof a._ui&&!1!==a._ui.active){var b=this._diff(a.validationResult,a._ui.lastValidationResult);a._ui.lastValidationResult=a.validationResult,a._ui.validatedOnce=!0,this.manageStatusClass(a),this.manageErrorsMessages(a,b),this.actualizeTriggers(a),(b.kept.length||b.added.length)&&"undefined"==typeof a._ui.failedOnce&&this.manageFailingFieldTrigger(a)}},manageStatusClass:function(a){!0===a.validationResult?this._successClass(a):a.validationResult.length>0?this._errorClass(a):this._resetClass(a)},manageErrorsMessages:function(b,c){if("undefined"==typeof b.options.errorsMessagesDisabled){if("undefined"!=typeof b.options.errorMessage)return void(c.added.length||c.kept.length?(0===b._ui.$errorsWrapper.find(".parsley-custom-error-message").length&&b._ui.$errorsWrapper.append(a(b.options.errorTemplate).addClass("parsley-custom-error-message")),b._ui.$errorsWrapper.addClass("filled").find(".parsley-custom-error-message").html(b.options.errorMessage)):b._ui.$errorsWrapper.removeClass("filled").find(".parsley-custom-error-message").remove());for(var d=0;d<c.removed.length;d++)this.removeError(b,c.removed[d].assert.name,!0);for(d=0;d<c.added.length;d++)this.addError(b,c.added[d].assert.name,void 0,c.added[d].assert,!0);for(d=0;d<c.kept.length;d++)this.updateError(b,c.kept[d].assert.name,void 0,c.kept[d].assert,!0)}},addError:function(b,c,d,e,f){b._ui.$errorsWrapper.addClass("filled").append(a(b.options.errorTemplate).addClass("parsley-"+c).html(d||this._getErrorMessage(b,e))),!0!==f&&this._errorClass(b)},updateError:function(a,b,c,d,e){a._ui.$errorsWrapper.addClass("filled").find(".parsley-"+b).html(c||this._getErrorMessage(a,d)),!0!==e&&this._errorClass(a)},removeError:function(a,b,c){a._ui.$errorsWrapper.removeClass("filled").find(".parsley-"+b).remove(),!0!==c&&this.manageStatusClass(a)},focus:function(a){if(!0===a.validationResult||"none"===a.options.focus)return a._focusedField=null;a._focusedField=null;for(var b=0;b<a.fields.length;b++)if(!0!==a.fields[b].validationResult&&a.fields[b].validationResult.length>0&&"undefined"==typeof a.fields[b].options.noFocus){if("first"===a.options.focus)return a._focusedField=a.fields[b].$element,a._focusedField.focus();a._focusedField=a.fields[b].$element}return null===a._focusedField?null:a._focusedField.focus()},_getErrorMessage:function(a,b){var c=b.name+"Message";return"undefined"!=typeof a.options[c]?a.options[c]:window.ParsleyValidator.getErrorMessage(b)},_diff:function(a,b,c){for(var d=[],e=[],f=0;f<a.length;f++){for(var g=!1,h=0;h<b.length;h++)if(a[f].assert.name===b[h].assert.name){g=!0;break}g?e.push(a[f]):d.push(a[f])}return{kept:e,added:d,removed:c?[]:this._diff(b,a,!0).added}},setupForm:function(b){b.$element.on("submit.Parsley",!1,a.proxy(b.onSubmitValidate,b)),!1!==b.options.uiEnabled&&b.$element.attr("novalidate","")},setupField:function(b){var c={active:!1};!1!==b.options.uiEnabled&&(c.active=!0,b.$element.attr(b.options.namespace+"id",b.__id__),c.$errorClassHandler=this._manageClassHandler(b),c.errorsWrapperId="parsley-id-"+("undefined"!=typeof b.options.multiple?"multiple-"+b.options.multiple:b.__id__),c.$errorsWrapper=a(b.options.errorsWrapper).attr("id",c.errorsWrapperId),c.lastValidationResult=[],c.validatedOnce=!1,c.validationInformationVisible=!1,b._ui=c,this._insertErrorWrapper(b),this.actualizeTriggers(b))},_manageClassHandler:function(b){if("string"==typeof b.options.classHandler&&a(b.options.classHandler).length)return a(b.options.classHandler);var c=b.options.classHandler(b);return"undefined"!=typeof c&&c.length?c:"undefined"==typeof b.options.multiple||b.$element.is("select")?b.$element:b.$element.parent()},_insertErrorWrapper:function(b){var c;if("string"==typeof b.options.errorsContainer){if(a(b.options.errorsContainer+"").length)return a(b.options.errorsContainer).append(b._ui.$errorsWrapper);window.console&&window.console.warn&&window.console.warn("The errors container `"+b.options.errorsContainer+"` does not exist in DOM")}return"function"==typeof b.options.errorsContainer&&(c=b.options.errorsContainer(b)),"undefined"!=typeof c&&c.length?c.append(b._ui.$errorsWrapper):"undefined"==typeof b.options.multiple?b.$element.after(b._ui.$errorsWrapper):b.$element.parent().after(b._ui.$errorsWrapper)},actualizeTriggers:function(b){var c=this;if(b.options.multiple?a("["+b.options.namespace+'multiple="'+b.options.multiple+'"]').each(function(){a(this).off(".Parsley")}):b.$element.off(".Parsley"),!1!==b.options.trigger){var d=b.options.trigger.replace(/^\s+/g,"").replace(/\s+$/g,"");""!==d&&(b.options.multiple?a("["+b.options.namespace+'multiple="'+b.options.multiple+'"]').each(function(){a(this).on(d.split(" ").join(".Parsley ")+".Parsley",!1,a.proxy("function"==typeof b.eventValidate?b.eventValidate:c.eventValidate,b))}):b.$element.on(d.split(" ").join(".Parsley ")+".Parsley",!1,a.proxy("function"==typeof b.eventValidate?b.eventValidate:this.eventValidate,b)))}},eventValidate:function(a){new RegExp("key").test(a.type)&&!this._ui.validationInformationVisible&&this.getValue().length<=this.options.validationThreshold||(this._ui.validatedOnce=!0,this.validate())},manageFailingFieldTrigger:function(b){return b._ui.failedOnce=!0,b.options.multiple&&a("["+b.options.namespace+'multiple="'+b.options.multiple+'"]').each(function(){return new RegExp("change","i").test(a(this).parsley().options.trigger||"")?void 0:a(this).on("change.ParsleyFailedOnce",!1,a.proxy(b.validate,b))}),b.$element.is("select")&&!new RegExp("change","i").test(b.options.trigger||"")?b.$element.on("change.ParsleyFailedOnce",!1,a.proxy(b.validate,b)):new RegExp("keyup","i").test(b.options.trigger||"")?void 0:b.$element.on("keyup.ParsleyFailedOnce",!1,a.proxy(b.validate,b))},reset:function(b){"undefined"!=typeof b._ui&&(b.$element.off(".Parsley"),b.$element.off(".ParsleyFailedOnce"),"ParsleyForm"!==b.__class__&&(b._ui.$errorsWrapper.children().each(function(){a(this).remove()}),this._resetClass(b),b._ui.validatedOnce=!1,b._ui.lastValidationResult=[],b._ui.validationInformationVisible=!1))},destroy:function(a){"undefined"!=typeof a._ui&&(this.reset(a),"ParsleyForm"!==a.__class__&&(a._ui.$errorsWrapper.remove(),delete a._ui))},_successClass:function(a){a._ui.validationInformationVisible=!0,a._ui.$errorClassHandler.removeClass(a.options.errorClass).addClass(a.options.successClass)},_errorClass:function(a){a._ui.validationInformationVisible=!0,a._ui.$errorClassHandler.removeClass(a.options.successClass).addClass(a.options.errorClass)},_resetClass:function(a){a._ui.$errorClassHandler.removeClass(a.options.successClass).removeClass(a.options.errorClass)}};var g=function(c,d,e,f){this.__class__="OptionsFactory",this.__id__=b.hash(4),this.formOptions=null,this.fieldOptions=null,this.staticOptions=a.extend(!0,{},c,d,e,{namespace:f})};g.prototype={get:function(a){if("undefined"==typeof a.__class__)throw new Error("Parsley Instance expected");switch(a.__class__){case"Parsley":return this.staticOptions;case"ParsleyForm":return this.getFormOptions(a);case"ParsleyField":case"ParsleyFieldMultiple":return this.getFieldOptions(a);default:throw new Error("Instance "+a.__class__+" is not supported")}},getFormOptions:function(c){return this.formOptions=b.attr(c.$element,this.staticOptions.namespace),a.extend({},this.staticOptions,this.formOptions)},getFieldOptions:function(c){return this.fieldOptions=b.attr(c.$element,this.staticOptions.namespace),null===this.formOptions&&"ParsleyForm"===c.parsleyInstance.__proxy__&&(this.formOptions=getFormOptions(c.parsleyInstance)),a.extend({},this.staticOptions,this.formOptions,this.fieldOptions)}};var h=function(c,d){if(this.__class__="ParsleyForm",this.__id__=b.hash(4),"Parsley"!==b.get(d,"__class__"))throw new Error("You must give a Parsley instance");this.parsleyInstance=d,this.$element=a(c)};h.prototype={init:function(){return this.validationResult=null,this.options=this.parsleyInstance.OptionsFactory.get(this),this._bindFields(),this},onSubmitValidate:function(b){return this.validate(void 0,void 0,b),!1===this.validationResult&&b instanceof a.Event&&b.preventDefault(),this},validate:function(b,c,d){this.submitEvent=d,this.validationResult=!0;var e=[];this._refreshFields(),a.emit("parsley:form:validate",this);for(var f=0;f<this.fields.length;f++)b&&b!==this.fields[f].options.group||(e=this.fields[f].validate(c),!0!==e&&e.length>0&&this.validationResult&&(this.validationResult=!1));return a.emit("parsley:form:validated",this),this.validationResult},isValid:function(a,b){this._refreshFields();for(var c=0;c<this.fields.length;c++)if((!a||a===this.fields[c].options.group)&&!1===this.fields[c].isValid(b))return!1;return!0},_refreshFields:function(){return this.actualizeOptions()._bindFields()},_bindFields:function(){var a=this;return this.fields=[],this.fieldsMappedById={},this.$element.find(this.options.inputs).each(function(){var b=new window.Parsley(this,{},a.parsleyInstance);"ParsleyField"!==b.__class__&&"ParsleyFieldMultiple"!==b.__class__||b.$element.is(b.options.excluded)||"undefined"==typeof a.fieldsMappedById[b.__class__+"-"+b.__id__]&&(a.fieldsMappedById[b.__class__+"-"+b.__id__]=b,a.fields.push(b))}),this}};var i=function(c,d,e,f,g){if(!new RegExp("ParsleyField").test(b.get(c,"__class__")))throw new Error("ParsleyField or ParsleyFieldMultiple instance expected");if("function"!=typeof window.ParsleyValidator.validators[d]&&"Assert"!==window.ParsleyValidator.validators[d](e).__parentClass__)throw new Error("Valid validator expected");var h=function(a,c){return"undefined"!=typeof a.options[c+"Priority"]?a.options[c+"Priority"]:b.get(window.ParsleyValidator.validators[c](e),"priority")||2};return f=f||h(c,d),"function"==typeof window.ParsleyValidator.validators[d](e).requirementsTransformer&&(e=window.ParsleyValidator.validators[d](e).requirementsTransformer()),a.extend(window.ParsleyValidator.validators[d](e),{name:d,requirements:e,priority:f,groups:[f],isDomConstraint:g||b.attr(c.$element,c.options.namespace,d)})},j=function(c,d){if(this.__class__="ParsleyField",this.__id__=b.hash(4),"Parsley"!==b.get(d,"__class__"))throw new Error("You must give a Parsley instance");this.parsleyInstance=d,this.$element=a(c),this.options=this.parsleyInstance.OptionsFactory.get(this)
+};j.prototype={init:function(){return this.constraints=[],this.validationResult=[],this.bindConstraints(),this},validate:function(b){return this.value=this.getValue(),a.emit("parsley:field:validate",this),a.emit("parsley:field:"+(this.isValid(b,this.value)?"success":"error"),this),a.emit("parsley:field:validated",this),this.validationResult},getConstraintsSortedPriorities:function(){for(var a=[],b=0;b<this.constraints.length;b++)-1===a.indexOf(this.constraints[b].priority)&&a.push(this.constraints[b].priority);return a.sort(function(a,b){return b-a}),a},isValid:function(a,b){this.refreshConstraints();var c=this.getConstraintsSortedPriorities();if(b=b||this.getValue(),0===b.length&&!this.isRequired()&&"undefined"==typeof this.options.validateIfEmpty&&"undefined"==typeof a)return this.validationResult=[];if(!1===this.options.priorityEnabled)return!0===(this.validationResult=this.validateThroughValidator(b,this.constraints,"Any"));for(var d=0;d<c.length;d++)if(!0!==(this.validationResult=this.validateThroughValidator(b,this.constraints,c[d])))return!1;return!0},isRequired:function(){var a=this._constraintIndex("required");return!(-1===a||-1!==a&&!1===this.constraints[a].requirements)},getValue:function(){var a;return a="undefined"!=typeof this.options.value?this.options.value:this.$element.val(),!0===this.options.trimValue?a.replace(/^\s+|\s+$/g,""):a},refreshConstraints:function(){return this.actualizeOptions().bindConstraints(),this},bindConstraints:function(){for(var a=[],b=0;b<this.constraints.length;b++)!1===this.constraints[b].isDomConstraint&&a.push(this.constraints[b]);this.constraints=a;for(var c in this.options)this.addConstraint(c,this.options[c]);return this.bindHtml5Constraints()},bindHtml5Constraints:function(){(this.$element.hasClass("required")||this.$element.attr("required"))&&this.addConstraint("required",!0,void 0,!0),"string"==typeof this.$element.attr("pattern")&&this.addConstraint("pattern",this.$element.attr("pattern"),void 0,!0),"undefined"!=typeof this.$element.attr("min")&&"undefined"!=typeof this.$element.attr("max")?this.addConstraint("range",[this.$element.attr("min"),this.$element.attr("max")],void 0,!0):"undefined"!=typeof this.$element.attr("min")?this.addConstraint("min",this.$element.attr("min"),void 0,!0):"undefined"!=typeof this.$element.attr("max")&&this.addConstraint("max",this.$element.attr("max"),void 0,!0);var a=this.$element.attr("type");return"undefined"==typeof a?this:"number"===a?this.addConstraint("type","integer",void 0,!0):new RegExp(a,"i").test("email url range")?this.addConstraint("type",a,void 0,!0):void 0},addConstraint:function(a,b,c,d){if(a=a.toLowerCase(),"function"==typeof window.ParsleyValidator.validators[a]){var e=new i(this,a,b,c,d);-1!==this._constraintIndex(e.name)&&this.removeConstraint(e.name),this.constraints.push(e)}return this},removeConstraint:function(a){for(var b=0;b<this.constraints.length;b++)if(a===this.constraints[b].name){this.constraints.splice(b,1);break}return this},updateConstraint:function(a,b,c){return this.removeConstraint(a).addConstraint(a,b,c)},_constraintIndex:function(a){for(var b=0;b<this.constraints.length;b++)if(a===this.constraints[b].name)return b;return-1}};var k=function(){this.__class__="ParsleyFieldMultiple"};k.prototype={init:function(a){return this.$elements=[this.$element],this.options.multiple=a,this},addElement:function(a){return this.$elements.push(a),this},refreshConstraints:function(){if(this.constraints=[],this.$element.is("select"))return this.actualizeOptions().bindConstraints(),this;for(var a=0;a<this.$elements.length;a++)this.constraints=this.constraints.concat(this.$elements[a].data("ParsleyFieldMultiple").refreshConstraints().constraints);return this},getValue:function(){if("undefined"!=typeof this.options.value)return this.options.value;if(this.$element.is("input[type=radio]"))return a("["+this.options.namespace+'multiple="'+this.options.multiple+'"]:checked').val()||"";if(this.$element.is("input[type=checkbox]")){var b=[];return a("["+this.options.namespace+'multiple="'+this.options.multiple+'"]:checked').each(function(){b.push(a(this).val())}),b.length?b:[]}return this.$element.is("select")?null===this.$element.val()?[]:this.$element.val():void 0}};var l=a({}),m={};a.listen=function(a){if("undefined"==typeof m[a]&&(m[a]=[]),"function"==typeof arguments[1])return m[a].push({fn:arguments[1]});if("object"==typeof arguments[1]&&"function"==typeof arguments[2])return m[a].push({fn:arguments[2],ctxt:arguments[1]});throw new Error("Wrong parameters")},a.listenTo=function(a,b,c){if("undefined"==typeof m[b]&&(m[b]=[]),!(a instanceof j||a instanceof h))throw new Error("Must give Parsley instance");if("string"!=typeof b||"function"!=typeof c)throw new Error("Wrong parameters");m[b].push({instance:a,fn:c})},a.unsubscribe=function(a,b){if("undefined"!=typeof m[a]){if("string"!=typeof a||"function"!=typeof b)throw new Error("Wrong arguments");for(var c=0;c<m[a].length;c++)if(m[a][c].fn===b)return m[a].splice(c,1)}},a.unsubscribeTo=function(a,b){if("undefined"!=typeof m[b]){if(!(a instanceof j||a instanceof h))throw new Error("Must give Parsley instance");for(var c=0;c<m[b].length;c++)if("undefined"!=typeof m[b][c].instance&&m[b][c].instance.__id__===a.__id__)return m[b].splice(c,1)}},a.unsubscribeAll=function(a){"undefined"!=typeof m[a]&&delete m[a]},a.emit=function(a,b){if("undefined"!=typeof m[a])for(var c=0;c<m[a].length;c++)if("undefined"!=typeof m[a][c].instance){if(b instanceof j||b instanceof h)if(m[a][c].instance.__id__!==b.__id__){if(m[a][c].instance instanceof h&&b instanceof j)for(var d=0;d<m[a][c].instance.fields.length;d++)if(m[a][c].instance.fields[d].__id__===b.__id__){m[a][c].fn.apply(l,Array.prototype.slice.call(arguments,1));continue}}else m[a][c].fn.apply(l,Array.prototype.slice.call(arguments,1))}else m[a][c].fn.apply("undefined"!=typeof m[a][c].ctxt?m[a][c].ctxt:l,Array.prototype.slice.call(arguments,1))},a.subscribed=function(){return m},window.ParsleyConfig=window.ParsleyConfig||{},window.ParsleyConfig.i18n=window.ParsleyConfig.i18n||{},window.ParsleyConfig.i18n.en=a.extend(window.ParsleyConfig.i18n.en||{},{defaultMessage:"This value seems to be invalid.",type:{email:"This value should be a valid email.",url:"This value should be a valid url.",number:"This value should be a valid number.",integer:"This value should be a valid integer.",digits:"This value should be digits.",alphanum:"This value should be alphanumeric."},notblank:"This value should not be blank.",required:"This value is required.",pattern:"This value seems to be invalid.",min:"This value should be greater than or equal to %s.",max:"This value should be lower than or equal to %s.",range:"This value should be between %s and %s.",minlength:"This value is too short. It should have %s characters or more.",maxlength:"This value is too long. It should have %s characters or less.",length:"This value length is invalid. It should be between %s and %s characters long.",mincheck:"You must select at least %s choices.",maxcheck:"You must select %s choices or less.",check:"You must select between %s and %s choices.",equalto:"This value should be the same."}),"undefined"!=typeof window.ParsleyValidator&&window.ParsleyValidator.addCatalog("en",window.ParsleyConfig.i18n.en,!0);var n=function(c,d,e){if(this.__class__="Parsley",this.__version__="2.0.0-rc4",this.__id__=b.hash(4),"undefined"==typeof c)throw new Error("You must give an element");return this.init(a(c),d,e)};n.prototype={init:function(a,d,e){if(!a.length)throw new Error("You must bind Parsley on an existing element.");if(this.$element=a,this.$element.data("Parsley")){var f=this.$element.data("Parsley");return"undefined"!=typeof e&&"ParsleyField"===f.parsleyInstance.__proxy__&&(f.parsleyInstance=e),f}return this.OptionsFactory=new g(c,b.get(window,"ParsleyConfig")||{},d,this.getNamespace(d)),this.options=this.OptionsFactory.get(this),this.$element.is("form")||b.attr(this.$element,this.options.namespace,"validate")&&!this.$element.is(this.options.inputs)?this.bind("parsleyForm",e):this.$element.is(this.options.inputs)&&!this.$element.is(this.options.excluded)?this.isMultiple()?this.handleMultiple(e):this.bind("parsleyField",e):this},isMultiple:function(){return this.$element.is("input[type=radio], input[type=checkbox]")&&"undefined"==typeof this.options.multiple||this.$element.is("select")&&"undefined"!=typeof this.$element.attr("multiple")},handleMultiple:function(c){var d,e,f,g=this;if(this.options=a.extend(this.options,b.attr(this.$element,this.options.namespace)),this.options.multiple?e=this.options.multiple:"undefined"!=typeof this.$element.attr("name")&&this.$element.attr("name").length?e=d=this.$element.attr("name"):"undefined"!=typeof this.$element.attr("id")&&this.$element.attr("id").length&&(e=this.$element.attr("id")),this.$element.is("select")&&"undefined"!=typeof this.$element.attr("multiple"))return this.bind("parsleyFieldMultiple",c,e||this.__id__);if("undefined"==typeof e)return window.console&&window.console.warn&&window.console.warn("To be binded by Parsley, a radio, a checkbox and a multiple select input must have either a name or a multiple option.",this.$element),this;if(e=e.replace(/(:|\.|\[|\]|\$)/g,""),"undefined"!=typeof d&&a('input[name="'+d+'"]').each(function(){a(this).attr(g.options.namespace+"multiple",e)}),a("["+this.options.namespace+"multiple="+e+"]").length)for(var h=0;h<a("["+this.options.namespace+"multiple="+e+"]").length;h++)if("undefined"!=typeof a(a("["+this.options.namespace+"multiple="+e+"]").get(h)).data("Parsley")){f=a(a("["+this.options.namespace+"multiple="+e+"]").get(h)).data("Parsley"),this.$element.data("ParsleyFieldMultiple")||(f.addElement(this.$element),this.$element.attr(this.options.namespace+"id",f.__id__));break}return this.bind("parsleyField",c,e,!0),f||this.bind("parsleyFieldMultiple",c,e)},getNamespace:function(a){return"undefined"!=typeof this.$element.data("parsleyNamespace")?this.$element.data("parsleyNamespace"):"undefined"!=typeof b.get(a,"namespace")?a.namespace:"undefined"!=typeof b.get(window,"ParsleyConfig.namespace")?window.ParsleyConfig.namespace:c.namespace},bind:function(c,e,f,g){var i;switch(c){case"parsleyForm":i=a.extend(new h(this.$element,e||this),new d,window.ParsleyExtend).init();break;case"parsleyField":i=a.extend(new j(this.$element,e||this),new d,window.ParsleyExtend).init();break;case"parsleyFieldMultiple":i=a.extend(new j(this.$element,e||this).init(),new d,new k,window.ParsleyExtend).init(f);break;default:throw new Error(c+"is not a supported Parsley type")}return"undefined"!=typeof f&&b.setAttr(this.$element,this.options.namespace,"multiple",f),"undefined"!=typeof g?(this.$element.data("ParsleyFieldMultiple",i),i):(new RegExp("ParsleyF","i").test(i.__class__)&&(this.$element.data("Parsley",i),this.__proxy__=i.__class__,a.emit("parsley:"+("parsleyForm"===c?"form":"field")+":init",i)),i)}},a.fn.parsley=a.fn.psly=function(b){if(this.length>1){var c=[];return this.each(function(){c.push(a(this).parsley(b))}),c}return a(this).length?new n(this,b):void(window.console&&window.console.warn&&window.console.warn("You must bind Parsley on an existing element."))},window.ParsleyUI="function"==typeof b.get(window,"ParsleyConfig.ParsleyUI")?(new window.ParsleyConfig.ParsleyUI).listen():(new f).listen(),"undefined"==typeof window.ParsleyExtend&&(window.ParsleyExtend={}),"undefined"==typeof window.ParsleyConfig&&(window.ParsleyConfig={}),window.Parsley=window.psly=n,window.ParsleyUtils=b,window.ParsleyValidator=new e(window.ParsleyConfig.validators,window.ParsleyConfig.i18n),!1!==b.get(window,"ParsleyConfig.autoBind")&&a(document).ready(function(){a("[data-parsley-validate]").length&&a("[data-parsley-validate]").parsley()})}(window.jQuery);
\ No newline at end of file
Index: js/project-savepost.js
===================================================================
--- js/project-savepost.js (revision 141566)
+++ js/project-savepost.js (working copy)
@@ -56,7 +56,7 @@
setInterval( function() {
if ( idle_state === false ) {
var post = $( '#post ').serialize();
-
+
$.ajax({
type: 'POST',
dataType: 'json',
Index: js/projects.js
===================================================================
--- js/projects.js (revision 141566)
+++ js/projects.js (working copy)
@@ -1,56 +1,54 @@
-jQuery(document).ready(function(){
- jQuery('#tabs li.steps').click(function() {
- jQuery(this).addClass('current');
+jQuery( document ).ready( function( $ ) {
+
+ // Handle the click actions on the list items in the steps box
+ $( 'body' ).on( 'click', '#tabs li.steps', function() {
var id = jQuery(this).attr('id');
- jQuery('#steppers div#js-' + id).slideDown().removeClass('hide').addClass('active');
- jQuery('#steppers div:not(#js-' + id + ')').slideUp();
- jQuery('#tabs li:not(#' + id + ')').removeClass('current');
+
+ // Progress the slider
+ $( '#steppers' ).find( '.jstep#js-' + id ).slideDown().removeClass( 'hide' );
+ $( '#steppers' ).find( '.jstep:not( #js-' + id + ')' ).slideUp();
+
+ // Update the side navigation list
+ jQuery( this ).addClass( 'current' );
+ jQuery( '#tabs li:not(#' + id + ')' ).removeClass( 'current' );
+
+ // Run our trackers
googletag.pubads().refresh();
_gaq.push(['_trackPageview']);
- console.log('Pushed a pageview, like a boss.');
- var urlref = location.href;
- PARSELY.beacon.trackPageView({
- url: urlref,
- urlref: urlref,
- js: 1,
- action_name: "Step Clicked"
- });
- return true;
});
- jQuery('.nexter').click(function() {
- var id = jQuery(this).attr('id');
- jQuery('#steppers div#js-' + id).slideDown().removeClass('hide');
- jQuery('#steppers div:not(#js-' + id + ')').slideUp();
- jQuery(this).addClass('current');
- jQuery('#tabs li#' + id).addClass('current');
- jQuery('#tabs li:not(#' + id + ')').removeClass('current');
+
+ // Allows us to advance in the slider
+ $( 'body' ).on( 'click', '.nexter', function() {
+ var id = $(this).attr('id');
+
+ // Progress the slider
+ $( '#steppers' ).find( '.jstep#js-' + id ).slideDown().removeClass( 'hide' );
+ $( '#steppers' ).find( '.jstep:not( #js-' + id + ')' ).slideUp();
+
+ // Update side navigation list
+ $( '#tabs').find( ' li#' + id ).addClass( 'current' );
+ $( '#tabs' ).find( 'li:not( #' + id + ')' ).removeClass( 'current' );
+
+ // Run our trackers
googletag.pubads().refresh();
_gaq.push(['_trackPageview']);
- console.log('Pushed a pageview, like a boss.');
- var urlref = location.href;
- PARSELY.beacon.trackPageView({
- url: urlref,
- urlref: urlref,
- js: 1,
- action_name: "Next Project Step"
+ });
+
+ // Display all projects when we click "View All"
+ jQuery( 'body' ).on( 'click', '.aller', function() {
+ // Display all the slides
+ jQuery( '#steppers' ).find( '.js-step' ).each( function() {
+ jQuery( this ).slideDown();
});
- return true;
- });
- jQuery('.aller').click(function() {
- jQuery('#steppers').children().slideDown();
- jQuery('#steppers .nexter, #steppers .disabled').hide();
+
+ // Hide the next/previous buttons
+ jQuery( '#steppers .nexter, #steppers .disabled' ).hide();
+
+ // Run our trackers
googletag.pubads().refresh();
_gaq.push(['_trackPageview']);
- console.log('Pushed a pageview, like a boss.');
- var urlref = location.href;
- PARSELY.beacon.trackPageView({
- url: urlref,
- urlref: urlref,
- js: 1,
- action_name: "View all on the projects"
- });
- return true;
});
+
jQuery('.carousel').on('slid', function () {
jQuery('.slide').find('iframe').each( function(){
jQuery(this).attr('src', '');
@@ -176,7 +174,7 @@
_gaq.push(['_trackPageview']);
} );
} )( jQuery );
-
+
jQuery('.print-page').on('click', function() {
window.print();
});
Index: less/bootstrap/bootstrap.less
===================================================================
--- less/bootstrap/bootstrap.less (revision 141566)
+++ less/bootstrap/bootstrap.less (working copy)
@@ -41,6 +41,9 @@
@import "tooltip.less";
@import "popovers.less";
+// Components: Panels
+@import "panels.less";
+
// Components: Misc
@import "thumbnails.less";
@import "media.less";
Index: less/bootstrap/mixins.less
===================================================================
--- less/bootstrap/mixins.less (revision 141566)
+++ less/bootstrap/mixins.less (working copy)
@@ -196,8 +196,27 @@
}
}
+// Panels
+// -------------------------
+.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {
+ border-color: @border;
+ & > .panel-heading {
+ color: @heading-text-color;
+ background-color: @heading-bg-color;
+ border-color: @heading-border;
+ + .panel-collapse .panel-body {
+ border-top-color: @border;
+ }
+ }
+ & > .panel-footer {
+ + .panel-collapse .panel-body {
+ border-bottom-color: @border;
+ }
+ }
+}
+
// CSS3 PROPERTIES
// --------------------------------------------------
Index: less/bootstrap/panels.less
===================================================================
--- less/bootstrap/panels.less (revision 0)
+++ less/bootstrap/panels.less (working copy)
@@ -0,0 +1,182 @@
+//
+// Panels
+// --------------------------------------------------
+
+
+// Base class
+.panel {
+ margin-bottom: @line-height-computed;
+ background-color: @panel-bg;
+ border: 1px solid transparent;
+ border-radius: @panel-border-radius;
+ .box-shadow(0 1px 1px rgba(0,0,0,.05));
+}
+
+// Panel contents
+.panel-body {
+ padding: 15px;
+ .clearfix();
+}
+
+
+// List groups in panels
+//
+// By default, space out list group content from panel headings to account for
+// any kind of custom content between the two.
+
+.panel {
+ > .list-group {
+ margin-bottom: 0;
+
+ .list-group-item {
+ border-width: 1px 0;
+
+ // Remove border radius for top one
+ &:first-child {
+ .border-top-radius(0);
+ }
+ // But keep it for the last one
+ &:last-child {
+ border-bottom: 0;
+ }
+ }
+ }
+}
+// Collapse space between when there's no additional content.
+.panel-heading + .list-group {
+ .list-group-item:first-child {
+ border-top-width: 0;
+ }
+}
+
+
+// Tables in panels
+//
+// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and
+// watch it go full width.
+
+.panel {
+ > .table,
+ > .table-responsive > .table {
+ margin-bottom: 0;
+ }
+ > .panel-body + .table,
+ > .panel-body + .table-responsive {
+ border-top: 1px solid @tableBorder;
+ }
+ > .table > tbody:first-child th,
+ > .table > tbody:first-child td {
+ border-top: 0;
+ }
+ > .table-bordered,
+ > .table-responsive > .table-bordered {
+ border: 0;
+ > thead,
+ > tbody,
+ > tfoot {
+ > tr {
+ > th:first-child,
+ > td:first-child {
+ border-left: 0;
+ }
+ > th:last-child,
+ > td:last-child {
+ border-right: 0;
+ }
+
+ &:last-child > th,
+ &:last-child > td {
+ border-bottom: 0;
+ }
+ }
+ }
+ }
+ > .table-responsive {
+ border: 0;
+ margin-bottom: 0;
+ }
+}
+
+
+// Optional heading
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ .border-top-radius(@panel-border-radius - 1);
+
+ > .dropdown .dropdown-toggle {
+ color: inherit;
+ }
+}
+
+// Within heading, strip any `h*` tag of it's default margins for spacing.
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: ceil((@baseFontSize * 1.125));
+ color: inherit;
+
+ > a {
+ color: inherit;
+ }
+}
+
+// Optional footer (stays gray in every modifier class)
+.panel-footer {
+ padding: 10px 15px;
+ background-color: @panel-footer-bg;
+ border-top: 1px solid @panel-inner-border;
+ .border-bottom-radius(@panel-border-radius - 1);
+}
+
+
+// Collapsable panels (aka, accordion)
+//
+// Wrap a series of panels in `.panel-group` to turn them into an accordion with
+// the help of our collapse JavaScript plugin.
+
+.panel-group {
+ // Tighten up margin so it's only between panels
+ .panel {
+ margin-bottom: 0;
+ border-radius: @panel-border-radius;
+ overflow: hidden; // crop contents when collapsed
+ + .panel {
+ margin-top: 5px;
+ }
+ }
+
+ .panel-heading {
+ border-bottom: 0;
+ + .panel-collapse .panel-body {
+ border-top: 1px solid @panel-inner-border;
+ }
+ }
+ .panel-footer {
+ border-top: 0;
+ + .panel-collapse .panel-body {
+ border-bottom: 1px solid @panel-inner-border;
+ }
+ }
+}
+
+
+// Contextual variations
+.panel-default {
+ .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);
+}
+.panel-primary {
+ .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);
+}
+.panel-success {
+ .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);
+}
+.panel-warning {
+ .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);
+}
+.panel-danger {
+ .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);
+}
+.panel-info {
+ .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);
+}
Index: less/bootstrap/variables.less
===================================================================
--- less/bootstrap/variables.less (revision 141566)
+++ less/bootstrap/variables.less (working copy)
@@ -74,6 +74,8 @@
@borderRadiusLarge: 6px;
@borderRadiusSmall: 3px;
+@line-height-base: 1.428571429; // 20/14
+@line-height-computed: floor(@baseFontSize * @line-height-base); // ~20px
// Tables
// -------------------------
@@ -233,7 +235,38 @@
@navbarBlueBrandColor: #ED1C24;
+// Panels
+// -------------------------
+@panel-bg: #fff;
+@panel-inner-border: #ddd;
+@panel-border-radius: @baseBorderRadius;
+@panel-footer-bg: #f5f5f5;
+@panel-default-text: @grayDark;
+@panel-default-border: #ddd;
+@panel-default-heading-bg: #f5f5f5;
+
+@panel-primary-text: #fff;
+@panel-primary-border: #000;
+@panel-primary-heading-bg: #ccc;
+
+@panel-success-text: @successText;
+@panel-success-border: @successBorder;
+@panel-success-heading-bg: @successBackground;
+
+@panel-warning-text: @warningText;
+@panel-warning-border: @warningBorder;
+@panel-warning-heading-bg: @warningBackground;
+
+@panel-danger-text: @errorText;
+@panel-danger-border: @errorBorder;
+@panel-danger-heading-bg: @errorBackground;
+
+@panel-info-text: @infoText;
+@panel-info-border: @infoBorder;
+@panel-info-heading-bg: @infoBackground;
+
+
// Pagination
// -------------------------
@paginationBackground: #fff;
Index: less/make/contribute.less
===================================================================
--- less/make/contribute.less (revision 0)
+++ less/make/contribute.less (working copy)
@@ -0,0 +1,35 @@
+.page-template-page-contribute-project-php {
+ .title {
+ width:98%;
+ margin-bottom:10px;
+ }
+ .thumbnail {
+ cursor:pointer;
+ &.add {
+ width:80px;
+ height:38px;
+ text-align:center;
+ padding:25px 5px;
+ opacity:.5;
+ border-style:dashed;
+ font-size:14px;
+ }
+ }
+ #step_content {
+ width:98%;
+ min-height:100px;
+ }
+ .wp-core-ui .button {
+ color: #555 !important;
+ &:hover {
+ background-color:#fafafa !important;
+ color:#222 !important;
+ }
+ }
+ .wp-editor-container {
+ border:1px solid #dedede;
+ }
+ .projects-masthead {
+ padding-top:0;
+ }
+}
\ No newline at end of file
Index: less/make/fileinput.less
===================================================================
--- less/make/fileinput.less (revision 0)
+++ less/make/fileinput.less (working copy)
@@ -0,0 +1,108 @@
+// Fileinput.less
+// CSS for file upload button and fileinput widget
+// ------------------------------------------------
+
+.btn-file {
+ overflow: hidden;
+ position: relative;
+ vertical-align: middle;
+ > input {
+ position: absolute;
+ top: 0;
+ right: 0;
+ margin: 0;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ transform: translate(-300px, 0) scale(4);
+ font-size: 23px;
+ height: 100%;
+ direction: ltr;
+ cursor: pointer;
+ }
+}
+
+.fileinput {
+ margin-bottom: 9px;
+ display: inline-block;
+ .form-control {
+ padding-top: 7px;
+ padding-bottom: 5px;
+ display: inline-block;
+ margin-bottom: 0px;
+ vertical-align: middle;
+ cursor: text;
+ }
+ .thumbnail {
+ overflow: hidden;
+ display: inline-block;
+ margin-bottom: 5px;
+ vertical-align: middle;
+ text-align: center;
+ > img {
+ max-height: 100%;
+ }
+ }
+ .btn {
+ vertical-align: middle;
+ }
+}
+.fileinput-exists .fileinput-new,
+.fileinput-new .fileinput-exists {
+ display: none;
+}
+.fileinput-inline .fileinput-controls {
+ display: inline;
+}
+
+.fileinput-filename {
+ vertical-align: middle;
+ display: inline-block;
+ overflow: hidden;
+}
+.form-control .fileinput-filename {
+ vertical-align: bottom;
+}
+
+// Not 100% correct, but helps in typical use case
+.fileinput-new .input-group .btn-file {
+ border-radius: 0 @border-radius-base @border-radius-base 0;
+}
+.fileinput-new .input-group .btn-file.btn-xs,
+.fileinput-new .input-group .btn-file.btn-sm {
+ border-radius: 0 @border-radius-small @border-radius-small 0;
+}
+.fileinput-new .input-group .btn-file.btn-lg {
+ border-radius: 0 @border-radius-large @border-radius-large 0;
+}
+
+.form-group.has-warning .fileinput {
+ .fileinput-preview {
+ color: @state-warning-text;
+ }
+ .thumbnail {
+ border-color: @state-warning-border;
+ }
+}
+.form-group.has-error .fileinput {
+ .fileinput-preview {
+ color: @state-danger-text;
+ }
+ .thumbnail {
+ border-color: @state-danger-border;
+ }
+}
+.form-group.has-success .fileinput {
+ .fileinput-preview {
+ color: @state-success-text;
+ }
+ .thumbnail {
+ border-color: @state-success-border;
+ }
+}
+
+
+// Input group fixes
+
+.input-group-addon:not(:first-child) {
+ border-left: 0;
+}
\ No newline at end of file
Index: less/make/header.less
===================================================================
--- less/make/header.less (revision 141566)
+++ less/make/header.less (working copy)
@@ -10,6 +10,22 @@
.nav { margin-bottom:0; }
}
+// User Login Wrapper
+.login-wrapper {
+ position:absolute;
+ left:245px;
+ top:-8px;
+ color:#005d8f;
+ font-size:18px;
+ font-weight:bold;
+ a {
+ display:inline-block;
+ &:hover {
+ text-decoration:underline !important;
+ }
+ }
+}
+
// Wrapper containing the main navigaiton
.main-header {
padding:15px 0 10px;
@@ -25,7 +41,9 @@
position:absolute;
top:-15px;
right:0;
- .subscribe, .search-make, { .pull-left; }
+ .subscribe, .search-make, {
+ .pull-left;
+ }
.subscribe {
> a {
margin-right:6px;
@@ -58,7 +76,8 @@
}
}
.search-make {
- float:right;
+ position:absolute;
+ left:-695px;
margin:0;
input[type="text"] {
width:165px;
@@ -133,7 +152,7 @@
font-size:20px !important;
font-weight:bold;
position:relative;
- top:92px;
+ top:65px;
ul {
> li {
margin-right: 18px;
Index: less/make/style.less
===================================================================
--- less/make/style.less (revision 141566)
+++ less/make/style.less (working copy)
@@ -1,6 +1,6 @@
// Google Custom Search Styles
-.gsc-cursor .gsc-cursor-current-page {
- background-color:#005d8f;
+.gsc-cursor .gsc-cursor-current-page {
+ background-color:#005d8f;
}
// Generic Page Styles
@@ -22,7 +22,7 @@
display: none;
}
-body.search .well {
+body.search .well {
margin-bottom:0 !important;
}
@@ -55,16 +55,16 @@
.blog {
.projects-masthead {
- h2 {
- margin-bottom: -10px;
+ h2 {
+ margin-bottom: -10px;
}
}
.projects-meta {
color:#333;
font-weight:normal;
}
- article {
- margin:20px 0 45px;
+ article {
+ margin:20px 0 45px;
}
}
// h1 masthead styles
@@ -93,6 +93,44 @@
}
}
}
+
+
+// Parseley Form Validation Styles
+input.parsley-error, select.parsley-error, textarea.parsley-error, div.parsley-error.mce-panel {
+ color: #B94A48;
+ background: #F2DEDE;
+ border: 1px solid #EED3D7 !important;
+}
+
+.parsley-errors-list {
+ margin: 2px 0 3px 0;
+ padding: 0;
+ list-style-type: none;
+ font-size: 0.9em;
+ line-height: 0.9em;
+ opacity: 0;
+ -moz-opacity: 0;
+ -webkit-opacity: 0;
+ color:#B94A48;
+
+ transition: all .3s ease-in;
+ -o-transition: all .3s ease-in;
+ -ms-transition: all .3s ease-in-;
+ -moz-transition: all .3s ease-in;
+ -webkit-transition: all .3s ease-in;
+ &.post_content_errors {
+ position:absolute;
+ bottom:26px;
+ left:10px;
+ }
+}
+
+.parsley-errors-list.filled {
+ opacity: 1;
+}
+
+
+
.waist {
background-image: url('../img/blue_bg1.jpg');
border-bottom:2px solid #DBDBDB;
@@ -132,8 +170,8 @@
line-height: 18px;
}
-.look_like_h4 {
- font-size: 14px !important;
+.look_like_h4 {
+ font-size: 14px !important;
font-weight: bold !important;
line-height: 18px !important;
height: 120px !important;
@@ -570,7 +608,7 @@
display: inline-block;
padding-bottom: 8px;
}
-
+
footer {
padding-top:40px;
padding-bottom:20px;
@@ -593,7 +631,7 @@
margin-right: 5px;
margin-top: 8px;
max-width: 140px!important;
-}
+}
//This class is to keep some pages from inheriting the wrong giant font in the footer.
.p_footer {
@@ -605,14 +643,14 @@
.twtr-widget {
border:1px solid #D0D0CE !important;
}
-
+
.more_faires {
background-color:#E8F1F4;
border:1px solid #D0D0CE;
padding:10px;
max-height: 373px;
}
-
+
.more_faires h3 {
font-family:"proxima-nova", sans-serif;
font-weight:300;
@@ -620,7 +658,7 @@
color:#67abc6;
margin-bottom:10px;
}
-
+
.more_faires h4 {
font-family:"proxima-nova", sans-serif;
font-weight:300;
@@ -633,19 +671,19 @@
font-size:16px;
list-style-image:url('../img/arrow.gif');
}
-
+
.more_faires ul li a {
font-weight:bold;
color:#065270;
}
-
-
-
-footer .pills {
+
+
+
+footer .pills {
margin-left:100px;
margin-top:20px;
}
-
+
footer .pills a {
color:#5a6479;
}
@@ -1019,14 +1057,14 @@
.drop-down {
margin-left:0px;
- columns: auto 3;
+ columns: auto 3;
-webkit-columns: auto 3;
-moz-columns: auto 3;
- margin-top: 23px;
+ margin-top: 23px;
}
.drop-down.fix {
- columns: auto auto;
+ columns: auto auto;
-webkit-columns: auto auto;
-moz-columns: auto auto;
}
@@ -1251,7 +1289,7 @@
.third:first-child {
margin-left:20px;
-}
+}
.third h4 {
color:#045876;
@@ -1401,10 +1439,10 @@
-moz-column-rule-style: solid;
-webkit-column-rule-width: 1px;
-webkit-column-rule-color: #ccc;
- -webkit-column-rule-style: solid;
+ -webkit-column-rule-style: solid;
column-rule-width: 1px;
column-rule-color: #ccc;
- column-rule-style: solid;
+ column-rule-style: solid;
}
.two-column h5 {
@@ -1623,7 +1661,7 @@
width: 8em;
text-align: right;
}
-.pop-posts a {
+.pop-posts a {
display: block;
margin-bottom:15px;
}
@@ -1676,6 +1714,11 @@
float:left;
}
+div.parts.row, div.tools.row {
+ width:100%;
+ float:none;
+}
+
.description {
text-align: right;
}
@@ -1717,14 +1760,14 @@
text-transform: uppercase;
border-bottom:1px solid #b0bac1;
}
-
+
#review_box h2 {
line-height:22px;
margin-bottom:2px;
color:#021329;
font-size:22px;
}
-
+
#review_box h5 {
text-transform: uppercase;
margin:5px 0 !important;
@@ -1744,41 +1787,41 @@
margin-left: 10px;
margin-bottom:20px;
}
-
+
#review_box .meta {
border-top:1px solid #b0bac1;
border-bottom:1px solid #b0bac1;
margin:5px 0;
padding:7px 0;
}
-
+
#review_box .meta p {
margin:0px;
line-height: 14px;
}
-
+
.ratings {
border-bottom:1px solid #b0bac1;
padding-bottom:5px;
margin-bottom:5px;
}
-
+
.ratings dd {
height:11px;
width:182px;
margin:0 0 5px;
- }
+ }
.term1 {
background-image: url('images/1.gif');
background-repeat:no-repeat;
}
-
+
.term2 {
background-image: url('images/2.gif');
background-repeat:no-repeat;
}
-
+
.term3 {
background-image: url('images/3.gif');
background-repeat:no-repeat;
@@ -1788,7 +1831,7 @@
background-image: url('images/4.gif');
background-repeat:no-repeat;
}
-
+
.term5 {
background-image: url('images/5.gif');
background-repeat:no-repeat;
@@ -1797,7 +1840,7 @@
.the_tags {
font-size:13px;
}
-
+
.the_tags a {
font-style: italic;
color:#5b5a54;
@@ -1833,39 +1876,39 @@
ul#sidebar {
margin:0px;
}
-
+
ul#sidebar li {
list-style-type: none;
}
-
+
.drillz {
background-color:#fff;
height:20px;
line-height:0px;
- padding:2px 10px 2px 0;
+ padding:2px 10px 2px 0;
}
-
+
.drill-list {
margin-bottom:15px;
}
-
+
.term-item {
list-style-image:url('images/uncheck.gif');
font-weight: bold;
}
-
+
.current-term {
list-style-image:url('images/check.gif');
}
-
+
#sidebar li ul li {
font-weight: bold;
}
-
+
.nobold li {
font-weight: 400 !important;
}
-
+
#term-list-post_tag ul li {
font-weight: 400 !important;
}
@@ -1878,7 +1921,7 @@
li.term-item {
margin-left:20px;
}
-
+
.side {
background-color: white;
margin-top: 10px;
@@ -1904,7 +1947,7 @@
font-style: normal;
font-weight: 900;
}
-
+
body#shop {
background: none!important;
}
@@ -2181,7 +2224,7 @@
}
.hoa.onair {
- background-image: url('../img/Google_Hangout_WebAd2_on_air.gif');
+ background-image: url('../img/Google_Hangout_WebAd2_on_air.gif');
}
.hoa h4 {
@@ -2210,31 +2253,31 @@
background-color:#dff0f8;
padding:10px 0 5px 15px;
}
-
+
#make-money #box-area ul {
- background-color:#fff;
+ background-color:#fff;
padding:20px 10px;
- margin:15px;color:#333;
- list-style-image:none;
- list-style-position:outside;
+ margin:15px;color:#333;
+ list-style-image:none;
+ list-style-position:outside;
list-style-type:square;
- color:#328938;
- margin-left:0;
+ color:#328938;
+ margin-left:0;
padding-left:25px;
}
-
+
#make-money #box-area ul li {color:green;}
#make-money #box-area ul li span { color:black;}
-#make-money ul.specials {
- list-style-type: square;
+#make-money ul.specials {
+ list-style-type: square;
margin:0 0 10px 15px;
padding-bottom:5px;
}
-#make-money ul.specials li {
-color:green;
+#make-money ul.specials li {
+color:green;
font-size:13px;
line-height:18px;
}
@@ -2243,14 +2286,14 @@
#make-money a.money {
color:green;
- text-decoration:none;
+ text-decoration:none;
}
#make-money h3, #make-money li {font-family: "proxima-nova", sans-serif;}
-
+
#make-money h3 {
margin:0;
- font-size:1.2em;
+ font-size:1.2em;
font-weight:normal;
margin:0;
}
@@ -2293,7 +2336,7 @@
width: auto !important;
}
#isBillShipAddrNotSame {
- width:auto !important;
+ width:auto !important;
width: auto !important;
}
#make_subcription_form .btn-02-area, #make_subcription_form .btn-01-area {
@@ -2562,7 +2605,7 @@
margin-top: 30px;
label {
font-size:16px;
- }
+ }
.control-label{
font-size:16px;
font-weight:600;
@@ -2582,7 +2625,7 @@
.ctx_blocks_widget2 {
.ctx_subhead {
background-color: transparent !important;
- }
+ }
.ctx_link {
font-size: 13px !important;
line-height: 16px !important;
@@ -2617,88 +2660,88 @@
.sprite {
background-image:url('../images/makezine_sprites.png');
}
-.sprite-arrow-footer {
- background-position: 0 0;
- width: 95px;
- height: 46px;
+.sprite-arrow-footer {
+ background-position: 0 0;
+ width: 95px;
+ height: 46px;
margin: -40px 0 5px -16px;
border: none !important;
-}
-.sprite-digital-book-foot {
- background-position: 0 -51px;
- width: 29px;
- height: 22px;
+}
+.sprite-digital-book-foot {
+ background-position: 0 -51px;
+ width: 29px;
+ height: 22px;
float: left;
padding-right: 5px;
margin-top: 29px;
-}
-.sprite-facebook {
- background-position: 0 -78px;
- width: 24px;
- height: 24px;
-}
-.sprite-flickr {
- background-position: 0 -107px;
- width: 24px;
- height: 24px;
-}
-.sprite-google-plus {
- background-position: 0 -136px;
- width: 24px;
- height: 24px;
-}
-.sprite-instagram {
- background-position: 0 -165px;
- width: 24px;
- height: 24px;
-}
-.sprite-makershed_footer1 {
- background-position: 0 -194px;
- width: 29px;
- height: 22px;
+}
+.sprite-facebook {
+ background-position: 0 -78px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-flickr {
+ background-position: 0 -107px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-google-plus {
+ background-position: 0 -136px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-instagram {
+ background-position: 0 -165px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-makershed_footer1 {
+ background-position: 0 -194px;
+ width: 29px;
+ height: 22px;
float: left;
margin: 30px 5px 0 13px;
-}
-.sprite-pinterest {
- background-position: 0 -221px;
- width: 24px;
- height: 24px;
-}
-.sprite-stumbleupon {
- background-position: 0 -274px;
- width: 24px;
- height: 24px;
-}
-.sprite-twitter {
- background-position: 0 -303px;
- width: 24px;
- height: 24px;
-}
-.sprite-youtube {
- background-position: 0 -332px;
- width: 24px;
- height: 24px;
}
+.sprite-pinterest {
+ background-position: 0 -221px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-stumbleupon {
+ background-position: 0 -274px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-twitter {
+ background-position: 0 -303px;
+ width: 24px;
+ height: 24px;
+}
+.sprite-youtube {
+ background-position: 0 -332px;
+ width: 24px;
+ height: 24px;
+}
// HOME PAGE JPG SPRITES
.sprite-images {
background-image:url('../images/sprite_images.jpg');
}
-.sprite-forum {
- background-position: 0 0;
- width: 620px;
- height: 174px;
-}
-.sprite-page2 {
- background-position: 0 -176px;
- width: 620px;
- height: 174px;
-}
-.sprite-sip {
- background-position: 0 -352px;
- width: 620px;
- height: 174px;
-}
-.sprite-forum{ background-position: 0 0; width: 620px; height: 174px; }
-.sprite-page2{ background-position: 0 -176px; width: 620px; height: 174px; }
-.sprite-sip{ background-position: 0 -352px; width: 620px; height: 174px; }
\ No newline at end of file
+.sprite-forum {
+ background-position: 0 0;
+ width: 620px;
+ height: 174px;
+}
+.sprite-page2 {
+ background-position: 0 -176px;
+ width: 620px;
+ height: 174px;
+}
+.sprite-sip {
+ background-position: 0 -352px;
+ width: 620px;
+ height: 174px;
+}
+.sprite-forum{ background-position: 0 0; width: 620px; height: 174px; }
+.sprite-page2{ background-position: 0 -176px; width: 620px; height: 174px; }
+.sprite-sip{ background-position: 0 -352px; width: 620px; height: 174px; }
\ No newline at end of file
Index: less/style.less
===================================================================
--- less/style.less (revision 141566)
+++ less/style.less (working copy)
@@ -13,7 +13,25 @@
@light-grey: #cdcdcd;
@font: "proxima-nova",sans-serif;
@blue: #005d8f;
+@border-radius-base: 4px;
+@border-radius-large: 6px;
+@border-radius-small: 3px;
+@state-success-text: #3c763d;
+@state-success-bg: #dff0d8;
+@state-success-border: darken(spin(@state-success-bg, -10), 5%);
+@state-info-text: #31708f;
+@state-info-bg: #d9edf7;
+@state-info-border: darken(spin(@state-info-bg, -10), 7%);
+
+@state-warning-text: #8a6d3b;
+@state-warning-bg: #fcf8e3;
+@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
+
+@state-danger-text: #a94442;
+@state-danger-bg: #f2dede;
+@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
+
// Bring in MAKE Styles.
@import "make/mixins.less";
@import "make/style.less";
@@ -42,4 +60,6 @@
@import "make/search.less";
@import "make/shortcodes.less";
@import "make/comments.less";
-@import "make/aside.less";
\ No newline at end of file
+@import "make/aside.less";
+@import "make/contribute.less";
+@import "make/fileinput.less";
\ No newline at end of file
Index: page-contribute-project.php
===================================================================
--- page-contribute-project.php (revision 0)
+++ page-contribute-project.php (working copy)
@@ -0,0 +1,461 @@
+<?php
+/*
+ * Template Name: Post Contribute Form
+ *
+ * @package makeblog
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
+ *
+ */
+get_header(); ?>
+
+ <div class="single">
+
+ <div class="container authentication">
+
+ <div class="row">
+
+ <div class="span12">
+
+ <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
+
+ <article class="post-holder category-top" style="display:none;">
+ <h3 class="submitted-title">Preview</h3>
+ <header class="projects-masthead">
+ <h1 class="post-title"></h1>
+ </header>
+ <div class="row content-wrapper">
+ <div class="span8">
+ <section class="post-content"></section>
+ </div>
+ <div class="span4">
+
+ <div class="projects-ad">
+
+ <!-- Beginning Sync AdSlot 2 for Ad unit header ### size: [[300,250]] -->
+ <div id='div-gpt-ad-664089004995786621-2'>
+ <script type='text/javascript'>
+ googletag.cmd.push(function(){googletag.display('div-gpt-ad-664089004995786621-2')});
+ </script>
+ </div>
+ <!-- End AdSlot 2 -->
+
+ </div>
+
+ <div class="parts-tools">
+ <ul class="top">
+ <li class="active"><a href="#parts-pane" data-toggle="tab">Parts</a></li>
+ <li class="divider"> / </li>
+ <li class=""><a href="#tools-pane" data-toggle="tab">Tools</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane tools-pane" id="tools-pane">
+ <p>No tools, yet...</p>
+ </div>
+ <div class="tab-pane active parts-pane" id="parts-pane">
+ <p>No parts, yet...</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="bottom-steps" id="target">
+
+ <form class="form form-horizontal contribute-form-get-steps" method="post">
+ <?php echo wp_nonce_field( 'get_steps', 'get_steps' ); ?>
+ <input type="hidden" name="post_id" value="80">
+ </form>
+
+ <div class="row">
+
+ <div class="span4">
+
+ <div class="steps-list-output"></div>
+
+ <!-- Beginning Sync AdSlot 3 for Ad unit header ### size: [[300,250]] -->
+ <div id='div-gpt-ad-664089004995786621-3'>
+ <script type='text/javascript'>
+ // We should find a way to display this ad...
+ // googletag.display('div-gpt-ad-664089004995786621-3');
+ </script>
+ </div>
+ <!-- End AdSlot 3 -->
+
+ </div>
+
+ <div class="span8">
+
+ <div class="tab-content" id="steppers">
+
+ <div class="steps-output"></div>
+
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+
+ <div class="saving-progress"></div>
+ </article>
+
+
+ <section id="contribute-form-wrapper" <?php post_class(); ?>>
+
+ <!-- Contribute -->
+ <form class="form form-horizontal validate-form contribute-form" id="add-post-content" method="post">
+ <?php echo wp_nonce_field( 'contribute_post_nonce', 'contribute_post' ); ?>
+ <input type="hidden" name="user_id" id="user_id" class="user_id" value="<?php echo get_current_user_id(); ?>">
+ <fieldset>
+ <div class="control-group">
+ <div class="control-label"></div>
+ <div class="controls">
+ <div class="projects-masthead">
+ <h1><?php the_title(); ?></h1>
+ </div>
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="post_title">Title</label>
+ <div class="controls">
+ <input type="text" class="input-xlarge" name="post_title" id="post_title" required>
+ <p class="help-block">Add the name of the post here.</p>
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="post_content">Content</label>
+ <div class="controls">
+ <?php wp_editor( '', 'post_content', array( 'teeny' => true ) ); ?>
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="category">Category</label>
+ <div class="controls">
+ <?php
+ $categories = get_categories();
+
+ if ( ! empty( $categories ) ) {
+
+ echo '<select name="cat" id="cat" class="postform" required>';
+ echo '<option value="">– Select A Category –</option>';
+ foreach ( $categories as $cat ) {
+ echo '<option class="category-' . absint( $cat->term_id ) . ' category-' . esc_attr( $cat->slug ) . '" value="' . absint( $cat->term_id ) . '">' . esc_html( $cat->name ) . '</option>';
+ }
+ echo '</select>';
+
+ }
+ ?>
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="browse_file">Image</label>
+ <div class="controls">
+ <input type="file" name="" value="" title="Add One or More Images" id="file" class="file-inputs" multiple required>
+ </div>
+ </div>
+ <div class="form-actions">
+ <button type="submit" class="btn btn-warning submit-review" data-type="projects">Want to add steps to the post?</button>
+ <button type="submit" class="btn btn-primary submit-review" data-type="post">Submit for Review</button>
+ </div>
+ </fieldset>
+ </form>
+
+ <!-- Steps -->
+ <form class="form form-horizontal validate-form contribute-form-steps" id="add-steps" action="add_steps" method="post">
+ <fieldset>
+ <section class="control-group">
+ <div class="control-label"></div>
+ <div class="controls">
+ <h1>Add Steps to the Project</h1>
+ </div>
+ </section>
+ <section class="nonce">
+ <?php echo wp_nonce_field( 'contribute_steps_nonce', 'contribute_steps_nonce' ); ?>
+ <input type="hidden" name="total-steps" value="1">
+ <input type="hidden" class="post_ID" name="post_ID" value="">
+ </section>
+ <section class="steps-wrapper">
+ <div class="steps-list">
+ <div class="step row">
+ <div class="span12">
+ <h4 class="step-title">Step 1</h4>
+ <input type="hidden" class="step-number" name="step-number-1" value="1">
+ </div>
+ <div class="image-wrapper span3">
+ <div class="fileinput fileinput-new" data-provides="fileinput">
+ <div class="fileinput-preview thumbnail" data-trigger="fileinput" style="width: 200px; height: 150px;"></div>
+ <div>
+ <span class="btn btn-default btn-file">
+ <span class="fileinput-new">Select image</span>
+ <span class="fileinput-exists">Change</span>
+ <input type="file" class="step-file" id="step-image" name="step-images-1[]" required>
+ </span>
+ <a href="#" class="btn btn-default fileinput-exists" data-dismiss="fileinput">Remove</a>
+ </div>
+ </div>
+ </div>
+ <div class="content-wrapper span9">
+ <input type="text" class="title" name="step-title-1" placeholder="Enter your step title..." value="">
+ <textarea name="step-lines-1[]" id="step_content"></textarea>
+ </div>
+ </div>
+ </div>
+ <script id="steps-template" type="text/template">
+ <div class="step row">
+ <div class="span12">
+ <h4 class="step-title">Step ##count##</h4>
+ <input type="hidden" class="step-number" name="step-number-##count##" value="##count##">
+ </div>
+ <div class="image-wrapper span3">
+ <div class="fileinput fileinput-new" data-provides="fileinput">
+ <div class="fileinput-preview thumbnail" data-trigger="fileinput" style="width: 200px; height: 150px;"></div>
+ <div>
+ <span class="btn btn-default btn-file">
+ <span class="fileinput-new">Select image</span>
+ <span class="fileinput-exists">Change</span>
+ <input type="file" class="step-file" id="step-image" name="step-images-##count##[]">
+ </span>
+ <a href="#" class="btn btn-default fileinput-exists" data-dismiss="fileinput">Remove</a>
+ </div>
+ </div>
+ </div>
+ <div class="content-wrapper span9">
+ <input type="text" class="title" name="step-title-##count##" placeholder="Enter your step title..." value="">
+ <textarea name="step-lines-##count##[]" class="step_content" id="step_content"></textarea>
+ <button class="btn alignright remove-step"><i class="icon icon-minus"></i> Remove Step</button>
+ </div>
+ </div>
+ </script>
+ <section class="repeater-tools">
+ <div class="control-group">
+ <label class="control-label" for="tools-url"></label>
+ <div class="controls">
+ <button class="btn add-step"><i class="icon icon-plus"></i> Add Another Step</button>
+ </div>
+ </div>
+ </section>
+ <div class="form-actions">
+ <button type="submit" class="btn btn-primary submit-steps">Save Steps</button>
+ </div>
+ </section>
+ </fieldset>
+ </form>
+
+ <!-- Parts Form -->
+ <form class="form form-horizontal validate-form contribute-form-parts" id="add-parts" method="post">
+ <fieldset>
+ <section class="control-group">
+ <div class="control-label"></div>
+ <div class="controls">
+ <h1>Add Parts to the Project</h1>
+ </div>
+ </section>
+ <section class="nonce">
+ <?php echo wp_nonce_field( 'contribute_parts', 'contribute_parts' ); ?>
+ <input type="hidden" name="total-parts" value="1">
+ <input type="hidden" class="post_ID" name="post_ID" value="">
+ </section>
+ <div class="parts-wrapper">
+ <div class="parts-list">
+ <div class="part row">
+ <div class="span12">
+ <div class="control-group">
+ <label class="control-label"></label>
+ <div class="controls">
+ <h4 class="part-title">Part 1</h4>
+ <input type="hidden" name="part-number-1" value="1">
+ <input type="hidden" name="parts-notes-1" class="parts-notes" id="parts-notes">
+ </div>
+ </div>
+ <section class="part">
+ <div class="control-group">
+ <label class="control-label" for="parts-name">Name</label>
+ <div class="controls">
+ <input type="text" name="parts-name-1" id="parts-name" class="input-xlarge parts-name" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="parts-qty">Quantity</label>
+ <div class="controls">
+ <input type="number" name="parts-qty-1" id="parts-qty" class="input-xlarge parts-qty" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="parts-url">URL</label>
+ <div class="controls">
+ <input type="url" name="parts-url-1" id="parts-url" class="input-xlarge parts-url" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="parts-type">Type</label>
+ <div class="controls">
+ <input type="text" name="parts-type-1" id="parts-type" class="input-xlarge parts-type" value="">
+ </div>
+ </div>
+ </section>
+ </div>
+ </div>
+ </div>
+ <script id="parts-template" type="text/template">
+ <div class="part row">
+ <div class="span12">
+ <div class="control-group">
+ <label class="control-label"></label>
+ <div class="controls">
+ <h4 class="part-title">Part ##count##</h4>
+ <input type="hidden" name="part-number-##count##" class="part-number" value="1">
+ <input type="hidden" name="parts-notes-##count##" class="parts-notes" id="parts-notes">
+ </div>
+ </div>
+ <section class="part">
+ <div class="control-group">
+ <label class="control-label" for="parts-name">Name</label>
+ <div class="controls">
+ <input type="text" name="parts-name-##count##" id="parts-name" class="input-xlarge parts-name" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="parts-qty">Quantity</label>
+ <div class="controls">
+ <input type="number" name="parts-qty-##count##" id="parts-qty" class="input-xlarge parts-qty" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="parts-url">URL</label>
+ <div class="controls">
+ <input type="url" name="parts-url-##count##" id="parts-url" class="input-xlarge parts-url" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="parts-type">Type</label>
+ <div class="controls">
+ <input type="text" name="parts-type-##count##" id="parts-type" class="input-xlarge parts-type" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label for="" class="control-label"></label>
+ <div class="controls">
+ <button class="btn alignleft remove-part"><i class="icon icon-minus"></i> Remove Part</button>
+ </div>
+ </div>
+ </section>
+ </div>
+ </div>
+ </script>
+ <section class="repeater-tools clear">
+ <div class="control-group">
+ <label class="control-label" for="parts-url"></label>
+ <div class="controls">
+ <button class="btn add-part"><i class="icon icon-plus"></i> Add Another Part</button>
+ </div>
+ </div>
+ </section>
+ <div class="form-actions">
+ <button type="submit" class="btn btn-primary submit-parts">Save Parts</button>
+ </div>
+ </section>
+ </fieldset>
+ </form>
+
+ <!-- Let's add the tools -->
+ <form class="form form-horizontal validate-form contribute-form-tools" id="add-tools" method="post">
+ <fieldset>
+ <section class="control-group">
+ <div class="control-label"></div>
+ <div class="controls">
+ <h1>Add Tools to the Project</h1>
+ </div>
+ </section>
+ <section class="nonce">
+ <?php echo wp_nonce_field( 'contribute_tools', 'contribute_tools' ); ?>
+ <input type="hidden" name="total-tools" value="1">
+ <input type="hidden" class="post_ID" name="post_ID" value="">
+ </section>
+ <div class="tool-wrapper">
+ <div class="tools-list">
+ <div class="tool row">
+ <div class="span12">
+ <div class="control-group">
+ <label class="control-label"></label>
+ <div class="controls">
+ <h4 class="part-title">Tool 1</h4>
+ <input type="hidden" name="tools-number-1" value="1">
+ <input type="hidden" name="tools-thumb-1" value="">
+ <input type="hidden" name="tools-notes-1" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="tools-name">Name</label>
+ <div class="controls">
+ <input type="text" name="tools-name-1" id="tools-name" class="input-xlarge" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="tools-url">URL</label>
+ <div class="controls">
+ <input type="url" name="tools-url-1" id="tools-url" class="input-xlarge" value="">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script id="tools-template" type="text/template">
+ <div class="tool row">
+ <div class="span12">
+ <div class="control-group">
+ <label class="control-label"></label>
+ <div class="controls">
+ <h4 class="tool-title">Tool ##count##</h4>
+ <input type="hidden" name="tools-number-##count##" class="tools-number" value="">
+ <input type="hidden" name="tools-thumb-##count##" class="tools-thumb" value="">
+ <input type="hidden" name="tools-notes-##count##" class="tools-notes" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="tools-name">Name</label>
+ <div class="controls">
+ <input type="text" name="tools-name-##count##" id="tools-name" class="input-xlarge tools-name" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label class="control-label" for="tools-url">URL</label>
+ <div class="controls">
+ <input type="url" name="tools-url-##count##" id="tools-url" class="input-xlarge tools-url" value="">
+ </div>
+ </div>
+ <div class="control-group">
+ <label for="" class="control-label"></label>
+ <div class="controls">
+ <button class="btn alignleft remove-tool"><i class="icon icon-minus"></i> Remove Tool</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </script>
+ <section class="repeater-tools">
+ <div class="control-group">
+ <label class="control-label" for="tools-url"></label>
+ <div class="controls">
+ <button class="btn add-tool"><i class="icon icon-plus"></i> Add Another Tool</button>
+ </div>
+ </div>
+ </section>
+ <div class="form-actions">
+ <button type="submit" class="btn btn-primary submit-tools">Save Tools</button>
+ </div>
+ </fieldset>
+ </form>
+ </section>
+
+ <?php endwhile; else: endif; ?>
+
+ <div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+<?php get_footer(); ?>
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment