HEX
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.30
System: Linux iZj6c1151k3ad370bosnmsZ 3.10.0-1160.76.1.el7.x86_64 #1 SMP Wed Aug 10 16:21:17 UTC 2022 x86_64
User: root (0)
PHP: 7.4.30
Disabled: NONE
Upload Files
File: /var/www/html/www.winghung.com/wp-content/plugins/login-with-ajax/login-with-ajax.php
<?php
/*
Plugin Name: Login With Ajax
Plugin URI: https://loginwithajax.com
Description: Ajax driven login widget. Customisable from within your template folder, and advanced settings from the admin area.
Author: Pixelite
Version: 4.5.1
Author URI: https://pixelite.com/?utm_source=login-with-ajax&utm_medium=plugin-header&utm_campaign=plugins
Tags: Login, Ajax, Redirect, BuddyPress, MU, MultiSite, security, sidebar, admin, widget
Text Domain: login-with-ajax

Copyright (C) 2025 Marcus Sykes

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
define('LOGIN_WITH_AJAX_VERSION', '4.5.1');
define('LOGIN_WITH_AJAX_PATH', dirname(__FILE__));
define('LOGIN_WITH_AJAX_URL', trailingslashit(plugin_dir_url(__FILE__)));

class LoginWithAjax {

	/**
	 * If logged in upon instantiation, it is a user object.
	 * @var WP_User
	 */
	public static  $current_user;
	/**
	 * List of templates available in the plugin dir and theme (populated in init())
	 * @var array
	 */
	public static $templates = array();
	/**
	 * Name of the default template or the currently loaded template. Best not to mess with this and only use for reference.
	 * @var string
	 * @deprecated This will be converted to a private variable in future versions, use \LoginWithAjax::get_template() instead.
	 */
	public static $template;
	/**
	 * lwa_data option
	 * @var array
	 */
	public static $data;
	/**
	 * Location of footer file if one is found when generating a widget, for use in loading template footers.
	 * @var string
	 */
	public static $footer_loc;
	/**
	 * URL for the AJAX Login procedure in templates (including callback and template parameters)
	 * @var string
	 * @deprecated Use \LoginWithAjax::get_login_url() instead.
	 */
	public static $url_login;
	/**
	 * URL for the AJAX Remember Password procedure in templates (including callback and template parameters)
	 * @var string
	 * @deprecated Use \LoginWithAjax::get_remember_url() instead.
	 */
	public static $url_remember;
	/**
	 * URL for the AJAX Registration procedure in templates (including callback and template parameters)
	 * @var string
	 * @deprecated Use \LoginWithAjax::get_register_url() instead.
	 */
	public static $url_register;
	
	public static $localized;
	
	public static $compat_mode = false;

	// Actions to take upon initial action hook
	public static function init(){
		//Load LWA options
		self::$data = get_option('lwa_data');
		if( !is_array(self::$data) ) self::$data = array();
		self::$template = !empty(self::$data['template']) ? self::$data['template'] : 'default';
		//Remember the current user, in case there is a logout
		self::$current_user = wp_get_current_user();

		//Generate URLs for login, remember, and register - backward compatibility
		self::$url_login = static::get_login_url();
		self::$url_register = self::get_register_url();
		self::$url_remember = static::get_remember_url();

		// load dependent files
		include_once('login-with-ajax-options.php');
		include_once('blocks/login/login-block.php');
		include_once('ajaxify/loader.php');
		include_once('transports/transports.php');
		include( 'passkeys/loader.php' );
		include_once('2FA/2FA.php');

		// add authentication filter for other LWA features/add-ons to hook into instead of directly to WP
		add_filter('authenticate', '\LoginWithAjax::authenticate', 9999, 1);
		
		// determine compat mode
		self::$data['compat_mode'] = isset(self::$data['compat_mode']) ? self::$data['compat_mode'] : 'auto';
		if ( !empty( self::$data['compat_mode'] ) ) {
			if ( self::$data['compat_mode'] === 'auto' ) {
				if ( function_exists( 'buddypress' ) || defined( 'BP_VERSION' ) ) {
					self::$compat_mode = true;
				}
			} else {
				self::$compat_mode = true;
			}
		}
		do_action('lwa_pre_loaded');
		
		//Make decision on what to display
		if ( !empty($_REQUEST["lwa"]) && (empty(self::$compat_mode) || empty($_REQUEST['action'])) ) { //AJAX Request
			do_action('lwa_loaded'); // loaded, will exit after this function
		    self::ajax();
		}elseif ( isset($_REQUEST["login-with-ajax-widget"]) ) { //Widget Request via AJAX
			do_action('lwa_loaded'); // loaded, will exit inside this clause
			$instance = ( !empty($_REQUEST["template"]) ) ? array('template' => $_REQUEST["template"]) : array();
			$instance['profile_link'] = ( !empty($_REQUEST["lwa_profile_link"]) ) ? $_REQUEST['lwa_profile_link']:0;
			self::widget( $instance );
			exit();
		}else{
			// add AJAX handler for compatibility with other plugins like BP that need to be fully loaded
			add_action( 'wp_ajax_nopriv_login-with-ajax', array( static::class, 'ajax') );
			//Add logout/in redirection
			add_action('wp_logout', '\LoginWithAjax::logoutRedirect');
			add_filter('logout_url', '\LoginWithAjax::logoutUrl');
			add_filter('login_redirect', '\LoginWithAjax::loginRedirect', 1, 3);
			add_shortcode('login-with-ajax', '\LoginWithAjax::shortcode');
			add_shortcode('lwa', '\LoginWithAjax::shortcode');
		}
		do_action('lwa_loaded');
		self::register_scripts_and_styles();
	}
	
	public static function __callStatic($name, $arguments) {
		if( $name == 'getRegisterLink' ){
			return static::get_register_url( $arguments );
		}
	}
	
	public static function register_scripts_and_styles(){
		//Enqueue scripts - Only one script enqueued here.... theme CSS takes priority, then default JS
		if( !empty(self::$data['legacy']) ){
			wp_register_style( "login-with-ajax", self::locate_template_url('widget.css'), array(), LOGIN_WITH_AJAX_VERSION );
		}else {
			$css_url = defined('WP_DEBUG') && WP_DEBUG ? 'login-with-ajax.css' : 'login-with-ajax.min.css';
			wp_register_style("login-with-ajax", self::locate_template_url($css_url), array(), LOGIN_WITH_AJAX_VERSION);
		}
		//Enqueue scripts - Only one script enqueued here.... theme JS takes priority, then default JS
		if( !empty(self::$data['legacy']) ){
			// if in legacy mode, we check first to see if theme overrides with old filenames, if not then use legacy filename extensions that ship with plugin update
			if( defined('WP_DEBUG') && WP_DEBUG ) {
				$js_url = self::locate_legacy_template('login-with-ajax.source.js') ? 'login-with-ajax.source.js':'login-with-ajax.legacy.js';
			}else {
				$js_url = self::locate_legacy_template('login-with-ajax.js') ? 'login-with-ajax.js':'login-with-ajax.legacy.min.js';
			}
		}else{
			$js_url = defined('WP_DEBUG') && WP_DEBUG ? 'login-with-ajax.js':'login-with-ajax.min.js';
		}
		wp_register_script( "login-with-ajax", self::locate_template_url($js_url), array( 'jquery' ), LOGIN_WITH_AJAX_VERSION );
		add_action('wp_enqueue_scripts', 'LoginWithAjax::enqueue_scripts_and_styles');
		do_action('lwa_register_scripts');
	}
	
	public static function enqueue_scripts_and_styles( $force_load = null ){
		if( $force_load || !is_admin() || (!empty($_REQUEST['legacy-widget-preview']['idBase']) && $_REQUEST['legacy-widget-preview']['idBase'] == 'loginwithajaxwidget') ) {
			wp_enqueue_script( 'login-with-ajax' );
			wp_enqueue_style( 'login-with-ajax' );
			//calendar translations
			static::localize_js();
			do_action('lwa_enqueue', $force_load);
		}
	}
	
	public static function localize_js ( $handle = 'login-with-ajax' ) {
		if( static::$localized ) return;
		$schema = is_ssl() ? 'https':'http';
		$js_vars = array('ajaxurl' => admin_url('admin-ajax.php', $schema), 'off' => false );
		static::$localized = wp_localize_script($handle, 'LWA', apply_filters('lwa_js_vars', $js_vars, $handle));
	}
	
	public static 	function is_rest() {
		if (defined('REST_REQUEST') && REST_REQUEST // (#1)
			|| isset($_GET['rest_route']) // (#2)
			&& strpos( $_GET['rest_route'] , '/', 0 ) === 0)
			return true;
		
		// (#3)
		global $wp_rewrite;
		if ($wp_rewrite === null) $wp_rewrite = new WP_Rewrite();
		
		// (#4)
		$rest_url = wp_parse_url( trailingslashit( rest_url( ) ) );
		$current_url = wp_parse_url( add_query_arg( array( ) ) );
		return strpos( $current_url['path'], $rest_url['path'], 0 ) === 0;
	}
	
	public static function widgets_init(){
		//Include and register widget
		include_once('login-with-ajax-widget.php');
		register_widget("LoginWithAjaxWidget");
	}
	
	public static function check_user_and_nonce( $nonce_action_prefix, $user = null, $ajax = true ) {
		if( empty($user) ) {
			if( !empty($_REQUEST['log']) ) {
				$user = get_user_by('login', $_REQUEST['log']);
			} elseif( !empty($_REQUEST['user_id']) ) {
				$user = get_user_by('user_id', $_REQUEST['user_id']);
			} elseif ( is_user_logged_in() ) {
				$user = wp_get_current_user();
			}
		}
		if( !empty($user) && !empty($_REQUEST['nonce']) && wp_verify_nonce( $_REQUEST['nonce'], $nonce_action_prefix . $user->ID ) ) {
			return $user;
		} elseif( $ajax ) {
			echo LoginWithAjax::json_encode( array(
				'result' => false,
				'error' => 'Invalid nonce or user supplied.', // not translated as this is a bug and edge (if at all)
			) );
			exit();
		}
		return false;
	}
	
	/*
	 * LOGIN OPERATIONS
	 */

	// Decides what action to take from the ajax request
	public static function ajax(){
		$return = array('result'=>false, 'error'=>'Unknown command requested');
		if( !empty($_REQUEST['login-with-ajax']) ) {
			switch ( $_REQUEST["login-with-ajax"] ) {
				case 'login': //A login has been requested
					//remove known interferences
					add_filter( 'ws_plugin__s2member_login_redirect', '__return_false' );
					$return = self::login();
					break;
				case 'remember': //Remember the password
					$return = self::remember();
					break;
				case 'register': //Register
				case 'registration':
					$return = self::register();
					break;
				default: // backwards-compatible with templates where lwa = registration
					do_action( 'lwa_ajax_action_' . $_REQUEST["login-with-ajax"], $return );
					break;
			}
			@header( 'Content-Type: application/javascript; charset=UTF-8', true ); //add this for HTTP -> HTTPS requests which assume it's a cross-site request
			// allow filters to intervene, including returning nothing to default to LWA error
			$response = apply_filters('lwa_ajax_'.$_REQUEST["login-with-ajax"], $return);
			if( !empty($response) ) {
				echo self::json_encode( $response );
			} else {
				echo self::json_encode( $return );
			}
		} else {
			echo self::json_encode( $return ); // no action
		}
		exit();
	}

	// Reads ajax login creds via POSt, calls the login script and interprets the result
	public static function login(){
		$return = array(); //What we send back
		$loginResult = false;
		if( !empty($_REQUEST['log']) && !empty($_REQUEST['pwd']) && trim($_REQUEST['log']) != '' && trim($_REQUEST['pwd'] != '') ){
			$loginResult = wp_signon();
			$return = static::login_result($loginResult);
		}else{
			$return['result'] = false;
			$return['error'] = __('Please supply your username and password.', 'login-with-ajax');
		}
		$return['action'] = 'login';
		//Return the result array with errors etc.
		return apply_filters('lwa_login', $return, $loginResult);
	}
	
	public static function login_result( $loginResult ){
		$return = array();
		if ( $loginResult instanceof WP_User ) {
			//User login successful
			self::$current_user = $loginResult;
			/* @var $loginResult WP_User */
			$return['result'] = true;
			$return['message'] = __("Login Successful, redirecting...",'login-with-ajax');
			//Do a redirect if necessary
			$redirect = self::getLoginRedirect(self::$current_user);
			if( !empty($_REQUEST['redirect_to']) ) $redirect= wp_sanitize_redirect($_REQUEST['redirect_to']);
			if( $redirect != '' ){
				$return['redirect'] = $redirect;
			}
			//If the widget should just update with ajax, then supply the URL here.
			if( isset($_REQUEST['refresh']) || !empty(self::$data['no_login_refresh']) && self::$data['no_login_refresh'] == 1 ){
				if( isset($_REQUEST['refresh']) && empty($_REQUEST['refresh']) || !isset($_REQUEST['refresh']) ){
					//Is this coming from a template?
					$query_vars = ( !empty($_REQUEST['template']) ) ? "&template=".esc_attr($_REQUEST['template']) : '';
					$query_vars .= ( !empty($_REQUEST['lwa_profile_link']) ) ? "&lwa_profile_link=1" : '';
					$return['widget'] = get_bloginfo('wpurl')."?login-with-ajax-widget=1$query_vars";
					$return['message'] = __("Login successful, updating...",'login-with-ajax');
				}
			}
		} elseif ( is_wp_error($loginResult) ) {
			//User login failed
			/* @var WP_Error $loginResult */
			$return['result'] = false;
			$return['error'] = $loginResult->get_error_message();
		} else {
			//Undefined Error
			$return['result'] = false;
			$return['error'] = __('An undefined error has ocurred', 'login-with-ajax');
		}
		return $return;
	}

	/**
	 * Checks post data and registers user, then exits
	 * @return array
	 */
	public static function register(){
	    $return = array();
	    if( get_option('users_can_register') ){
			$errors = register_new_user($_REQUEST['user_login'], $_REQUEST['user_email']);
			if ( !is_wp_error($errors) ) {
				//Success
				$return['result'] = true;
				$return['message'] = __('Registration complete. Please check your e-mail.','login-with-ajax');
				//add user to blog if multisite
				if( is_multisite() ){
				    add_user_to_blog(get_current_blog_id(), $errors, get_option('default_role'));
				}
			}else{
				//Something's wrong
				$return['result'] = false;
				$return['error'] = $errors->get_error_message();
			}
			$return['action'] = 'register';
	    }else{
	    	$return['result'] = false;
			$return['error'] = __('Registration has been disabled.','login-with-ajax');
	    }
		return $return;
	}

	// Reads ajax login creds via POST, calls the login script and interprets the result
	public static function remember(){
		$return = array(); //What we send back
		//if we're not on wp-login.php, we need to load it since retrieve_password() is located there
		if( !function_exists('retrieve_password') ){
		    ob_start();
		    include_once(ABSPATH.'wp-login.php');
		    ob_clean();
		}
		$result = retrieve_password();
		if ( $result === true ) {
			//Password correctly remembered
			$return['result'] = true;
			$return['message'] = __("We have sent you an email", 'login-with-ajax');
		} elseif ( strtolower(get_class($result)) == 'wp_error' ) {
			//Something went wrong
			/* @var $result WP_Error */
			$return['result'] = false;
			$return['error'] = $result->get_error_message();
		} else {
			//Undefined Error
			$return['result'] = false;
			$return['error'] = __('An undefined error has ocurred', 'login-with-ajax');
		}
		$return['action'] = 'remember';
		//Return the result array with errors etc.
		return $return;
	}

	//Added fix for WPML
	public static function logoutUrl( $logout_url ){
		//Add ICL if necessary
		if( defined('ICL_LANGUAGE_CODE') ){
			$logout_url .= ( strstr($logout_url,'?') !== false ) ? '&amp;':'?';
			$logout_url .= 'lang='.ICL_LANGUAGE_CODE;
		}
		return $logout_url;
	}
	
	// links to register, sign in or remember
	
	/**
	 * Returns registration url (escaped) with template querystring if supplied
	 * @param string $template
	 * @return string
	 * @see \LoginWithAjax::template_link()
	 */
	public static function get_register_url( $template = null ){
	    if ( function_exists('bp_get_signup_page') && (empty($_REQUEST['action']) || ($_REQUEST['action'] != 'deactivate' && $_REQUEST['action'] != 'deactivate-selected')) ) { //Buddypress
	    	$register_link = bp_get_signup_page();
	    }elseif ( is_multisite() ) { //MS
	    	$register_link = apply_filters( 'wp_signup_location', network_site_url( 'wp-signup.php' ) );
	    } else {
	    	$register_link = wp_registration_url();
	    }
	    return static::template_link( $register_link, $template );
	}
	
	/**
	 * Returns forgotten password url (escaped) with template querystring if supplied
	 * @param string $template
	 * @return string
	 * @see \LoginWithAjax::template_link()
	 */
	public static function get_remember_url( $template = null ){
		$url = apply_filters('lwa_remember_url', wp_lostpassword_url(), $template );
		return static::template_link( $url, $template );
	}
	
	/**
	 * Returns login url (escaped) with template querystring if supplied.
	 * @param string $template
	 * @return string
	 * @see \LoginWithAjax::template_link()
	 */
	public static function get_login_url( $template = null ){
		$url = apply_filters('lwa_login_url', wp_login_url(), $template);
		return static::template_link( $url, $template );
	}

	/*
	 * Redirect Functions
	 */

	public static function logoutRedirect(){
		$redirect = self::getLogoutRedirect();
		if($redirect != ''){
			wp_safe_redirect($redirect);
			exit();
		}
	}

	public static function getLogoutRedirect(){
		$data = self::$data;
		//Global redirect
		$redirect = '';
		if( !empty($data['logout_redirect']) ){
			$redirect = $data['logout_redirect'];
		}
		//WPML global redirect
		$lang = !empty($_REQUEST['lang']) ? sanitize_text_field($_REQUEST['lang']):'';
		$lang = apply_filters('lwa_lang', $lang);
		if( !empty($lang) ){
			if( isset($data["logout_redirect_".$lang]) ){
				$redirect = $data["logout_redirect_".$lang];
			}
		}
		//Role based redirect
		if( !empty($_REQUEST['redirect']) ){
			$redirect = esc_url_raw($_REQUEST['redirect']);
		}elseif( strtolower(get_class(self::$current_user)) == "wp_user" ){
			//Do a redirect if necessary
			$data = self::$data;
			$user_role = array_shift(self::$current_user->roles); //Checking for role-based redirects
			if( !empty($data["role_logout"]) && is_array($data["role_logout"]) && isset($data["role_logout"][$user_role]) ){
				$redirect = $data["role_logout"][$user_role];
			}
			//Check for language redirects based on roles
			if( !empty($lang) ){
				if( isset($data["role_logout"][$user_role."_".$lang]) ){
					$redirect = $data["role_logout"][$user_role."_".$lang];
				}
			}
		}
		//final replaces
		if( !empty($redirect) ){
			$redirect = str_replace("%LASTURL%", wp_get_raw_referer(), $redirect);
			if( !empty($lang) ){
				$redirect = str_replace("%LANG%", $lang.'/', $redirect);
			}
		}
		return esc_url_raw($redirect);
	}

	public static function loginRedirect( $redirect, $redirect_notsurewhatthisis, $user ){
		$data = self::$data;
		if( is_object($user) ){
			$lwa_redirect = self::getLoginRedirect($user);
			if( $lwa_redirect != '' ){
				$redirect = $lwa_redirect;
			}
		}
		return $redirect;
	}

	public static function getLoginRedirect($user){
		$data = self::$data;
		//Global redirect
		$redirect = false;
		if( !empty($data['login_redirect']) ){
			$redirect = $data["login_redirect"];
		}
		//WPML global redirect
		$lang = !empty($_REQUEST['lang']) ? sanitize_text_field($_REQUEST['lang']):'';
		$lang = apply_filters('lwa_lang', $lang);
		if( !empty($lang) && isset($data["login_redirect_".$lang]) ){
			$redirect = $data["login_redirect_".$lang];
		}
		//Role based redirects
		if( strtolower(get_class($user)) == "wp_user" ){
			$user_role = array_shift($user->roles); //Checking for role-based redirects
			if( isset($data["role_login"][$user_role]) ){
				$redirect = $data["role_login"][$user_role];
			}
			//Check for language redirects based on roles
			if( !empty($lang) && isset($data["role_login"][$user_role."_".$lang]) ){
				$redirect = $data["role_login"][$user_role."_".$lang];
			}
			//Do user string replacements
			$redirect = str_replace('%USERNAME%', $user->user_login, $redirect);
			$redirect = str_replace('%USERNICENAME%', $user->user_nicename, $redirect);
		}
		//Do string replacements
		if( !strstr( wp_get_referer(), wp_login_url()) ){
			$redirect = str_replace("%LASTURL%", wp_get_referer(), $redirect);
		}else{
			$redirect = str_replace('%LASTURL%', admin_url(), $redirect);
		}
		if( !empty($lang) ){
			$redirect = str_replace("%LANG%", $lang.'/', $redirect);
		}
		return esc_url_raw($redirect);
	}

	/*
	 * WIDGET OPERATIONS
	 */

	public static function output( $instance = array() ){
		//Extract widget arguments
		//Merge instance options with global default options
		$lwa = wp_parse_args($instance, self::$data);
		$lwa['id'] = !empty($instance['id']) ? absint($instance['id']) : rand(0, 100000); // for html id fields to be unique
		$lwa['css_vars'] = array();
		$lwa['css_style'] = '';
		//Create HSL CSS for barebones color customization
		if( !empty($lwa['template_color']) ){
			if( !is_array($lwa['template_color']) ){
				$hsl = explode(',', str_replace(' ', '', $lwa['template_color']));
				if( count($hsl) === 3 ) {
					$lwa['template_color']['H'] = $hsl[0];
					$lwa['template_color']['S'] = $hsl[1];
					$lwa['template_color']['L'] = $hsl[2];
				}elseif( preg_match('/^#?[0-9A-Za-z]{3,6}$/', $lwa['template_color']) ){
					// we assume it's hex, convert it to HSL
					require_once('assets/php/color.php');
					try {
						$hsl = \Login_With_AJAX\Color::hexToHsl($lwa['template_color']);
						$lwa['template_color'] = array(
							'H' => round($hsl['H']),
							'S' => round($hsl['S'] * 100),
							'L' => round($hsl['L'] * 100),
						);
					}catch ( Exception $exception ){
						$lwa['template_color'] = self::$data['template_color'];
					}
				}else{
					$lwa['template_color'] = self::$data['template_color'];
				}
			}
			$lwa['css_vars']['accent-hue'] = $lwa['template_color']['H'];
			$lwa['css_vars']['accent-s'] = $lwa['template_color']['S'].'%';
			$lwa['css_vars']['accent-l'] = $lwa['template_color']['L'].'%';
		}
		//Deal with specific variables
		$lwa['profile_link'] = ( !empty($lwa['profile_link']) && $lwa['profile_link'] !== "false" );
		$lwa['avatar_size'] = !empty($lwa['avatar_size']) && is_numeric($lwa['avatar_size']) ? absint($lwa['avatar_size']) : 60;
		// hook here
		$lwa = apply_filters('lwa_output_data', $lwa, $instance);
		//Add template logic
		$templates = static::load_templates();
		$template = ( !empty($lwa['template']) && array_key_exists($lwa['template'], $templates) ) ? $lwa['template']:self::$data['template'];
		$lwa['template'] = static::$template = $template;
		// convert CSS styles to inline
		if( empty($lwa['css_vars']['avatar-size']) ) $lwa['css_vars']['avatar-size'] = $lwa['avatar_size'].'px';
		foreach( $lwa['css_vars'] as $k => $v ){
			$lwa['css_style'] .= '--'.$k.':'. $v.'; ';
		}
		//Choose the widget content to display.
		$show_preview = isset($lwa['v']) && $lwa['v'] && isset($lwa['force_login_display']) && $lwa['force_login_display'];
		if( is_user_logged_in() && !$show_preview ){
			// one more thing...
			if( !empty($lwa['title_loggedin']) ) {
				$lwa['title_loggedin'] = str_replace(array('%username%', '%USERNAME%'), \LoginWithAjax::$current_user->display_name, $lwa['title_loggedin']);
			}
			//Then check for custom templates or theme template default
			$lwa_data = $lwa; // backwards compatibility
			if( !empty($lwa['legacy']) ){
				$template_loc = $templates[$template].'/widget_in.php';
				include ( $template_loc != '' && file_exists($template_loc) ) ? $template_loc : 'templates/legacy-default/widget_in.php';
			}else{
				$template_loc = $templates[$template].'/logged-in.php';
				include ( $template_loc != '' && file_exists($template_loc) ) ? $template_loc : 'templates/default/logged-in.php';
			}
		}else{
		    //quick/easy WPML fix, should eventually go into a seperate file
		    if(  defined('ICL_LANGUAGE_CODE') ){
		        if( !function_exists('lwa_wpml_input_var') ){
                    function lwa_wpml_input_var(){ echo '<input type="hidden" name="lang" id="lang" value="'.esc_attr(ICL_LANGUAGE_CODE).'" />'; }
		        }
		        foreach( array('login_form','lwa_register_form', 'lostpassword_form') as $action ) add_action($action, 'lwa_wpml_input_var');
		    }
			//First check for custom templates or theme template default
			$lwa_data = $lwa; // backwards compatibility
			if( !empty($lwa['legacy']) ){
				$template_loc = $templates[$template].'/widget_out.php';
				include ( $template_loc != '' && file_exists($template_loc) ) ? $template_loc : 'templates/legacy-default/widget_out.php';
			}else{
				$template_loc = $templates[$template].'/login.php';
				include ( $template_loc != '' && file_exists($template_loc) ) ? $template_loc : 'templates/default/login.php';
			}
			//quick/easy WPML fix, should eventually go into a seperate file
			if(  defined('ICL_LANGUAGE_CODE') ){
			    foreach( array('login_form','lwa_register_form', 'lostpassword_form') as $action ) remove_action($action, 'lwa_wpml_input_var');
			}
		}
		if( !empty(self::$data['template']) ) static::$template = self::$data['template'];
	}
	
	public static function get_output( $instance = array() ){
		ob_start();
		static::output($instance);
		return ob_get_clean();
	}
	
	public static function widget( $instance = array() ){
		static::output( $instance );
	}

	public static function shortcode($atts){
		$defaults = array(
			'profile_link' => true,
			'template' => 'default',
			'registration' => true,
			'redirect' => false,
			'redirect_logout' => false,
			'remember' => true,
			'rememberme' => 1,
			'refresh' => true,
		);
		$atts = shortcode_atts($defaults, $atts);
		unset($atts['modal_button_html']); // security precaution
		unset($atts['v']);
		unset($atts['force_login_display']);
		return static::get_output( $atts );
	}

	public static function new_user_notification($user_login, $login_link, $user_email, $blogname){
		//Copied out of /wp-includes/pluggable.php
		$message = self::$data['notification_message'];
		$message = str_replace('%USERNAME%', $user_login, $message);
		$message = str_replace('%PASSWORDURL%', $login_link, $message);
		$message = str_replace('%BLOGNAME%', $blogname, $message);
		$message = str_replace('%BLOGURL%', get_bloginfo('wpurl'), $message);

		$subject = self::$data['notification_subject'];
		$subject = str_replace('%BLOGNAME%', $blogname, $subject);
		$subject = str_replace('%BLOGURL%', get_bloginfo('wpurl'), $subject);

		wp_mail($user_email, $subject, $message);
	}

	/*
	 * Auxillary Functions
	 */
	
	/**
	 * Gets name of currently loaded template.
	 * @return string
	 */
	public static function get_template(){
		return self::$template;
	}
	
	/**
	 * Returns the URL for a relative filepath which would be located in either a child, parent or plugin folder in order of priority.
	 * 
	 * This would search for $template_path within:
	 * /wp-content/themes/your-child-theme/plugins/login-with-ajax/...
	 * /wp-content/themes/your-parent-theme/plugins/login-with-ajax/...
	 * /wp-content/plugin-templates/login-with-ajax/...
	 * /wp-content/plugins/login-with-ajax/templates/...
	 * 
	 * It is assumed that the file always exists within the core plugin folder if the others aren't found.
	 * 
	 * @param string $template_path
	 *
	 * @return string
	 */
	public static function locate_template_url( $template_path ){
	    if( file_exists(get_stylesheet_directory().'/plugins/login-with-ajax/'.$template_path) ){ //Child Theme (or just theme)
	    	return trailingslashit(get_stylesheet_directory_uri())."plugins/login-with-ajax/$template_path";
	    }elseif( file_exists(get_template_directory().'/plugins/login-with-ajax/'.$template_path) ){ //Parent Theme (if parent exists)
	    	return trailingslashit(get_template_directory_uri())."plugins/login-with-ajax/$template_path";
	    }elseif( file_exists(WP_CONTENT_DIR.'/plugin-templates/login-with-ajax/'.$template_path) ){ //login-with-ajax folder in wp-contents
		    return trailingslashit(WP_CONTENT_DIR)."plugin-templates/login-with-ajax/$template_path";
	     
	    }
	    //Default file in plugin folder
	    return trailingslashit(plugin_dir_url(__FILE__))."templates/$template_path";
	}
	
	/**
	 * Returns the directory path for a relative filepath which would be located in either a child, parent or plugin folder in order of priority.
	 *
	 * This would search for $template_path within:
	 * /wp-content/themes/your-child-theme/plugins/login-with-ajax/...
	 * /wp-content/themes/your-parent-theme/plugins/login-with-ajax/...
	 * /wp-content/plugin-templates/login-with-ajax/...
	 *
	 * It is assumed that the file always exists within the core plugin folder if the others aren't found.
	 *
	 * @param string $template_path
	 *
	 * @return string
	 */
	public static function locate_template( $template_path ){
		if( file_exists(get_stylesheet_directory().'/plugins/login-with-ajax/'.$template_path) ){ //Child Theme (or just theme)
			return get_stylesheet_directory()."/plugins/login-with-ajax/$template_path";
		}elseif( file_exists(get_template_directory().'/plugins/login-with-ajax/'.$template_path) ){ //Parent Theme (if parent exists)
			return get_stylesheet_directory()."/plugins/login-with-ajax/$template_path";
		}elseif( file_exists(WP_CONTENT_DIR.'/plugin-templates/login-with-ajax/'.$template_path) ){ //login-with-ajax folder in wp-contents
			return WP_CONTENT_DIR."plugin-templates/login-with-ajax/$template_path";
		}
		//Default file in plugin folder
		return LOGIN_WITH_AJAX_PATH."/templates/$template_path";
	}
	
	/**
	 * Detects whether template file exists in a child or parent theme, false if not. This is used to check if legacy files are still being used.
	 * @param $template_path
	 * @return bool
	 */
	public static function locate_legacy_template( $template_path ){
		$path = '/plugins/login-with-ajax/'.$template_path;
		return file_exists(get_stylesheet_directory().$path) || file_exists(get_template_directory().$path);
	}
	
	public static function get_template_path( $template ){
		static::load_templates();
		if( !empty(static::$templates[$template]) ){
			return static::$templates[$template];
		}
		return false;
	}
	
	public static function get_template_data( $template ){
		if( empty(static::$templates[$template]) ) $template = 'default';
		// get data about a template, we use filters here to provide
		$settings = self::$data;
		if( !empty($settings['legacy']) ){
			$legacy_templates = array('default' => 'Default', 'divs-only' => 'Divs Only', 'modal' => 'Modal');
			$name = !empty($legacy_templates[$template]) ? $legacy_templates[$template] : $template;
			$data = (object) array('label' => sprintf(esc_html__('%s (Legacy)', 'login-with-ajax'), $name), 'path' => static::get_template_path($template) );
		}else{
			$templates = array('default' => 'Default', 'modal' => 'Modal', 'minimalistic' => 'Minimalistic', 'modal-minimalistic' => 'Modal Minimalistic', 'classic' => 'Classic', 'classic-vanilla' => 'Classic Vanilla');
			$name = !empty($templates[$template]) ? $templates[$template] : $template;
			$data = (object) array('label' => $name, 'path' => static::get_template_path($template) );
		}
		$template_data = apply_filters('lwa_get_template_data_'.$template, $data);
		if( $data === $template_data ) $template_data->legacy = true;
		return $template_data;
	}
	
	/**
	 * Searches for templates within the specified directories and loads their data.
	 * This function will first load known templates within the plugin directory, then it will search for templates within the wp-content/plugin-templates/login-with-ajax folder,
	 * and finally in the parent/child theme folders in the plugins/login-with-ajax (if it exists). The last found directory would override the first from plugin > child theme in precedence.
	 * @param boolean $reload If set templates will be reloaded
	 * @return array
	 */
	public static function load_templates($reload = null ){
		if( !empty(static::$templates) && !$reload ) return static::$templates;
		static::$templates = array();
		// we will pre-load a functions.php file to allow pre-loading
		$wp_content_folder = path_join( WP_CONTENT_DIR , "plugin-templates/login-with-ajax/");
		if( is_dir($wp_content_folder) && file_exists($wp_content_folder.'functions.php') ){
			include($wp_content_folder.'functions.php');
		}
		// allow for short-circuiting template search, maybe desirable for a minor performance boost to avoid unecessary template searching
		do_action('lwa_before_get_templates');
		if( !empty(static::$templates) ) return static::$templates;
		//Get Templates from theme and default by checking for folders - we assume a template works if a folder exists!
		//Note that duplicate template names are overwritten in this order of precedence (highest to lowest) - Child Theme > Parent Theme > wp-content folder > Plugin Defaults
		//First are the defaults in the plugin directory, we know these so hard-code found data
		self::find_templates( path_join( WP_PLUGIN_DIR , basename( dirname( __FILE__ ) ). "/templates/") );
		$plugin_templates_folder = path_join( WP_PLUGIN_DIR , basename( dirname( __FILE__ ) ). "/templates/");
		if( !empty(static::$data['legacy']) ) {
			self::$templates = array('default' => $plugin_templates_folder . 'legacy-default', 'modal' => $plugin_templates_folder . 'legacy-modal', 'divs-only' => $plugin_templates_folder . 'legacy-divs-only',);
		}else{
			self::$templates = array('default' => $plugin_templates_folder . 'default', 'modal' => $plugin_templates_folder . 'modal', 'minimalistic' => $plugin_templates_folder . 'minimalistic', 'modal-minimalistic' => $plugin_templates_folder . 'modal-minimalistic', 'classic' => $plugin_templates_folder . 'classic', 'classic-vanilla' => $plugin_templates_folder . 'classic-vanilla');
		}
		// then, add a search for custom folder in wp-contents/plugin-templates/login-with-ajax/ - The new and preferred way if you have themes that get updated and may overwrite custom lwa themes
		self::find_templates($wp_content_folder);
		//Now, the parent theme (if exists)
		if( get_stylesheet_directory() != get_template_directory() ){
			self::find_templates( get_template_directory().'/plugins/login-with-ajax/' );
		}
		//Finally, the child theme
		self::find_templates( get_stylesheet_directory().'/plugins/login-with-ajax/' );
		do_action('lwa_after_get_templates');
		return static::$templates;
	}

	//Checks a directory for folders and populates the template file
	public static function find_templates($dir){
		if (is_dir($dir)) {
		    if ($dh = opendir($dir)) {
				$ignore_files = array('.', '..', '.svn', '.git', '2FA');
		        while (($file = readdir($dh)) !== false) {
		            if ( is_dir($dir . $file) && !in_array($file, $ignore_files) ) {
		            	//Template dir found, add it to the template array
		            	self::$templates[$file] = path_join($dir, $file);
		            }
		        }
		        closedir($dh);
		    }
		}
	}
	
	public static function get_templates_data(){
		$templates_data = array();
		foreach( static::load_templates() as $template => $path ){
			$templates_data[$template] = static::get_template_data( $template );
		}
		return apply_filters('lwa_get_templates_data', $templates_data);
	}
	
	public static function get_color_hsl( $css = false, $default = false ){
		$hsl = array('H' => 220, 'S' => 86, 'L' => 57); // #3372f0
		if( !empty(static::$data['template_color']) && !$default ){
			$hsl = static::$data['template_color'];
		}
		if( $css ){
			return "hsl(".$hsl['H'].", ".$hsl['S']."%, ".$hsl['L']."%)";
		}
		return $hsl;
	}
	
	/**
	 * @param false $default
	 * @return string
	 */
	public static function get_color_hex( $default = false ){
		$hsl = static::get_color_hsl( false, $default );
		try {
			require_once('assets/php/color.php');
			return Login_With_AJAX\Color::hslToHex( array( 'H' => $hsl['H'], 'S' => $hsl['S']/100, 'L' => $hsl['L']/100) );
		} catch ( Exception $e ){
			return '3372f0'; // return the default color
		}
	}
	
	/**
	 * @param $color
	 * @param bool $default
	 * @return Login_With_AJAX\Color
	 */
	public static function get_color( $color = false, $default = false ){
		require_once('assets/php/color.php');
		try {
			return new Login_With_AJAX\Color(static::get_color_hex($default));
		} catch( Exception $ex ){
			return new Login_With_AJAX\Color('3372f0');
		}
	}
	
	/**
	 * Adds a template query param to the provided link and escapes the link. If $template is not provided, then the currently loaded (or default) template is used. If false is provided then query variable will be removed if present in the url.
	 * @param string $url       The url to add template link
	 * @param string $template
	 * @return string
	 */
	public static function template_link( $url, $template = null ){
		if( $template === null ){
			$template = self::$template;
		}elseif( $template === false ){
			$template = null;
		}
		return esc_url(add_query_arg(array('template'=>$template), $url));
	}
	
	/**
	 * Returns a sanitized JSONP response from an array
	 * @param array $array
	 * @return string
	 */
	public static function json_encode($array){
		$return = json_encode($array);
		if( isset($_REQUEST['callback']) && preg_match("/^jQuery[_a-zA-Z0-9]+$/", $_REQUEST['callback']) ){
			$return = $_REQUEST['callback']."($return)";
		}
		return $return;
	}
	
	/**
	 * Triggered by WP's authenticate hook which retriggers an lwa_authenticate filter, allowing LWA add-ons to hook into this and
	 * the possibility to deactivate all authentication triggers in one go (e.g. if a 2FA is successful).
	 *
	 * @param $result
	 * @return mixed|void
	 */
	public static function authenticate( $result ){
		return apply_filters('lwa_authenticate', $result);
	}
	
	/**
	 * Gets the usermeta table row 'lwa' and returns the specific meta key in that array, for easy retrieval.
	 * @param $user_id
	 * @param $meta_key
	 *
	 * @return array|mixed
	 */
	public static function get_user_meta( $user_id, $meta_key, $default = null ) {
		$meta = get_user_meta( $user_id, 'lwa', true );
		if( is_array($meta) && !empty($meta[$meta_key]) ){
			return $meta[$meta_key];
		} elseif( preg_match('/^([a-z_A-Z0-9-]+)\[([a-z_A-Z0-9-]+)\]/', $meta_key, $matches) ) {
			// check for array key
			if( !empty($meta[$matches[1]]) && is_array($meta[$matches[1]]) && isset($meta[$matches[1]][$matches[2]]) ){
				return $meta[$matches[1]][$matches[2]];
			}
		}
		return $default;
	}
	
	/**
	 * Updates the usermeta table row 'lwa' and sets the specific meta key in that array, for easy updating.
	 *
	 * @param $user_id
	 * @param $meta_key
	 * @param $meta_value
	 *
	 * @return bool|int
	 */
	public static function update_user_meta( $user_id, $meta_key, $meta_value ) {
		$meta = get_user_meta( $user_id, 'lwa', true );
		if( !is_array($meta) ) $meta = array();
		if( preg_match('/^([a-z_A-Z0-9-]+)\[([a-z_A-Z0-9-]+)\]/', $meta_key, $matches) ) {
			// check for array key
			if( $meta_value !== null ) {
				if( empty($meta[$matches[1]]) ) {
					$meta[$matches[1]] = array();
				}
				$meta[$matches[1]][$matches[2]] = $meta_value;
			} else {
				unset( $meta[$matches[1]][$matches[2]] );
			}
		} elseif( $meta_value !== null ) {
			$meta[$meta_key] = $meta_value;
		}else {
			unset( $meta[$meta_key] );
		}
		return update_user_meta( $user_id, 'lwa', $meta );
	}
	
	/**
	 * Alters the usermeta table row 'lwa' and deletes the specific meta key in that array, for easy deletion.
	 *
	 * @param $user_id
	 * @param $meta_key
	 *
	 * @return bool|int
	 */
	public static function delete_user_meta( $user_id, $meta_key ) {
		// run update with null, which deletes the key
		return static::update_user_meta( $user_id, $meta_key, null );
	}
}
//Set when to init this class
add_action( 'init', '\LoginWithAjax::init' );
add_action( 'widgets_init', '\LoginWithAjax::widgets_init' );

//Installation and Updates
$lwa_data = get_option('lwa_data');
if( version_compare( get_option('lwa_version',0), LOGIN_WITH_AJAX_VERSION, '<' ) ){
    include_once('lwa-install.php');
}

//Include admin file if needed
if(is_admin()){
	include_once('admin/admin.php');
}

//Include pluggable functions file if user specifies in settings
if( !empty($lwa_data['notification_override']) ){
	include_once('pluggable.php');
}

//Template Tag
function login_with_ajax($atts = ''){
    if( !array($atts) ) $atts = shortcode_parse_atts($atts);
	echo \LoginWithAjax::shortcode($atts);
}