Commit 24b03b4c authored by Eric Windham's avatar Eric Windham Committed by GitHub

Merge pull request #3399 from wpninjas/txnmail

Ninja Forms Services
parents 2420426b d36699ac
This diff is collapsed.
This diff is collapsed.
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
......@@ -319,3 +325,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();
......@@ -224,7 +249,7 @@ define( [ 'views/sections/widgets.js', 'views/sections/apps.js', 'views/sections
} );
}
},
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 );
// this.listenTo( nfRadio.channel( 'dashboard' ), 'did:something', this.something );
}
} );
return view;
} );
/**
* @package Ninja Forms
* @subpackage Dashboard
* @copyright (c) 2017 WP Ninjas
* @since 3.2
*/
define( [ 'views/services/services' ], function( ServicesView ) {
var view = Marionette.View.extend( {
template: '#tmpl-nf-services',
regions: {
services: '.services'
},
onRender: function() {
this.showChildView( 'services', new ServicesView() );
}
} );
return view;
} );
define( [], function() {
var view = Marionette.View.extend( {
template: '#tmpl-nf-service',
className: function(){
return 'nf-extend nf-box ' + this.model.get( 'classes' );
},
ui: {
install: '.js--install',
learnMore: '.js--learn-more',
enabled: '.nf-toggle.setting',
toggleEnable: '.nf-toggle + label',
},
events: {
'click @ui.install': function() {
nfRadio.channel( 'dashboard' ).request( 'install:service', this.model );
},
'click @ui.learnMore': function() {
this.showLearnMore();
},
'click @ui.toggleEnable': function() {
if( null == this.model.get( 'enabled' ) ){
if( this.model.get( 'link' ) ){
window.location = this.model.get( 'link' );
return this.render();
}
}
this.model.set( 'enabled', ! this.model.get( 'enabled' ) );
this.model.save("enabled");
this.render();
},
},
initialize: function( oauthModel ) {