Commit e6408760 authored by KR Moorhouse's avatar KR Moorhouse

Resolved merge conflicts.

parents 96edb7eb 24b03b4c
This diff is collapsed.
This diff is collapsed.
......@@ -159,6 +159,55 @@ define( ['views/app/drawer/optionRepeaterError'], function( ErrorView ) {
// The template requires a string.
return label.innerHTML;
},
renderNonSaveFieldSelect: function( dataID, value ){
var initialOption, select, emptyContainer, label;
var fields = nfRadio.channel( 'fields' ).request( 'get:collection' );
initialOption = document.createElement( 'option' );
initialOption.value = '';
initialOption.label = '--';
initialOption.innerHTML = '--';
select = document.createElement( 'select' );
select.classList.add( 'setting' );
select.setAttribute( 'data-id', dataID );
select.appendChild( initialOption );
// Build a lookup table for fields we want to remove from our fields list.
var removeFieldsLookup = [ 'html', 'submit', 'hr',
'recaptcha', 'spam', 'creditcard', 'creditcardcvc',
'creditcardexpiration', 'creditcardfullname',
'creditcardnumber', 'creditcardzip' ];
fields.each( function( field ){
// Check for the field type in our lookup array and...
if( jQuery.inArray( field.get( 'type' ), removeFieldsLookup ) !== -1 ) {
// Return if the type is in our lookup array.
return '';
}
var option = document.createElement( 'option' );
if ( value == field.get( 'key' ) ) {
option.setAttribute( 'selected', 'selected' );
}
option.value = field.get( 'key' );
option.innerHTML = field.get( 'label' );
option.label = field.get( 'label' );
select.appendChild( option );
});
label = document.createElement( 'label' );
label.classList.add( 'nf-select' );
label.appendChild( select );
// Select Lists need an empty '<div></div>' for styling purposes.
emptyContainer = document.createElement( 'div' );
label.appendChild( emptyContainer );
// The template requires a string.
return label.innerHTML;
},
renderOptions: function( column, value ) {
if( 'undefined' == typeof that.options.columns[ column ] ) return;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
......@@ -16,6 +16,10 @@ a {
outline: none !important;
border: 0;
}
a:focus {
outline: none !important;
box-shadow: unset; // Override WP focus state.
}
#wpwrap,
#wpcontent {
......@@ -189,8 +193,10 @@ Sections
---------------------------------------------*/
@import 'sections/widgets';
@import 'sections/services';
@import 'sections/apps';
@import 'sections/memberships';
@import 'sections/promotions';
/*
Widgets
......@@ -339,3 +345,33 @@ Loading Spinner
}
/*
Promotions
---------------------------------------------*/
.promotion--wrapper {
> div {
a {
position: relative;
display: block;
background: #f1f1f1;
padding: 16px 80px;
text-align: center;
border-radius: 4px;
font-size: 16px;
color: #ef4748 !important;
overflow: hidden;
line-height: 24px;
> .cta {
font-size: 22px;
}
> .dashicons, .dashicons-before::before {
position: absolute;
left: 0;
top: -20px;
font-size: 90px;
transform: rotate(20deg);
}
}
}
}
\ No newline at end of file
/* This section intentionally left blank. */
\ No newline at end of file
@keyframes nf-connected {
0% {
box-shadow: 0 0 2px rgba(255, 255, 255, 0),
inset 0 0 2px rgba(255, 255, 255, 0);
}
50% {
box-shadow: 0 0 2px #84cc1e,
inset 100vw 0 2px rgba(255, 255, 255, .2);
}
100% {
box-shadow: 0 0 2px rgba(255, 255, 255, 0),
inset 100vw 0 2px rgba(255, 255, 255, 0);
}
}
.nf-notices--oauth {
margin-top: -40px; // Account for the bottom margin on the topbar.
padding-bottom: 20px; // Increase hover target, while maintaining whitespace.
text-align:center;
}
.nf-oauth--checking {
padding: 5px;
}
.nf-oauth--connect {
display:block;
background-color:#1EA9EA;
color:white;
padding: 5px;
text-decoration:none;
}
.nf-oauth--connected {
color: transparent;
padding: 0;
line-height: .5;
transition: all .4s;
background-color:#84CC1E;
}
.nf-oauth--connected:not(:hover) {
animation: nf-connected 8s ease infinite;
animation-delay: 2s;
}
.nf-notices--oauth:hover .nf-oauth--connected {
color:white;
line-height: 2;
padding: 5px;
}
.promotion--wrapper {
width: 100%;
max-width: 50rem;
margin: auto;
margin-bottom: 20px; // Maintain whitespace.
}
.promotion--wrapper a:focus {
box-shadow: unset; // Override WP focus state.
}
@keyframes nf-service-installing {
100% {
transform:rotate(360deg);
}
}
.dashicons-update-spin {
animation: nf-service-installing 1s linear infinite;
}
.services {
.nf-extend.full {
flex-basis: 100%;
}
.nf-extend-buttons a {
margin-top: 20px;
}
.nf-extend-buttons .nf-button {
margin-top: auto; // Reset from anchor.
margin-bottom: 10px; // Account for toggle.
}
}
.jBox-Confirm-button {
height: auto !important;
font-size: 17px !important;
padding: 10px 30px !important;
}
.jBox-Confirm-button-submit {
background: #1ea9ea !important;
border: 1px solid #fff !important;
color: #fff !important;
}
.jBox-Confirm-button-cancel {
background: #fff !important;
border: 1px solid #1ea9ea !important;
color: #1ea9ea !important;
}
define([ 'models/oauthModel' ], function( OAuthModel ) {
var controller = Marionette.Object.extend( {
initialize: function() {
this.oauth = new OAuthModel();
nfRadio.channel( 'dashboard' ).reply( 'get:oauth', this.getOAuth, this );
nfRadio.channel( 'dashboard' ).reply( 'disconnect:oauth', this.disconnect, this );
this.initOAuth();
},
getOAuth: function() {
return this.oauth;
},
/*
* Fetch the OAuth Model add and notify via nfRadio.
*/
initOAuth: function() {
this.oauth.fetch({
success: function( model ){
nfRadio.channel( 'dashboard' ).trigger( 'fetch:oauth' );
}
});
},
/**
* Confirm disconnecting services, then POST to the server to to disconnect.
*/
disconnect: function() {
var that = this;
new jBox('Confirm', {
width: 750,
content: 'Disconnecting from my.ninjaforms.com will disrupt the functionality of all services.',
confirmButton: 'Disconnect',
cancelButton: 'Stay Connected',
closeOnConfirm: true,
confirm: function(){
jQuery.ajax({
type: "POST",
url: ajaxurl + '?action=nf_oauth_disconnect',
success: function( response ){
console.log( response );
that.initOAuth();
}
});
}
}).open();
}
});
return controller;
} );
define([ 'models/serviceCollection' ], function( ServiceCollection ) {
var controller = Marionette.Object.extend( {
initialize: function() {
this.services = new ServiceCollection();
nfRadio.channel( 'dashboard' ).reply( 'install:service', this.installService, this );
nfRadio.channel( 'dashboard' ).reply( 'get:services', this.getServices, this );
this.fetchServices();
},
getServices: function() {
return this.services;
},
/*
* Fetch services, with an optional callback function.
*/
fetchServices: function( callback ) {
this.services.fetch({
success: function( model ){
if( callback ) callback( model );
nfRadio.channel( 'dashboard' ).trigger( 'fetch:services' );
}
});
},
/*
* Request the remote install of the service's corresponding plugin.
*/
installService: function( serviceModel ) {
var that = this;
if ( ! ( serviceModel instanceof Backbone.Model ) ) {
var serviceModel = this.services.find( function( model ) {
return serviceModel == model.get( 'slug' );
});
}
serviceModel.set( 'is_installing', true );
var slug = serviceModel.get( 'slug' );
var installPath = serviceModel.get( 'installPath' );
// Request to Install the service plugin.
jQuery.post( ajaxurl, { action: 'nf_services_install', plugin: slug, install_path: installPath }, function( response ){
that.fetchServices(function(){
nfRadio.channel( 'dashboard' ).request( 'install:service:' + slug );
});
} );
}
});
return controller;
} );
var nfRadio = Backbone.Radio;
var nfDebug = false;
if( ! useServices ){
/**
* If the feature flag isn't set then filter out the "Services" tab.
*/
nfDashItems = nfDashItems.filter(function(item){
return 'services' !== item.slug;
});
}
jQuery( document ).ready( function( $ ) {
require( [ 'controllers/formsController', 'views/dashboardView' ], function( FormsController, DashboardView ) {
require( [ 'controllers/formsController', 'controllers/oauthController', 'controllers/servicesController', 'views/dashboardView' ], function( FormsController, OAuthController, ServicesController, DashboardView ) {
var NinjaFormsDashboard = Marionette.Application.extend( {
......@@ -31,6 +40,8 @@ jQuery( document ).ready( function( $ ) {
this.showView( new DashboardView() );
this.controllers.forms = new FormsController();
if( useServices ) this.controllers.oauth = new OAuthController();
if( useServices ) this.controllers.services = new ServicesController();
//var data = {id: 1, title: 'Contact Me', created: '10-23-2016'};
//var form = new FormModel(data);
......@@ -61,18 +72,20 @@ jQuery( document ).ready( function( $ ) {
} );
} );
/**
* Submenu Routing
*/
jQuery( 'a[href="admin.php?page=ninja-forms#new-form"]' ).on( 'click', function(){
jQuery( 'a[href="admin.php?page=ninja-forms#new-form"]' ).on( 'click', function( event ){
event.preventDefault();
window.location.hash = 'new-form';
nfRadio.channel( 'dashboard' ).request( 'show:widgets' );
nfRadio.channel( 'widget-forms' ).request( 'show:newFormsGrid' );
} );
jQuery( 'a[href="admin.php?page=ninja-forms#apps"]' ).on( 'click', function(){
jQuery( 'a[href="admin.php?page=ninja-forms#apps"]' ).on( 'click', function( event ){
event.preventDefault();
window.location.hash = 'apps';
nfRadio.channel( 'dashboard' ).request( 'show:apps' );
......@@ -85,3 +98,12 @@ jQuery( 'a[href="admin.php?page=ninja-forms"]' ).on( 'click', function( event ){
nfRadio.channel( 'dashboard' ).request( 'show:widgets' );
nfRadio.channel( 'widget-forms' ).request( 'show:formsTable' );
} );
/**
* Hash Change Routing Fallback
* To avoid the need to manually add listeners to views, this is a generic hash change listener.
*/
jQuery(window).on('hashchange', function() {
var hash = window.location.hash.substr(1);
nfRadio.channel( 'dashboard' ).request( 'show:' + hash );
});
define( [], function() {
var model = Backbone.Model.extend( {
defaults: {
connected: null,
connect_url: '',
},
url: function() {
return ajaxurl + "?action=nf_oauth";
},
initialize: function() {
/* ... */
},
/*
* Use the `data` property of the response.
*/
parse: function( response, options ){
return response.data;
}
} );
return model;
} );
define( [], function() {
var model = Backbone.Model.extend( {
defaults: {
id: '',
content: '',
},
initialize: function() {
/* ... */
},
} );
return model;
} );
define( ['models/serviceModel'], function( ServiceModel ) {
var collection = Backbone.Collection.extend( {
model: ServiceModel,
comparator: 'name',
url: function() {
return ajaxurl + "?action=nf_services";
},
initialize: function() {
/* ... */
},
/*
* Use the `data` property of the response.
*/
parse: function( response, options ){
return response.data;
},
} );
return collection;
} );
define( [], function() {
var model = Backbone.Model.extend( {
defaults: {
objectType: 'service',
name: '',
slug: '',
installPath: '',
description: '',
enabled: null,
infoLink: null,
serviceLink: null,
is_installing: false,
classes: ''
},
url: function() {
return ajaxurl + "?action=nf_service_" + this.get( 'slug' );
},
/*
* - Check for "Success"/Setup modal.
* - (Maybe) Auto-redirect to the service.
*/
initialize: function() {
// Check for successful setup.
if( this.get( 'slug' ) == serviceSuccess && this.get( 'successMessage' ) ){
new jBox( 'Modal', {
width: 300,
addClass: 'dashboard-modal',
overlay: true,
closeOnClick: 'body',
content: this.get( 'successMessage' ),
} ).open();
}
// Auto-redirect to the serviceLink on install.
var that = this;
nfRadio.channel( 'dashboard' ).reply( 'install:service:' + this.get( 'slug' ), function(){
// If no service link url, then no need to auto-redirect.
if( ! that.get( 'serviceLink' ) ) return;
if( ! that.get( 'serviceLink' ).href ) return;
var redirect = that.get( 'serviceLink' ).href;
// Display a redirect notice.
new jBox( 'Modal', {
width: 300,
addClass: 'dashboard-modal',
overlay: true,
closeOnClick: 'body',
content: '<p style="text-align:center;">Redirecting to NinjaForms.com</p>',
} ).open();
// Trigger a redirect, where depends on the connected status.
var oauth = nfRadio.channel( 'dashboard' ).request( 'get:oauth' );
if( ! oauth.get( 'connected' ) ){
if( that.get( 'connect_url' ) ){
return window.location = that.get( 'connect_url' ) + '&redirect=' + redirect;
}
window.location = oauth.get( 'connect_url' ) + '&redirect=' + redirect;
} else {
window.location = redirect;
}
} );
},
/*
* Sync the server with the model.
*/
save: function() {
var that = this;
jQuery.ajax({
type: "POST",
url: this.url(),
data: this.toJSON()
}).done( function( response ){
var data = JSON.parse( response );
if( 'undefined' !== typeof data.error ) {
alert( 'Unable to update the service. ' + data.error );
that.set( 'enabled', ! that.get( 'enabled' ) );
}
nfRadio.channel( 'dashboard').trigger( 'save:service-' + that.get( 'slug' ) );
});
}
} );
return model;
} );
......@@ -6,13 +6,15 @@
* @copyright (c) 2017 WP Ninjas
* @since 3.2
*/
define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections/memberships.js' ], function( WidgetView, AppsView, MembershipsView ) {
define( [ 'views/sections/widgets.js', 'views/sections/services.js', 'views/sections/apps.js', 'views/sections/memberships.js', 'views/oauth.js', 'views/promotion.js' ], function( WidgetView, ServicesView, AppsView, MembershipsView, OAuthView, PromotionView ) {
var view = Marionette.View.extend( {
template: "#tmpl-nf-dashboard",
currentView: 'widgets',
regions: {
notices: '.notices',
promotions: '.promotions',
content: '.content'
},
......@@ -23,6 +25,12 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
e.target.classList.add( 'active' );
this.currentView = 'widgets';
},
'click .services a': function(e){
this.showChildView( 'content', new ServicesView() );
jQuery( '.' + this.currentView).find( 'a' ).removeClass( 'active' );
e.target.classList.add( 'active' );
this.currentView = 'services';
},
'click .apps a': function(e){
this.showChildView( 'content', new AppsView() );
jQuery( '.' + this.currentView).find( 'a' ).removeClass( 'active' );
......@@ -38,10 +46,14 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
},
initialize: function() {
switch( window.location.hash ) {
case '#apps':
this.currentView = 'apps';
break;
case '#services':
this.currentView = 'services';
break;
case '#memberships':
this.currentView = 'memberships';
break;
......@@ -60,6 +72,12 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
jQuery( 'nav.sections .widgets a' ).addClass( 'active' );
this.currentView = 'widgets';
}, this );
nfRadio.channel( 'dashboard' ).reply( 'show:services', function(){
this.showChildView('content', new ServicesView() );
jQuery( 'nav.sections a.active' ).removeClass( 'active' );
jQuery( 'nav.sections .services a' ).addClass( 'active' );
this.currentView = 'services';
}, this );
nfRadio.channel( 'dashboard' ).reply( 'show:apps', function(){
this.showChildView('content', new AppsView() );
jQuery( 'nav.sections a.active' ).removeClass( 'active' );
......@@ -69,6 +87,10 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
},
onRender: function() {
if( useServices ) this.showChildView( 'notices', new OAuthView() );
if( useServices ) this.showChildView( 'promotions', new PromotionView() );
switch( window.location.hash ) {
case '#apps':
var childView = new AppsView();
......@@ -76,6 +98,9 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
case '#memberships':
var childView = new MembershipsView();
break;
case '#services':
var childView = new ServicesView();
break;
case '#widgets':
default:
var childView = new WidgetView();
......@@ -284,7 +309,7 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
jQuery.post( ajaxurl, data );
}
},
templateContext: function() {
var that = this;
return {
......
define( [ 'models/oauthModel' ], function( OAuthModel ) {
var view = Marionette.View.extend( {
model: new OAuthModel(),
template: '#tmpl-nf-notices-oauth',
className: 'nf-notices--oauth',
ui: {
disconnect: '.js--disconnect',
},
/*
* Update when the OAuth controller is synced.
*/
initialize: function( oauthModel ) {
this.listenTo( nfRadio.channel( 'dashboard' ), 'fetch:oauth', this.updateModel );
},
updateModel: function() {
this.model = nfRadio.channel( 'dashboard' ).request( 'get:oauth' );
this.render();
},
events: {
'click @ui.disconnect': function() {
nfRadio.channel( 'dashboard' ).request( 'disconnect:oauth' );
},
}
} );
return view;
} );
define( [ 'models/promotionModel' ], function( PromotionModel ) {
var view = Marionette.View.extend( {
model: null,
template: '#tmpl-nf-promotion',
className: 'nf-promotion',
/*
* Display a single promotion - chosen at random.
*/
initialize: function() {
var promotion = nfPromotions[Math.floor(Math.random()*nfPromotions.length)];
this.model = new PromotionModel( promotion );