Initial commit. State 04.2021.

This commit is contained in:
2021-04-22 17:57:16 +02:00
commit 82781cca41
2974 changed files with 975656 additions and 0 deletions

View File

@@ -0,0 +1,515 @@
<?php
require_once(CLEF_PATH . 'includes/class.clef-settings.php');
require_once(CLEF_PATH . 'includes/class.clef-invite.php');
class ClefAdmin {
const FORM_ID = "clef";
const CONNECT_CLEF_ID_ACTION = "connect_clef_account_clef_id";
const INVITE_USERS_ACTION = "clef_invite_users";
const DISMISS_WALTZ_ACTION = "clef_dismiss_waltz";
const CONNECT_CLEF_PAGE = "connect_clef_account";
const CLEF_WALTZ_LOGIN_COUNT = 3;
const DASHBOARD_WALTZ_LOGIN_COUNT = 15;
const HIDE_WALTZ_BADGE = 'clef_hide_waltz_badge';
const HIDE_WALTZ_PROMPT = 'clef_hide_waltz_prompt';
const HIDE_USER_SETUP_BADGE = 'clef_hide_user_setup_badge';
private static $instance = null;
private static $affiliates = array('siteground');
protected $settings;
protected function __construct($settings) {
$this->settings = $settings;
$this->initialize_hooks();
if (is_admin()) {
require_once(CLEF_PATH . "/includes/lib/ajax-settings/ajax-settings.php");
$this->ajax_settings = AjaxSettings::start(array(
"options_name" => CLEF_OPTIONS_NAME,
"initialize" => false,
"base_url" => CLEF_URL . "includes/lib/ajax-settings/",
"formSelector" => "#clef-form"
));
}
}
public function initialize_hooks() {
add_action('admin_init', array($this, "setup_plugin"));
add_action('admin_init', array($this, "settings_form"));
add_action('admin_init', array($this, "multisite_settings_edit"));
// Display the badge message, if appropriate
add_action('admin_init', array($this, 'clef_hook_onboarding'));
add_action('clef_hook_admin_menu', array($this, "hook_admin_menu"));
add_filter('clef_add_affiliate', array($this, "add_affiliates"));
add_action('admin_enqueue_scripts', array($this, "admin_enqueue_scripts"));
add_action('admin_notices', array($this, 'display_messages') );
add_action('admin_notices', array($this, 'display_clef_waltz_prompt'));
add_action('admin_notices', array($this, 'display_dashboard_waltz_prompt'));
add_action('clef_onboarding_first_login', array($this, 'disable_passwords_for_clef_users'));
add_filter( 'plugin_action_links_'.plugin_basename( CLEF_PATH.'wpclef.php' ), array($this, 'clef_settings_action_links' ) );
global $clef_ajax;
$clef_ajax->add_action(self::CONNECT_CLEF_ID_ACTION, array($this, 'ajax_connect_clef_account_with_clef_id'));
$clef_ajax->add_action(self::INVITE_USERS_ACTION, array($this, 'ajax_invite_users'));
$clef_ajax->add_action(
self::DISMISS_WALTZ_ACTION,
array($this, 'ajax_dismiss_waltz_notification'),
array('capability' => 'read')
);
}
public function clef_hook_onboarding() {
do_action('clef_hook_onboarding');
}
private function render_waltz_prompt($class="") {
echo ClefUtils::render_template('admin/waltz-prompt.tpl', array(
'next_href' => '#',
'next_text' => __('Hide this message', 'clef'),
'class' => $class
));
}
public function display_dashboard_waltz_prompt() {
$onboarding = ClefOnboarding::start($this->settings);
$login_count = $onboarding->get_login_count_for_current_user();
$is_settings_page = ClefUtils::isset_GET('page') == $this->settings->settings_path;
$hide_waltz_prompt = get_user_meta(get_current_user_id(), self::HIDE_WALTZ_PROMPT, true);
// If the user has access to the dashboard and they haven't already
// dismissed the prompt, then display it.
$should_display_for_user = !$hide_waltz_prompt && current_user_can('read');
if ($login_count < self::DASHBOARD_WALTZ_LOGIN_COUNT || !$should_display_for_user || $is_settings_page) return;
$this->render_waltz_prompt("settings waltz-notification");
// Make sure the notification doesn't ever show again for this user
update_user_meta(get_current_user_id(), self::HIDE_WALTZ_PROMPT, true);
}
public function display_clef_waltz_prompt() {
$is_google_chrome = strpos($_SERVER['HTTP_USER_AGENT'], 'Chrome') !== false;
$is_settings_page = ClefUtils::isset_GET('page') == $this->settings->settings_path;
$should_hide = get_user_meta(get_current_user_id(), self::HIDE_WALTZ_PROMPT, true);
$onboarding = ClefOnboarding::start($this->settings);
$login_count = $onboarding->get_login_count_for_current_user();
if (!$is_google_chrome || !$is_settings_page || $should_hide || $login_count < self::CLEF_WALTZ_LOGIN_COUNT) return;
$this->render_waltz_prompt("settings waltz-notification");
}
public function hook_admin_menu() {
add_action('admin_menu', array($this, 'admin_menu'));
}
public function display_messages() {
settings_errors( CLEF_OPTIONS_NAME );
}
public function admin_enqueue_scripts($hook) {
$exploded_path = explode('/', $hook);
$settings_page_name = array_shift($exploded_path);
// only register clef logout if user is a clef user
if (ClefUtils::user_has_clef()) {
$ident = ClefUtils::register_script('clef_heartbeat');
wp_enqueue_script($ident);
}
$ident = ClefUtils::register_script('waltz_notification', array('jquery'));
wp_enqueue_script($ident);
$ident = ClefUtils::register_style('admin');
wp_enqueue_style($ident);
if(preg_match("/".$this->settings->settings_path."/", $settings_page_name)) {
wp_enqueue_media();
$ident = ClefUtils::register_script(
'settings',
array('jquery', 'backbone', 'underscore', $this->ajax_settings->identifier())
);
wp_enqueue_script($ident);
}
}
/**
* @return array Users filtered by >= $role
*/
protected function filter_users_by_role($users, $role) {
$filtered_users = array();
if ($role === 'everyone') {
$filtered_users = $users;
}
else {
foreach ($users as $user) {
if (ClefUtils::user_fulfills_role($user, $role)) {
$filtered_users[] = $user;
}
}
}
return $filtered_users;
}
public function admin_menu() {
// Ensure that if the Waltz notification bubble was showing, that it is
// never shown again.
if (ClefUtils::isset_REQUEST('page') === $this->settings->connect_path &&
$this->get_menu_badge() === _('add security')) {
update_user_meta(get_current_user_id(), self::HIDE_USER_SETUP_BADGE, true);
}
if ($this->settings->multisite_disallow_settings_override()) {
if ($this->settings->is_configured()) {
// if the single site override of settings is not allowed
// let's add a menu page that only lets a user connect
// their clef account
add_menu_page(
__("Clef", 'clef'),
__("Clef", 'clef'),
"read",
$this->settings->settings_path,
array($this, 'render_clef_user_settings'),
CLEF_URL . 'assets/dist/img/gradient_icon_16.png'
);
}
return;
}
$clef_menu_title = $this->get_clef_menu_title();
$menu_name = $this->settings->settings_path;
add_menu_page(
__("Clef", 'clef'),
$clef_menu_title,
"manage_options",
$menu_name,
array($this, 'general_settings'),
CLEF_URL . 'assets/dist/img/gradient_icon_16.png'
);
if ($this->settings->is_configured()) {
if (ClefUtils::user_has_clef()) $name = __('Disconnect Clef account', 'clef');
else $name = __('Connect Clef account', 'clef');
add_submenu_page(
$menu_name,
$name,
$name,
'read',
self::CONNECT_CLEF_PAGE,
array($this, 'render_clef_user_settings')
);
}
}
public function clef_settings_action_links( $links ) {
array_unshift( $links, '<a href="' . add_query_arg( array( 'page' => $this->settings->settings_path ), admin_url( 'admin.php' ) ) . '">' . __( 'Settings' ) . '</a>' );
return $links;
}
/**
* Determines whether to badge the Clef menu icon.
*
* @return string The title of the menu with or without a badge
*/
public function get_clef_menu_title() {
$clef_menu_title = __('Clef', 'clef');
if ($badge = $this->get_menu_badge()) {
$clef_menu_title .= $this->render_badge($badge);
}
return $clef_menu_title;
}
public function get_menu_badge() {
$user_is_admin = current_user_can('manage_options');
$needs_setup_badge = ($user_is_admin && !$this->settings->is_configured());
if ($needs_setup_badge) return _('needs setup');
$has_seen_user_setup_badge = get_user_meta(get_current_user_id(), self::HIDE_USER_SETUP_BADGE, true);
$needs_connect_badge = !$user_is_admin && $this->settings->is_configured() && !ClefUtils::user_has_clef() && !$has_seen_user_setup_badge;
if ($needs_connect_badge) return _('add security');
return false;
}
public function render_clef_user_settings() {
do_action('clef_render_user_settings');
}
public function render_badge($count) {
return " <span class='update-plugins count-1'><span class='update-count'>" . $count . "</span></span>";
}
public function general_settings($options = false) {
$form = ClefSettings::forID(self::FORM_ID, CLEF_OPTIONS_NAME, $this->settings);
if (!$options) {
$options = $this->settings->get_site_option();
}
$options = array_merge(array(
'setup' => array(
'siteName' => get_option('blogname'),
'siteDomain' => get_option('siteurl'),
'logoutHook' => home_url('/'),
'source' => 'wordpress',
'affiliates' => apply_filters('clef_add_affiliate', array())
),
'nonces' => array(
'connectClef' => wp_create_nonce(self::CONNECT_CLEF_ID_ACTION),
'inviteUsers' => wp_create_nonce(self::INVITE_USERS_ACTION),
'getProServices' => wp_create_nonce(ClefPro::GET_PRO_SERVICES_ACTION)
),
'configured' => $this->settings->is_configured(),
'clefBase' => CLEF_BASE,
'optionsName' => CLEF_OPTIONS_NAME,
'settingsPath' => $this->settings->settings_path,
'isMultisite' => is_multisite(),
'isNetworkSettings' => false,
'isNetworkSettingsEnabled' => $this->settings->network_settings_enabled(),
'isSingleSiteSettingsAllowed' => $this->settings->single_site_settings_allowed(),
'isUsingIndividualSettings' => $this->settings->use_individual_settings,
'connectClefUrl' => admin_url('admin.php?page=' . ClefAdmin::CONNECT_CLEF_PAGE)
), $options);
echo ClefUtils::render_template('admin/settings.tpl', array(
"form" => $form,
"options" => $options
));
}
public function add_affiliates($affiliates) {
if (get_site_option("bruteprotect_installed_clef")) {
array_push($affiliates, "bruteprotect");
}
$theme = wp_get_theme();
if ($theme && strtolower($theme->name) == "responsive") {
array_push($affiliates, "responsive");
}
$saved_affiliates = $this->settings->get_saved_affiliates();
if ($saved_affiliates && count($saved_affiliates) > 0) {
$affiliates = array_unique(array_merge($affiliates, $saved_affiliates));
}
return $affiliates;
}
public function multisite_settings() {
echo ClefUtils::render_template('admin/multisite-disabled.tpl');
}
public function settings_form() {
$form = ClefSettings::forID(self::FORM_ID, CLEF_OPTIONS_NAME, $this->settings);
$settings = $form->addSection('clef_settings', __('API Settings', 'clef'));
$settings->addField('app_id', __('Application ID', "clef"), Settings_API_Util_Field::TYPE_TEXTFIELD);
$settings->addField('app_secret', __('Application Secret', "clef"), Settings_API_Util_Field::TYPE_TEXTFIELD);
$settings->addField('register', __('Register with Clef', 'clef'), Settings_API_Util_Field::TYPE_CHECKBOX);
$pw_settings = $form->addSection('clef_password_settings', __('Password Settings', 'clef'), '');
$pw_settings->addField('disable_passwords', __('Disable passwords for Clef users', "clef"), Settings_API_Util_Field::TYPE_CHECKBOX);
$pw_settings->addField(
'disable_certain_passwords',
__('Disable certain passwords', "clef"),
Settings_API_Util_Field::TYPE_SELECT,
"Disabled",
array( "options" => array_merge(array(""), ClefUtils::$default_roles))
);
$custom_roles = ClefUtils::get_custom_roles();
if (count($custom_roles) > 0) {
$pw_settings->custom_roles = $custom_roles;
foreach ($custom_roles as $role => $role_obj) {
$pw_settings->addField(
"disable_passwords_custom_role_$role",
$role_obj['name'],
Settings_API_Util_Field::TYPE_CHECKBOX
);
}
}
$pw_settings->addField('force', __('Disable all passwords', "clef"), Settings_API_Util_Field::TYPE_CHECKBOX);
$pw_settings->addField(
'xml_allowed',
__('Allow XML', 'clef'),
Settings_API_Util_Field::TYPE_CHECKBOX
);
$form_settings = $form->addSection('clef_form_settings', __('Form settings', 'clef'), '');
$form_settings->addField('embed_clef', __('Embed Clef wave in the login form', 'clef'), Settings_API_Util_Field::TYPE_CHECKBOX);
$override_settings = $form->addSection('clef_override_settings', __('Override Settings', 'clef'));
$override_settings->addField('key', __("Override key", "clef"), Settings_API_Util_Field::TYPE_TEXTFIELD);
$support_clef_settings = $form->addSection('support_clef', __('Support Clef', "clef"));
$support_clef_settings->addField(
'badge',
__("Support Clef by automatically adding a link!", "clef"),
Settings_API_Util_Field::TYPE_SELECT,
"disabled",
array("options" => array(array(__("Badge", "clef"), "badge") , array(__("Link", "clef"), "link"), array(__("Disabled", "clef"), "disabled")))
);
$invite_users_settings = $form->addSection('invite_users', __('Invite Users', "clef"));
$pro = ClefPro::start();
$pro->add_settings($form);
return $form;
}
public function multisite_settings_edit() {
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
ClefUtils::isset_GET('page') == 'clef' &&
ClefUtils::isset_GET('action') == 'clef_multisite' &&
!is_network_admin()
) {
if (!wp_verify_nonce($_POST['_wpnonce'], 'clef_multisite')) {
die(__("Security check; nonce failed.", "clef"));
}
$override = get_option(ClefInternalSettings::MS_OVERRIDE_OPTION);
if (!add_option(ClefInternalSettings::MS_OVERRIDE_OPTION, !$override)) {
update_option(ClefInternalSettings::MS_OVERRIDE_OPTION, !$override);
}
wp_redirect(add_query_arg(array('page' => $this->settings->settings_path, 'updated' => 'true'), admin_url('admin.php')));
exit();
}
}
public function setup_plugin() {
if (is_admin() && get_option("Clef_Activated")) {
delete_option("Clef_Activated");
wp_redirect(add_query_arg(array('page' => $this->settings->settings_path), admin_url('options.php')));
exit();
}
}
public function affiliate_check() {
$saved_affiliates = $this->settings->get_saved_affiliates();
foreach (self::$affiliates as $affiliate) {
if (in_array($affiliate, $saved_affiliates)) return true;
}
return false;
}
public function disable_passwords_for_clef_users() {
$this->settings->disable_passwords_for_clef_users();
}
/**** BEGIN AJAX HANDLERS ******/
public function ajax_dismiss_waltz_notification() {
update_user_meta(get_current_user_id(), self::HIDE_WALTZ_PROMPT, true);
return array('success' => true);
}
public function ajax_invite_users() {
$role = strtolower(ClefUtils::isset_POST('roles'));
$is_network_admin = filter_var(ClefUtils::isset_POST('networkAdmin'), FILTER_VALIDATE_BOOLEAN);
if (!$role) {
return new WP_Error('invalid_role', __('invalid role', 'clef'));
}
$opts = array(
'exclude' => array(get_current_user_id()),
'meta_query' => array(array(
'key' => 'clef_id',
'value' => '',
'compare' => 'NOT EXISTS'
))
);
# if we are on the network admin page, don't filter users by
# blog ID.
if ($is_network_admin) $opts['blog_id'] = false;
$other_users = get_users($opts);
$filtered_users = $this->filter_users_by_role($other_users, $role);
if (empty($filtered_users)) {
return new WP_Error('no_users', __("there are no other users without Clef with this role or greater", "clef"));
}
// Get the site domain and get rid of www.
$sitename = strtolower( $_SERVER['SERVER_NAME'] );
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}
$from_email = 'wordpress@' . $sitename;
$errors = array();
foreach ($filtered_users as &$user) {
$invite = new ClefInvite($user, $is_network_admin);
$invite->persist();
$success = $invite->send_email($from_email);
if (!$success) {
$errors[] = $user->user_email;
}
}
if (count($errors) > 0) {
if (count($errors) == count($filtered_users)) {
$message = __("there was an error sending the invite email to all users. Copy and paste the preview email to your users and they'll be walked through a tutorial to connect with Clef", 'clef');
} else {
$message = __("unable to send emails to the following users: ", 'clef');
$message .= join(", ", $errors);
$message .= __(". Copy and paste the preview email to your users and they'll be walked through a tutorial to connect with Clef", 'clef');
}
return new WP_Error('clef_mail_error', $message);
} else {
return array("success" => true);
}
}
public function ajax_connect_clef_account_with_clef_id() {
if (!ClefUtils::isset_POST('identifier')) {
return new WP_Error("invalid_clef_id", __("invalid Clef ID", "clef"));
}
$result = ClefUtils::associate_clef_id($_POST["identifier"]);
if (is_wp_error($result)) {
return $result;
} else {
$session = ClefSession::start();
$session->set('logged_in_at', time());
return array("success" => true);
}
}
/**** END AJAX HANDLERS ******/
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}

View File

@@ -0,0 +1,89 @@
<?php
class ClefAjax {
private $settings;
private static $instance = null;
private $hook_map = array();
private $defaults = array(
"nonce" => true,
"capability" => "manage_options",
"priority" => 10,
"accepted_args" => 1
);
private function __construct($settings) {
$this->settings = $settings;
}
public function add_action($action, $function_to_add, $opts = array()) {
$opts = array_merge($this->defaults, $opts);
$this->hook_map[$action] = array(
"function" => $function_to_add,
"options" => $opts
);
add_action(
'wp_ajax_' . $action,
array($this, "handle_ajax_request"),
$opts['priority'],
$opts['accepted_args']
);
}
public function handle_ajax_request() {
$action = $_REQUEST['action'];
$hook_info = $this->hook_map[$action];
$options = $hook_info['options'];
if(isset($_SERVER["CONTENT_TYPE"]) && 0 === strpos($_SERVER["CONTENT_TYPE"], 'application/json')) {
global $HTTP_RAW_POST_DATA;
if (!isset($HTTP_RAW_POST_DATA)) {
$HTTP_RAW_POST_DATA = file_get_contents( "php://input" );
}
$data = json_decode($HTTP_RAW_POST_DATA, true);
# if the request is coming from backbone, we need to non-200 error
$send_non_200_error = true;
} else {
$data = $_REQUEST;
$send_non_200_error = false;
}
if ($options['nonce'] && (!isset($data['_wpnonce']) || !wp_verify_nonce($data['_wpnonce'], $action))) {
$this->send_json_error(
array("error" => __("invalid nonce", "clef")),
$send_non_200_error
);
}
if (!current_user_can($options['capability'])) {
$this->send_json_error(
array("error" => __("user does not have correct capabilities", "clef")),
$send_non_200_error
);
}
$response = call_user_func($hook_info['function']);
if (is_wp_error($response)) {
$this->send_json_error(
array("error" => $response->get_error_message()),
$send_non_200_error
);
} else {
wp_send_json($response);
}
}
public static function send_json_error($data, $send_non_200) {
if ($send_non_200) header('HTTP/1.0 400');
wp_send_json_error($data);
}
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}

View File

@@ -0,0 +1,95 @@
<?php
class ClefBadge {
const SETTING_NAME = "support_clef_badge";
const PROMPT_HIDDEN = "badge_prompt_hidden";
private static $instance = null;
private $settings;
private $onboarding;
private function __construct($settings, $onboarding) {
$this->settings = $settings;
$this->onboarding = $onboarding;
add_action('clef_hook_onboarding', array($this, 'hook_onboarding'));
}
public function is_active() {
$setting = $this->settings->get(self::SETTING_NAME);
return $setting != "" && $setting != "disabled";
}
public function should_display_prompt() {
if ($this->is_active()) return false;
$login_count = $this->onboarding->get_login_count_for_current_user();
$prompt_hidden = $this->onboarding->get_key(self::PROMPT_HIDDEN);
$has_admin_capability = current_user_can('manage_options');
return $login_count > 0 && !$prompt_hidden && $has_admin_capability;
}
public function hook_onboarding() {
if (empty($_POST)) {
if ($this->should_display_prompt()) {
add_action('admin_enqueue_scripts', array($this, 'register_scripts'));
add_action('admin_notices', array($this, 'badge_prompt_html'));
}
} else {
global $clef_ajax;
$clef_ajax->add_action('clef_badge_prompt', array($this, 'handle_badge_prompt_ajax'));
}
}
public function hook_display() {
if (!$this->is_active()) return;
add_action('wp_footer', array($this, 'draw'));
}
public function draw() {
$pretty = $this->settings->get(self::SETTING_NAME) == "badge";
echo ClefUtils::render_template('badge.tpl', array("pretty" => $pretty));
}
public function badge_prompt_html() {
if (!$this->should_display_prompt()) return;
$had_clef_before_onboarding = $this->onboarding->had_clef_before_onboarding();
echo ClefUtils::render_template('admin/badge-prompt.tpl', array(
"had_clef_before_onboarding" => $had_clef_before_onboarding
));
// Ensure the prompt is hidden on next load
$this->hide_prompt();
}
public function register_scripts() {
$ident = ClefUtils::register_script('badge');
wp_enqueue_script($ident);
}
public function handle_badge_prompt_ajax() {
if (isset($_POST['enable'])) {
$this->settings->set(self::SETTING_NAME, $_POST['enable']);
}
$this->hide_prompt();
return array( "success" => true );
}
/**
* Mark settings so that the badge prompt will be hidden on next page load.
*/
public function hide_prompt() {
$this->onboarding->set_key(self::PROMPT_HIDDEN, true);
}
public static function start($settings, $onboarding) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings, $onboarding);
}
return self::$instance;
}
}

View File

@@ -0,0 +1,164 @@
<?php
class LoginException extends Exception {}
class ClefStateException extends Exception {}
class ClefCore {
private static $instance = null;
private $settings;
private $badge;
private $onboarding;
private function __construct() {
// General utility functions
require_once(CLEF_PATH . 'includes/lib/utils.inc');
require_once(CLEF_PATH . 'includes/class.clef-utils.php');
require_once(CLEF_PATH . 'includes/class.clef-translation.php');
require_once(CLEF_PATH . 'includes/class.clef-session.php');
// Site options
require_once(CLEF_PATH . 'includes/class.clef-internal-settings.php');
$settings = ClefInternalSettings::start();
global $clef_ajax;
require_once(CLEF_PATH . 'includes/class.clef-ajax.php');
$clef_ajax = ClefAjax::start($settings);
// Onboarding settings
require_once(CLEF_PATH . 'includes/class.clef-onboarding.php');
$onboarding = ClefOnboarding::start($settings);
require_once(CLEF_PATH. 'includes/class.clef-user-settings.php');
$user_settings = ClefUserSettings::start($settings);
// Clef login functions
require_once(CLEF_PATH . 'includes/class.clef-login.php');
$login = ClefLogin::start($settings);
// Clef logout hook functions
require_once(CLEF_PATH . 'includes/class.clef-logout.php');
$logout = ClefLogout::start($settings);
// Badge display options
require_once(CLEF_PATH . 'includes/class.clef-badge.php');
$badge = ClefBadge::start($settings, $onboarding);
$badge->hook_display();
// Admin functions and hooks
require_once(CLEF_PATH . 'includes/class.clef-admin.php');
$admin = ClefAdmin::start($settings);
require_once(CLEF_PATH . 'includes/class.clef-network-admin.php');
$network_admin = ClefNetworkAdmin::start($settings);
require_once(CLEF_PATH . 'includes/pro/class.clef-pro.php');
$pro = ClefPro::start($settings);
// Plugin setup hooks
require_once(CLEF_PATH . 'includes/class.clef-setup.php');
$this->settings = $settings;
$this->badge = $badge;
$this->onboarding = $onboarding;
// Register public hooks
if ($admin) {
add_action('clef_render_settings', array($admin, 'general_settings'));
}
add_action('clef_plugin_uninstall', array('ClefSetup', 'uninstall_plugin'));
add_action('clef_plugin_updated', array($this, 'plugin_updated'), 10, 2);
add_action('wp_enqueue_scripts', array($this, 'load_base_styles'));
// Run migrations and other hooks upon plugin update
$old_version = $settings->get('version');
$current_version = CLEF_VERSION;
if (!$old_version || $current_version != $old_version) {
do_action('clef_plugin_updated', $current_version, $old_version);
}
if (CLEF_IS_BASE_PLUGIN) {
do_action('clef_hook_admin_menu');
}
}
public function load_base_styles() {
$ident = ClefUtils::register_style('main');
wp_enqueue_style($ident);
}
public function plugin_updated($version, $previous_version) {
$settings_changes = false;
if ($previous_version) {
if (version_compare($previous_version, '2.2.9.1', '<')) {
$this->onboarding->set_first_login_true();
}
if (version_compare($previous_version, '2.1', '<')) {
if (!session_id()) @session_start();
if (isset($_SESSION['logged_in_at'])) {
$session = ClefSession::start();
$session->set('logged_in_at', $_SESSION['logged_in_at']);
}
}
if (version_compare($previous_version, '2.0', '<')) {
$this->onboarding->migrate_global_login_count();
$this->badge->hide_prompt();
if ($this->settings->get('clef_password_settings_disable_certain_passwords') == "Disabled") {
$this->settings->set('clef_password_settings_disable_certain_passwords', '');
}
}
if (version_compare($previous_version, "1.9.1.1", '<')) {
$this->badge->hide_prompt();
}
if (version_compare($previous_version, "1.9", '<')) {
if (!$previous_version) {
$previous_version = $version;
}
$this->settings->get('installed_at', $previous_version);
}
if (version_compare($previous_version, "1.8.0", '<')) {
$settings_changes = array(
"clef_password_settings_override_key" => "clef_override_settings_key"
);
}
} else {
$this->settings->set('installed_at', $version);
$this->settings->set('clef_form_settings_embed_clef', 1);
$this->settings->set_saved_affiliates();
}
if ($settings_changes) {
foreach ($settings_changes as $old_name => $new_name) {
$value = $this->settings->get($old_name);
if ($value) {
$this->settings->set($new_name, $value);
$this->settings->remove($old_name);
}
}
}
$this->settings->set("version", $version);
}
public static function manage_wp_fix() {
if (isset($_REQUEST['action']) && preg_match('/ajax_settings/', $_REQUEST['action']) && function_exists('mmb_authenticate')) {
remove_action('plugins_loaded', 'mmb_authenticate', 1);
}
}
public static function start() {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self;
}
return self::$instance;
}
}

View File

@@ -0,0 +1,221 @@
<?php
class ClefInternalSettings {
const MS_ENABLED_OPTION = "clef_multisite_enabled";
const MS_ALLOW_OVERRIDE_OPTION = 'clef_multsite_allow_override';
const MS_OVERRIDE_OPTION = 'clef_multisite_override';
private static $instance = null;
public $use_individual_settings;
public $settings_path;
private $settings;
private function __construct() {
$this->use_individual_settings = $this->check_individual_settings();
$this->settings = $this->get_site_option();
$this->settings_path = 'clef';
$this->connect_path = 'connect_clef_account';
add_action('admin_menu', array($this, 'apply_settings_path_filter'), 11);
add_filter('ajax_settings_pre_save', array($this, 'merge_settings'));
}
public function merge_settings($to_be_saved) {
return array_merge($this->settings, $to_be_saved);
}
public function apply_settings_path_filter() {
$this->settings_path = apply_filters('clef_settings_path', $this->settings_path);
}
private function check_individual_settings() {
if (!$this->is_multisite_enabled()) return true;
$override = false;
if (get_site_option(self::MS_ALLOW_OVERRIDE_OPTION)) {
$override = get_option(self::MS_OVERRIDE_OPTION, 'undefined');
// check to see whether the override is set (it would not be set
// if the blog had previously been used without multisite
// enabled). sets it if it is null.
if ($override == "undefined") {
$override = !!get_option(CLEF_OPTIONS_NAME);
add_option(self::MS_OVERRIDE_OPTION, $override);
}
}
return $override && !is_network_admin();
}
public function get($name) {
return isset($this->settings[$name]) ? $this->settings[$name] : null;
}
public function set($name, $value) {
$sanitized_value = $this->maybe_sanitize($value);
if ($this->get($name) !== $sanitized_value) {
$this->settings[$name] = $sanitized_value;
$this->update_site_option();
}
}
public function maybe_sanitize($value) {
$sanitized_value = $value;
if (is_string($value)) {
$sanitized_value = sanitize_text_field($value);
}
return $sanitized_value;
}
public function remove($name) {
$value = $this->get($name);
if ($value) {
unset($this->settings[$name]);
$this->update_site_option();
}
return $value;
}
public function get_site_option() {
$getter = $this->use_individual_settings ? 'get_option' : 'get_site_option';
return $getter(CLEF_OPTIONS_NAME);
}
public function update_site_option() {
$setter = $this->use_individual_settings ? 'update_option' : 'update_site_option';
return $setter(CLEF_OPTIONS_NAME, $this->settings);
}
/**
* Returns whether Clef is activated network-wide and whether it has
* been enabled on the whole network.
*
* @return bool
*/
public function is_multisite_enabled() {
return is_plugin_active_for_network('wpclef/wpclef.php') &&
get_site_option(self::MS_ENABLED_OPTION);
}
/**
* Returns whether passwords are disabled site-wide.
*
* @return bool
*/
public function passwords_disabled() {
return $this->get('clef_password_settings_disable_passwords')
|| $this->get('clef_password_settings_force')
|| $this->get('clef_password_settings_disable_certain_passwords') != "";
}
/**
* Returns whether passwords are disabled for a specific user based on
* user roles.
*
* @param WP_User $user
* @return bool
*/
public function passwords_are_disabled_for_user($user) {
if (!$this->is_configured()) return false;
if ($this->get('clef_password_settings_force')) {
return true;
}
if ($this->get( 'clef_password_settings_disable_passwords' ) && ClefUtils::user_has_clef($user)) {
return true;
}
$disable_certain_passwords =
$this->get( 'clef_password_settings_disable_certain_passwords');
if ($disable_certain_passwords && $disable_certain_passwords != "") {
$max_role = strtolower($disable_certain_passwords);
if (ClefUtils::user_fulfills_role($user, $max_role)) return true;
}
$potential_custom_user_roles = (array) $user->roles;
foreach ($potential_custom_user_roles as $role) {
if ($this->get("clef_password_settings_disable_passwords_custom_role_$role")) return true;
}
return false;
}
public function xml_passwords_enabled() {
return !$this->passwords_disabled() || $this->get('clef_password_settings_xml_allowed');
}
public function is_configured() {
$app_id = $this->get('clef_settings_app_id');
$app_secret = $this->get('clef_settings_app_secret');
return $app_id && $app_secret && !empty($app_id) && !empty($app_secret);
}
public function multisite_disallow_settings_override() {
return $this->is_multisite_enabled() && !get_site_option(self::MS_ALLOW_OVERRIDE_OPTION);
}
public function network_settings_enabled() {
return !!get_site_option(self::MS_ENABLED_OPTION);
}
public function single_site_settings_allowed() {
return !!get_site_option(self::MS_ALLOW_OVERRIDE_OPTION);
}
public function registration_with_clef_is_allowed() {
return !!$this->get('clef_settings_register');
}
public function should_embed_clef_login() {
return $this->get('clef_form_settings_embed_clef');
}
public function set_saved_affiliates() {
$affiliate_file_path = trailingslashit(CLEF_PATH) . "affiliates";
$affiliates = false;
if (file_exists($affiliate_file_path) && $affiliate_file = fopen($affiliate_file_path, "r")) {
$affiliates = fgets($affiliate_file);
fclose($affiliate_file);
if (strlen($affiliates) > 0) {
$this->set('affiliates', $affiliates);
return $affiliates;
}
}
return false;
}
public function get_saved_affiliates() {
$saved_affiliates = $this->get('affiliates');
if (is_null($saved_affiliates)) $saved_affiliates = $this->set_saved_affiliates();
if ($saved_affiliates) {
return array_map('trim', explode(',', $saved_affiliates));
} else {
return array();
}
}
public function disable_passwords_for_clef_users() {
return $this->set('clef_password_settings_disable_passwords', 1);
}
public static function start() {
if (!isset(self::$instance) || self::$instance === null || defined('CLEF_TESTING')) {
self::$instance = new self;
}
return self::$instance;
}
public static function get_instance() {
return self::start();
}
}
?>

View File

@@ -0,0 +1,60 @@
<?php
class ClefInvite {
public $code;
public $created_at;
private $user_email;
function __construct($user, $is_network_admin=false) {
$this->code = md5(uniqid(mt_rand(), true));
$this->user_email = $user->user_email;
$this->user_id = $user->ID;
$this->created_at = time();
$sites = get_blogs_of_user($user->ID);
if ($is_network_admin || count($sites) == 0) {
$site = array_shift($sites);
switch_to_blog($site->userblog_id);
$this->site_name = get_bloginfo('name');
$this->login_url = wp_login_url();
restore_current_blog();
} else {
$this->site_name = get_bloginfo('name');
$this->login_url = wp_login_url();
}
}
function persist() {
update_user_meta($this->user_id, 'clef_invite_code', $this);
}
function get_link() {
return ($this->login_url .
'?clef_invite_code=' . $this->code .
'&clef_invite_id=' . urlencode(base64_encode($this->user_email)));
}
function send_email($from_email) {
if (empty($this->user_email)) return true;
$invite_link = $this->get_link();
$subject = '['. $this->site_name . '] ' . __('Set up Clef for your account', "clef");
$message = ClefUtils::render_template(
'invite_email.tpl',
array(
"invite_link" => $this->get_link(),
"site_name" => $this->site_name
),
false
);
$headers = "From: WordPress <".$from_email."> \r\n";
add_filter('wp_mail_content_type', array('ClefUtils', 'set_html_content_type'));
$sent = wp_mail($this->user_email, $subject, $message, $headers);
remove_filter('wp_mail_content_type', array('ClefUtils', 'set_html_content_type'));
return $sent;
}
}
?>

View File

@@ -0,0 +1,443 @@
<?php
require_once(CLEF_PATH . 'includes/class.clef-invite.php');
class ClefLogin {
private static $instance = null;
private $settings;
private function __construct($settings) {
$this->settings = $settings;
$this->initialize_hooks();
}
public function initialize_hooks() {
add_action('init', array($this, 'initialize_state'));
// Authenticate with Clef is there is a valid OAuth code present
add_action('authenticate', array($this, 'authenticate_clef'), 10, 3);
// Disable password authentication according to settings
add_action('wp_authenticate_user', array($this, 'disable_passwords'));
// Clear logout hook if the user is logging in again
add_filter('wp_authenticate_user', array($this, 'clear_logout_hook'));
// Connect Clef account if logging in with a clef ID
add_filter('wp_authenticate_user', array($this, 'connect_clef_account_on_login'));
// Check if account was connected on login and if so flash a success message
add_action('admin_notices', array($this, 'display_connect_clef_account_success'));
// adds classes which hide login form if appropriate
add_action('login_body_class', array($this, 'add_login_form_classes' ));
// Render the login form with the Clef button no matter what
add_action('login_form', array( $this, 'login_form' ) );
add_action('register_form', array( $this, 'register_form') );
add_filter('wp_login_failed', array($this, 'handle_login_failed'));
add_action('login_enqueue_scripts', array($this, 'load_base_styles'));
// Show an error message if there is an invite code but it is invalid
add_filter('login_message', array($this, 'validate_invite'));
// Disable password reset, according to settings
add_filter('allow_password_reset', array($this, 'disable_password_reset'), 10, 2);
// Redirect to an Clef onboarding page if a user logs in with an invite
// code
add_filter('login_redirect', array($this, 'redirect_if_invite_code'), 10, 3);
// Allow the Clef button to be rendered anywhere
add_action('clef_render_login_button', array($this, 'render_login_button'), 10, 4);
add_shortcode('clef_render_login_button', array($this, 'shortcode_render_login_button'));
if (defined('MEMBERSHIP_MASTER_ADMIN') && defined('MEMBERSHIP_SETACTIVATORAS_ADMIN')) {
add_action('signup_hidden_fields', array($this, 'add_clef_login_button_to_wpmu'));
add_action('bp_before_account_details_fields', array($this, 'add_clef_login_button_to_wpmu'));
add_action('membership_popover_extend_registration_form', array($this, 'add_clef_login_button_to_wpmu'));
add_action('signup_extra_fields', array($this, 'add_clef_login_button_to_wpmu'));
add_action('membership_popover_extend_login_form', array($this, 'add_clef_login_button_to_wpmu'));
}
$this->apply_filter_and_action_fixes('plugins_loaded');
}
public function add_clef_login_button_to_wpmu() {
if ($this->settings->is_configured()) {
$_REQUEST['redirect_to'] = $redirect_url = apply_filters('wdfb_registration_redirect_url', '');
do_action('clef_render_login_button');
}
}
public function load_base_styles() {
$ident = ClefUtils::register_style('main');
wp_enqueue_style($ident);
$ident = ClefUtils::register_script('login');
wp_enqueue_script($ident);
if (!has_action('login_enqueue_scripts', 'wp_print_styles'))
add_action('login_enqueue_scripts', 'wp_print_styles', 11);
}
public function redirect_if_invite_code($redirect_to, $request, $user) {
if (isset($_REQUEST['clef_invite_code'])) {
$invite_code = $_REQUEST['clef_invite_code'];
$invite_email = base64_decode(ClefUtils::isset_GET('clef_invite_id'));
$error = $this->validate_invite_code($invite_code, $invite_email);
if (!$error) {
return admin_url('admin.php?page=connect_clef_account');
}
}
return $redirect_to;
}
public function validate_invite($message) {
$invite_code = ClefUtils::isset_GET('clef_invite_code');
$invite_email = base64_decode(ClefUtils::isset_GET('clef_invite_id'));
$error = $this->validate_invite_code($invite_code, $invite_email);
if ($invite_code && $error) {
return '<div id="login_error">' . $error . '</div>';
} else {
return $message;
}
}
public function login_form() {
if($this->settings->is_configured()) {
$redirect_url = $this->get_callback_url();
$passwords_disabled = $this->settings->get('clef_password_settings_force');
$override_key = ClefUtils::isset_GET('override');
if (!$this->is_valid_override_key($override_key)) {
$override_key = null;
}
$invite_code = ClefUtils::isset_GET('clef_invite_code');
$invite_email_encoded = ClefUtils::isset_GET('clef_invite_id');
if (!$this->has_valid_invite_code()) {
$invite_code = null;
$invite_email = null;
} else {
$redirect_url = add_query_arg(array( 'invite_code' => $invite_code), $redirect_url);
}
$app_id = $this->settings->get( 'clef_settings_app_id' );
$clef_embedded = $this->settings->should_embed_clef_login();
if (ClefUtils::isset_GET('clefup') == 'true') $clef_embedded = false;
echo ClefUtils::render_template('login.tpl', array(
"passwords_disabled" => $passwords_disabled,
"clef_embedded" => $clef_embedded,
"override_key" => $override_key,
"redirect_url" => $redirect_url,
"invite_code" => $invite_code,
"invite_email" => $invite_email_encoded,
"clef_id" => (isset($this->clef_id_to_connect) ? $this->clef_id_to_connect : false),
"app_id" => $app_id
));
}
}
public function register_form() {
if ($this->settings->is_configured() && $this->settings->registration_with_clef_is_allowed()) {
echo ClefUtils::render_template('register.tpl', array(
"redirect_url" => $this->get_callback_url(),
"app_id" => $this->settings->get( 'clef_settings_app_id' )
));
}
}
public function shortcode_render_login_button($atts) {
$app_id = isset($atts['app_id']) ? $atts['app_id'] : false;
$redirect_url = isset($atts['redirect_url']) ? $atts['redirect_url'] : false;
$embed = isset($atts['embed']) ? $atts['embed'] : false;
$type = isset($atts['type']) ? $atts['type'] : "login";
ob_start();
$this->render_login_button($redirect_url, $app_id, $embed);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
public function render_login_button($redirect_url=false, $app_id=false, $embed=false, $type="login") {
if (!$app_id) $app_id = $this->settings->get( 'clef_settings_app_id' );
if (!$redirect_url) $redirect_url = $this->get_callback_url();
echo ClefUtils::render_template('button.tpl', array(
"app_id" => $app_id,
"redirect_url" => $redirect_url,
"embed" => $embed,
"type" => $type,
"custom" => array(
"logo" => $this->settings->get('customization_logo'),
"message" => $this->settings->get('customization_message')
)
));
}
public function handle_login_failed($errors) {
if (isset($_POST['override'])) {
// if the person submitted an override before, automatically
// submit it for them the next time
$_GET['override'] = $_POST['override'];
}
}
private function is_valid_override_key($override_key) {
$valid_key = $this->settings->get( 'clef_override_settings_key' );
$is_valid_override_key =
(!empty($valid_key) &&
!empty($override_key) &&
$valid_key != '' &&
$override_key != '' &&
$override_key === $valid_key);
return $is_valid_override_key;
}
private function validate_invite_code($incoming_invite_code, $email) {
$generic_error_message = __("Sorry, that isn't a valid invite code.", "clef");
if (!$incoming_invite_code || !$email) {
return $generic_error_message;
}
$user = get_user_by('email', $email);
if (empty($user)) {
return $generic_error_message;
}
return $this->validate_invite_code_for_user($incoming_invite_code, $user);
}
private function validate_invite_code_for_user($incoming_invite_code, $user) {
$invite_code = get_user_meta($user->ID, 'clef_invite_code', true);
$three_days_ago = time() - 3 * 24 * 60 * 60;
if (empty($invite_code) ||
($invite_code->created_at < $three_days_ago) ||
($invite_code->code !== $incoming_invite_code)) {
return __("Sorry, this invite link has expired. Please contact your administrator for a new one.", "clef");
}
}
public function add_login_form_classes($classes) {
if (!$this->settings->is_configured()) return $classes;
array_push($classes, 'clef-login-form');
$override_key = ClefUtils::isset_GET('override');
$valid_override_or_invite = $this->is_valid_override_key($override_key) || $this->has_valid_invite_code();
if ($valid_override_or_invite) {
array_push($classes, 'clef-override-or-invite');
}
if ($this->settings->get( 'clef_password_settings_force' )) {
array_push($classes, 'clef-hidden');
}
if (isset($this->clef_id_to_connect)) {
array_push($classes, 'clef-auto-connect-account');
}
if ($this->settings->should_embed_clef_login()) {
array_push($classes, 'clef-login-form-embed');
}
// used to show username and password form in worst case scenario
// where javascript fails and on-page toggle fails
$show_username_password_form = ClefUtils::isset_GET('clefup') == 'true';
if ($show_username_password_form) {
array_push($classes, 'clef-show-username-password');
}
return $classes;
}
private function has_valid_invite_code() {
if (!isset($_REQUEST['clef_invite_code']) || !isset($_REQUEST['clef_invite_id'])) {
return false;
}
$incoming_invite_code = $_REQUEST['clef_invite_code'];
$invite_email = base64_decode($_REQUEST['clef_invite_id']);
if (!$incoming_invite_code || !$invite_email) {
return false;
}
$error = $this->validate_invite_code($incoming_invite_code, $invite_email);
return !$error;
}
public function disable_passwords($user) {
if (isset($_POST['override']) && $this->is_valid_override_key($_POST['override'])) {
return $user;
}
if ($this->has_valid_invite_code()) {
return $user;
}
$disabled_for_user = $this->settings->passwords_are_disabled_for_user($user);
$disabled_for_xml_rpc = !(defined('XMLRPC_REQUEST') && XMLRPC_REQUEST && $this->settings->xml_passwords_enabled());
if ($disabled_for_user && $disabled_for_xml_rpc) {
add_filter('xmlrpc_login_error', array($this, "return_xml_error_message"));
return new WP_Error('passwords_disabled', __("Passwords have been disabled for this user.", "clef"));
} else {
return $user;
}
}
public function authenticate_clef($user, $username, $password) {
if ( isset( $_REQUEST['clef'] ) && isset( $_REQUEST['code'] ) ) {
$this->apply_filter_and_action_fixes("authenticate");
// Authenticate
try {
$info = ClefUtils::exchange_oauth_code_for_info($_REQUEST['code'], $this->settings);
} catch (LoginException $e) {
return new WP_Error('clef', $e->getMessage());
} catch (ClefStateException $e) {
return new WP_Error('clef', $e->getMessage());
}
$clef_id = $info->id;
$email = isset($info->email) ? $info->email : "";
$first_name = isset($info->first_name) ? $info->first_name : "";
$last_name = isset($info->last_name) ? $info->last_name : "";
$users = get_users(array('meta_key' => 'clef_id', 'meta_value' => $clef_id, 'blog_id' => false));
if ($users) {
// already have a user with this clef_id
$user = $users[0];
} else {
$user = get_user_by('email', $email);
if (!$user) {
if (get_option('users_can_register') && $this->settings->registration_with_clef_is_allowed()) {
// Users can register, so create a new user
$id = wp_create_user($email, wp_generate_password(16, FALSE), $email);
if(is_wp_error($id)) {
return new WP_Error(
'clef',
__("An error occurred when creating your new account: ", 'clef') . $id->get_error_message()
);
}
$user = get_user_by('id', $id );
} else {
$this->clef_id_to_connect = $clef_id;
return new WP_Error(
'clef',
__("There's <b>no WordPress user</b> connected to your Clef account. <br></br> Log in with your standard username and password to <b>automatically connect your Clef account</b> now.", 'clef')
);
}
}
ClefUtils::associate_clef_id($clef_id, $user->ID);
}
do_action('clef_login', $user->ID);
// Log in the user
$session = ClefSession::start();
$session->set('logged_in_at', time());
return $user;
} else {
return $user;
}
}
public function disable_password_reset($allow, $user_id) {
$user = get_user_by('id', (int) $user_id);
return !$this->settings->passwords_are_disabled_for_user($user);
}
public function clear_logout_hook($user) {
$session = ClefSession::start();
if ($session->get('logged_in_at')) {
$session->set('logged_in_at', null);
}
return $user;
}
public function connect_clef_account_on_login($user) {
if (ClefUtils::isset_POST('clef_id') && $user && !is_wp_error($user)) {
ClefUtils::associate_clef_id(ClefUtils::isset_POST('clef_id'), $user->ID);
$session = ClefSession::start();
$session->set('clef_account_connected_on_login', true);
$session->set('logged_in_at', time());
}
return $user;
}
public function display_connect_clef_account_success() {
$session = ClefSession::start();
if ($session->get('clef_account_connected_on_login')) {
$session->set('clef_account_connected_on_login', null);
?>
<div class="updated clef-flash connect-clef-account-on-login-message">
<img src="<?php echo CLEF_URL . 'assets/dist/img/gradient_icon_32.png'?>" alt="Clef">
<p><?php _e('Success. <b>Your Clef account has been connected!</b>', 'clef'); ?></p>
</div>
<?php
}
}
public function return_xml_error_message() {
return new IXR_Error( 403, __("Passwords have been disabled for this user.", "clef") );
}
public function apply_filter_and_action_fixes($hook) {
if ($hook === "plugins_loaded") {
// Hack to make Clef work with theme my login. This works
// because Theme My Login only runs the login commands on their custom
// login page if the request is a POST. Could potentially cause
// other issues, so should be conscious of this.
if (isset($_REQUEST['clef']) && isset($_REQUEST['code']) && function_exists('is_plugin_active') && is_plugin_active('theme-my-login/theme-my-login.php')) {
$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST['log'] = true;
}
}
if ($hook === "authenticate") {
// remove login filters that cause problems — not necessary if we're
// logging in with Clef. These filters suppress errors that
// this login function throws.
remove_filter('authenticate', 'dr_email_login_authenticate', 20, 3);
remove_filter('authenticate', 'wp_authenticate_username_password', 20, 3);
// remove google captcha filter that prevents redirect
remove_filter('login_redirect', 'gglcptch_login_check');
}
}
public function get_callback_url() {
$url = add_query_arg(array( 'clef' => 'true'), wp_login_url());
# add redirect to if it exists
if (isset($_REQUEST['redirect_to']) && $_REQUEST['redirect_to'] != '') {
$url = add_query_arg(
array('redirect_to' => urlencode($_REQUEST['redirect_to'])),
$url
);
}
if (isset($_REQUEST['interim-login'])) {
$url = add_query_arg(array('interim-login' => 1), $url);
}
return $url;
}
public function initialize_state() {
ClefUtils::initialize_state();
}
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null || defined('CLEF_TESTING')) {
self::$instance = new self($settings);
}
return self::$instance;
}
}
?>

View File

@@ -0,0 +1,112 @@
<?php
class ClefLogout {
private static $instance = null;
private $settings;
private function __construct($settings) {
$this->settings = $settings;
$this->initialize_hooks();
}
public function initialize_hooks() {
add_filter( 'heartbeat_received', array($this, "hook_heartbeat"), 10, 3);
add_filter( 'init', array($this, 'logout_hook_handler'));
if (!defined('DOING_AJAX') || !DOING_AJAX) {
add_filter('init', array($this, "logged_out_check_with_redirect"));
}
}
/**
* Handle a Clef logout hook handshake, if the parameters exist.
*/
public function logout_hook_handler() {
if(isset($_POST) && isset($_POST['logout_token'])) {
$args = array(
'logout_token' => $_REQUEST['logout_token'],
'app_id' => $this->settings->get( 'clef_settings_app_id' ),
'app_secret' => $this->settings->get( 'clef_settings_app_secret' ),
);
$response = wp_remote_post( CLEF_API_BASE . 'logout', array( 'method' => 'POST',
'timeout' => 45, 'body' => $args ) );
if (is_wp_error($response)) {
$return = array(
"error" => $response->get_error_message(),
"success" => false
);
} else {
$body = json_decode( $response['body'] );
if (isset($body->success) && isset($body->clef_id)) {
$this->set_user_logged_out_at($body->clef_id);
$return = array(
"success" => true
);
} else {
$return = array(
"success" => false,
"error" => $body->error
);
}
}
echo(json_encode($return));
exit();
}
}
private function set_user_logged_out_at($clef_id) {
$user = get_users(array('meta_key' => 'clef_id', 'meta_value' => $clef_id, 'blog_id' => false));
if (!empty($user)) {
$user = $user[0];
// upon success, log user out
update_user_meta($user->ID, 'logged_out_at', time());
}
}
public function logged_out_check_with_redirect() {
$this->logged_out_check(array("redirect" => true));
}
public function logged_out_check($opts = array("redirect" => false)) {
$logged_out = false;
// if the user is logged into WP but logged out with Clef, sign them out of Wordpress
if (is_user_logged_in()) {
$session = ClefSession::start();
if ($session->get('logged_in_at') && $session->get('logged_in_at') < get_user_meta(wp_get_current_user()->ID, "logged_out_at", true)) {
wp_logout();
$logged_out = true;
} else {
$logged_out = false;
}
}
if ($opts['redirect'] && $logged_out) {
wp_redirect(wp_login_url());
exit();
} else {
return $logged_out;
}
}
public function hook_heartbeat($response, $data, $screen_id) {
$logged_out = $this->logged_out_check(array("redirect" => false));
if ($logged_out) {
$response['cleflogout'] = true;
}
return $response;
}
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}
?>

View File

@@ -0,0 +1,136 @@
<?php
require_once(CLEF_PATH . 'includes/class.clef-settings.php');
class ClefNetworkAdmin extends ClefAdmin {
private static $instance = null;
const MULTISITE_SETTINGS_ACTION = "clef_multisite_settings";
protected function __construct($settings) {
$this->settings = $settings;
if (is_network_admin()) {
$this->initialize_hooks();
require_once(CLEF_PATH . "/includes/lib/ajax-settings/ajax-settings.php");
$this->ajax_settings = AjaxSettings::start();
}
global $clef_ajax;
$clef_ajax->add_action(
self::MULTISITE_SETTINGS_ACTION,
array($this, 'ajax_multisite_options'),
array( 'capability' => 'manage_network_options')
);
}
public function initialize_hooks() {
add_action('admin_init', array($this, "setup_plugin"));
add_action('admin_init', array($this, "settings_form"));
add_action('admin_enqueue_scripts', array($this, "admin_enqueue_scripts"));
add_action('clef_hook_admin_menu', array($this, 'hook_admin_menu'));
// MULTISITE
if ($this->network_active()) {
add_action('network_admin_edit_clef_multisite', array($this, "multisite_settings_edit"), 10, 0);
}
}
public function admin_enqueue_scripts($hook) {
parent::admin_enqueue_scripts($hook);
}
public function hook_admin_menu() {
if ($this->network_active()) {
add_action('network_admin_menu', array($this, "admin_menu"));
}
}
public function network_active() {
if ( ! function_exists( 'is_plugin_active_for_network' ) )
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
return is_plugin_active_for_network('wpclef/wpclef.php');
}
public function admin_menu() {
add_menu_page(
"Clef",
"Clef",
"manage_options",
'clef',
array($this, 'general_settings'),
CLEF_URL . 'assets/dist/img/gradient_icon_16.png'
);
}
public function general_settings($options=array()) {
$options['isNetworkSettings'] = true;
$options['isUsingIndividualSettings'] = false;
$options['network_wide'] = true;
parent::general_settings($options);
}
public function ajax_multisite_options() {
global $HTTP_RAW_POST_DATA;
if (!isset($HTTP_RAW_POST_DATA)) {
$HTTP_RAW_POST_DATA = file_get_contents( "php://input" );
}
$settings = json_decode($HTTP_RAW_POST_DATA, true);
if (isset($settings['allow_override'])) {
update_site_option(ClefInternalSettings::MS_ALLOW_OVERRIDE_OPTION, (bool) $settings['allow_override']);
}
return array("success" => true);
}
public function multisite_settings_edit() {
if (!is_super_admin()) die(__('Cheatin&#8217; uh?'));
if (!wp_verify_nonce($_POST['_wpnonce'], 'clef_multisite')) {
die(__("Security check; nonce failed.", "clef"));
}
if (isset($_POST['allow_override_form'])) {
update_site_option(ClefInternalSettings::MS_ALLOW_OVERRIDE_OPTION, isset($_POST['allow_override']));
}
$enabled = get_site_option(ClefInternalSettings::MS_ENABLED_OPTION);
if (isset($_POST['disable']) || isset($_POST['enable'])) {
update_site_option(ClefInternalSettings::MS_ENABLED_OPTION, !$enabled);
}
if ($enabled) {
// TODO: actions for when network wide multisite is disabled
} else {
// TODO: actions for when network wide multiside is enabled
}
wp_redirect(add_query_arg(array('page' => $this->settings->settings_path, 'updated' => 'true'), network_admin_url('admin.php')));
exit();
}
public function setup_plugin() {
if (is_network_admin() && get_site_option("Clef_Activated")) {
delete_site_option("Clef_Activated");
if (!add_site_option(ClefInternalSettings::MS_ENABLED_OPTION, true)) {
update_site_option(ClefInternalSettings::MS_ENABLED_OPTION, true);
}
wp_redirect(add_query_arg(array('page' => $this->settings->settings_path), network_admin_url('admin.php')));
exit();
}
}
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}
?>

View File

@@ -0,0 +1,97 @@
<?php
class ClefOnboarding {
const ONBOARDING_KEY = "onboarding_data";
const FIRST_LOGIN_KEY = "has_logged_in";
const LOGINS = 'clef_logins';
private static $instance = null;
private $settings;
private function __construct($settings) {
$this->settings = $settings;
add_action('clef_login', array($this, 'mark_login_for_user_id'));
add_action('clef_login', array($this, 'do_first_login_action'));
}
public function get_data() {
$data = $this->settings->get(self::ONBOARDING_KEY);
if ($data) {
return unserialize($data);
} else {
return array();
}
}
public function set_data($data) {
return $this->settings->set(self::ONBOARDING_KEY, serialize($data));
}
public function get_key($key, $default=false) {
$onboarding_data = $this->get_data();
if (isset($onboarding_data[$key])) {
return $onboarding_data[$key];
} else {
return $default;
}
}
public function set_key($key, $value) {
$data = $this->get_data();
$data[$key] = $value;
return $this->set_data($data);
}
public function mark_login_for_user_id($user_id) {
$this->increment_logins_for_user_id($user_id, 1);
}
public function increment_logins_for_user_id($user_id, $by=1) {
$login_count = get_user_meta($user_id, self::LOGINS, true);
update_user_meta($user_id, self::LOGINS, $login_count + $by);
}
public function get_login_count_for_current_user() {
$user_id = get_current_user_id();
return get_user_meta($user_id, self::LOGINS, true);
}
public function do_first_login_action() {
if (!$this->get_key(self::FIRST_LOGIN_KEY)) {
$this->set_key(self::FIRST_LOGIN_KEY, true);
do_action('clef_onboarding_first_login');
}
}
public function had_clef_before_onboarding() {
return version_compare($this->settings->get("installed_at"), "1.9", "<");
}
/**
* Migrate the global login count to the user who is updating the plugin.
*/
public function migrate_global_login_count() {
$global_login_count = (int) $this->get_key('logins', 0);
if (!empty($global_login_count) && current_user_can('manage_options')) {
$this->increment_logins_for_user_id(get_current_user_id(), $global_login_count);
}
}
/**
* Set FIRST_LOGIN true for users who are upgrading — we don't want
* to disable passwords for all of our previous users before the 2.2.9.1
* update.
*/
public function set_first_login_true() {
$this->set_key(self::FIRST_LOGIN_KEY, true);
}
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}

View File

@@ -0,0 +1,159 @@
<?php
/**
* Clef Session
*
* This is a wrapper class for WP_Session / PHP $_SESSION.
*
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class ClefSession {
private static $instance = null;
/**
* Holds our session data
*
* @var array
* @access private
* @since 1.5
*/
private $session = array();
/**
* Whether to use PHP $_SESSION or WP_Session
*
* PHP $_SESSION is opt-in only by defining the CLEF_USE_PHP_SESSIONS constant
*
* @var bool
* @access private
* @since 1.5,1
*/
private $use_php_sessions = false;
/**
* Get things started
*
* Defines our WP_Session constants, includes the necessary libraries and
* retrieves the WP Session instance
*
* @since 1.5
*/
public function __construct() {
$this->use_php_sessions = defined( 'CLEF_USE_PHP_SESSIONS' ) && CLEF_USE_PHP_SESSIONS;
if( $this->use_php_sessions ) {
if( ! session_id() )
add_action( 'init', 'session_start', -2 );
} else {
// Use WP_Session (default)
if ( ! defined( 'WP_SESSION_COOKIE' ) )
define( 'WP_SESSION_COOKIE', 'clef_wp_session' );
if ( ! class_exists( 'Recursive_ArrayAccess' ) )
require_once CLEF_PATH . 'includes/lib/wp-session/class-recursive-arrayaccess.php';
if ( ! class_exists( 'WP_Session' ) ) {
require_once CLEF_PATH . 'includes/lib/wp-session/class-wp-session.php';
require_once CLEF_PATH . 'includes/lib/wp-session/wp-session.php';
}
add_filter( 'wp_session_expiration', array( $this, 'set_expiration_time' ), 99999 );
}
$this->init();
}
/**
* Setup the WP_Session instance
*
* @access public
* @since 1.5
* @return void
*/
public function init() {
if( $this->use_php_sessions )
$this->session = isset( $_SESSION['clef'] ) && is_array( $_SESSION['clef'] ) ? $_SESSION['clef'] : array();
else
$this->session = WP_Session::get_instance();
return $this->session;
}
/**
* Retrieve session ID
*
* @access public
* @since 1.6
* @return string Session ID
*/
public function get_id() {
return $this->session->session_id;
}
/**
* Retrieve a session variable
*
* @access public
* @since 1.5
* @param string $key Session key
* @return string Session variable
*/
public function get( $key ) {
$key = sanitize_key( $key );
return isset( $this->session[ $key ] ) ? maybe_unserialize( $this->session[ $key ] ) : false;
}
/**
* Set a session variable
*
* @since 1.5
*
* @param $key Session key
* @param $value Session variable
* @return mixed Session variable
*/
public function set( $key, $value ) {
$key = sanitize_key( $key );
if ( is_array( $value ) )
$this->session[ $key ] = serialize( $value );
else
$this->session[ $key ] = $value;
if( $this->use_php_sessions )
$_SESSION['clef'] = $this->session;
return $this->session[ $key ];
}
/**
* Force the cookie expiration time to 365 days
*
* @access public
* @since 1.9
* @param int $exp Default expiration (1 hour)
* @return int
*/
public function set_expiration_time( $exp ) {
return current_time( 'timestamp' ) + ( 60 * 60 * 24 * 365 );
}
public static function start() {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}

View File

@@ -0,0 +1,78 @@
<?php
require_once(CLEF_PATH . '/includes/lib/Settings_API_Util.inc');
class ClefSettings extends Settings_API_Util {
private function __construct($id, $optionName, $settings) {
$this->id = $id;
$this->optionName = $optionName;
$this->sections = array();
$this->introHTML = '';
$this->outroHTML = '';
register_setting( $id, $optionName, array(__CLASS__, 'validate'));
if ($settings->use_individual_settings) {
$this->values = get_option($optionName);
} else {
$this->values = get_site_option($optionName);
}
}
public static function validate(array $input) {
$input = parent::validate($input);
// sanitize inputs as text fields
foreach ($input as $key => &$value) {
$input[$key] = sanitize_text_field($value);
}
$attrs_to_escape = array(
'clef_settings_app_id',
'clef_settings_app_secret',
'customization_message',
'customization_logo'
);
foreach ($attrs_to_escape as $attr) {
if (isset($input[$attr])) {
$input[$attr] = esc_attr($input[$attr]);
}
}
if (isset($input['clef_password_settings_force']) && $input['clef_password_settings_force'] == "1") {
if (!ClefUtils::user_has_clef()) {
unset($input['clef_password_settings_force']);
$url = admin_url('admin.php?page=' . ClefAdmin::CONNECT_CLEF_PAGE);
add_settings_error(
CLEF_OPTIONS_NAME,
'clef_password_settings_force',
sprintf(__( "Please link your Clef account before you fully disable passwords. You can do this <a href='%s'>here</a>", "clef"), $url),
"error"
);
}
}
return $input;
}
public static function forID($id, $optionName=null, $settings=null) {
if(null === $optionName) {
$optionName = $id;
}
static $instances;
if(!isset($instances)) {
$instances = array();
}
if(!isset($instances[$id])) {
$instances[$id] = new ClefSettings($id, $optionName, $settings);
}
return $instances[$id];
}
}
?>

View File

@@ -0,0 +1,65 @@
<?php
/**
* Handles activation, deactivation, and uninstall when Clef is the base
* plugin.
*
* @package Clef
* @since 2.0
*/
class ClefSetup {
public static $meta_keys = array(
'clef_id',
'clef_invite_code',
'logged_out_at',
'clef_logins',
'clef_hide_waltz_badge',
'clef_hide_waltz_prompt'
);
public static function activate_plugin($network) {
if (is_network_admin()) {
add_site_option("Clef_Activated", true);
} else {
add_option("Clef_Activated", true);
}
}
public static function deactivate_plugin($network) {
if (CLEF_DEBUG) {
self::uninstall_plugin();
}
}
/**
* Clean up Clef metadata and site options.
*/
public static function uninstall_plugin() {
if (current_user_can( 'delete_plugins' )) {
foreach (self::$meta_keys as $meta_key) {
delete_metadata( 'user', 0, $meta_key, '', true );
}
}
if (is_multisite() && is_network_admin()) {
self::multisite_uninstall();
} else {
delete_option(CLEF_OPTIONS_NAME);
}
}
private static function multisite_uninstall() {
delete_site_option(CLEF_OPTIONS_NAME);
require_once(CLEF_PATH . 'includes/class.clef-internal-settings.php');
delete_site_option(ClefInternalSettings::MS_OVERRIDE_OPTION);
delete_site_option(ClefInternalSettings::MS_ENABLED_OPTION);
}
public static function register_plugin_hooks() {
register_activation_hook(CLEF_PATH . 'wpclef.php', array('ClefSetup', 'activate_plugin'));
register_deactivation_hook(CLEF_PATH . 'wpclef.php', array('ClefSetup', 'deactivate_plugin'));
register_uninstall_hook(CLEF_PATH . 'wpclef.php', array('ClefSetup', 'uninstall_plugin'));
}
}
?>

View File

@@ -0,0 +1,31 @@
<?php
/**
* Plugin-wide utility functions
*
* @package Clef
* @since 2.0
*/
class ClefTranslation {
public static function javascript() {
return array(
"messages" => array(
"error" => array(
"connect" => __("There was a problem automatically connecting your Clef account: <%= error %>. Please refresh and try again.", "clef"),
"create" => __("There was a problem creating a new Clef application for your WordPress site: <%= error %>. Please refresh and try again. If the issue, persists, email <a href='mailto:support@getclef.com'>support@getclef.com</a>.", "clef"),
"invite" => __("There was a problem sending invites: <%= error %>.", "clef"),
"generic" => __("Something wrong, please refresh and try again.", "clef"),
"disconnect" => __("There was a problem disconnecting your Clef account: <%= error %>.", "clef")
),
"success" => array(
"connect" => __("You've successfully connected your account with Clef", "clef"),
"configured" => __("You're all set up!", "clef"),
"invite" => __("Email invitations have been sent to your users.", "clef"),
"disconnect" => __("Successfully disconnected Clef account.", "clef")
),
"saving" => __("Settings are being saved. Are you sure you want to navigate away?", "clef")
)
);
}
}

View File

@@ -0,0 +1,112 @@
<?php
class ClefUserSettings {
private static $instance = null;
private $rendered = false;
const CONNECT_CLEF_OAUTH_ACTION = "connect_clef_account_oauth_code";
const DISCONNECT_CLEF_ACTION = "disconnect_clef_account";
protected function __construct($settings) {
$this->settings = $settings;
$this->initialize_hooks();
$this->connect_error = null;
}
public function initialize_hooks() {
add_action('init', array($this,'connect_clef_account'));
add_action('clef_render_user_settings', array($this, 'render'));
add_action('wp_footer', array($this, 'print_assets'));
add_shortcode('clef_user_settings', array($this, 'render'));
add_action('wp_enqueue_scripts', array($this, 'register_assets'));
global $clef_ajax;
$clef_ajax->add_action(
self::CONNECT_CLEF_OAUTH_ACTION,
array($this, 'ajax_connect_clef_account_with_oauth_code'),
array('capability' => 'read')
);
$clef_ajax->add_action(
self::DISCONNECT_CLEF_ACTION,
array($this, 'ajax_disconnect_clef_account'),
array('capability' => 'read')
);
}
public function render() {
echo ClefUtils::render_template(
'user_settings.tpl',
array(
"connect_error" => $this->connect_error,
"options" => array(
"connected" => ClefUtils::user_has_clef(),
"appID" => $this->settings->get( 'clef_settings_app_id' ),
"clefJSURL" => CLEF_JS_URL,
"state" => ClefUtils::get_state(),
"nonces" => array(
"disconnectClef" => wp_create_nonce(self::DISCONNECT_CLEF_ACTION)
)
)
)
);
$this->rendered = true;
}
public function register_assets() {
$this->script_identifier = ClefUtils::register_script('connect', array('jquery', 'backbone', 'underscore'));
$this->style_identifier = ClefUtils::register_style('admin');
wp_localize_script($this->script_identifier, 'ajaxurl', admin_url('admin-ajax.php'));
}
public function print_assets() {
if ($this->rendered) {
if (!ClefUtils::style_has_been_added('admin')) {
wp_print_styles($this->style_identifier);
wp_admin_css( 'wp-admin', true );
wp_admin_css( 'colors-fresh', true );
}
if (!ClefUtils::script_has_been_added('settings')) wp_print_scripts($this->script_identifier);
}
}
public function connect_clef_account() {
if (ClefUtils::isset_GET('connect_clef_account') && ClefUtils::isset_get('code')) {
try {
$info = ClefUtils::exchange_oauth_code_for_info(ClefUtils::isset_GET('code'), $this->settings);
$result = ClefUtils::associate_clef_id($info->id);
if (is_wp_error($result)) {
$this->connect_error = $result;
} else {
$session = ClefSession::start();
$session->set('logged_in_at', time());
return;
}
} catch (LoginException $e) {
$this->connect_error = new WP_Error("bad_oauth_exchange", $e->getMessage());
} catch (ClefStateException $e) {
$this->connect_error = new WP_Error("bad_state_parameter", $e->getMessage());
}
}
}
public function ajax_disconnect_clef_account() {
$user = wp_get_current_user();
$passwords_disabled = $this->settings->passwords_are_disabled_for_user($user);
if (current_user_can('manage_options') && $passwords_disabled) {
return new WP_Error('passwords_disabled', __("your passwords are currently disabled. <br/> If you disconnect your Clef account, you won't be able to log in. Please enable passwords for yourself before disconnecting your Clef account", 'clef'));
}
ClefUtils::dissociate_clef_id();
return array("success" => true);
}
public static function start($settings) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}

View File

@@ -0,0 +1,270 @@
<?php
/**
* Plugin-wide utility functions
*
* @package Clef
* @since 2.0
*/
class ClefUtils {
public static $default_roles = array("Subscriber", "Contributor", "Author", "Editor", "Administrator", "Super Administrator" );
/**
* Runs esc_html on strings. Leaves input untouched if it's not a string.
*
* @return mixed
*/
private static function escape_string($maybe_string) {
$escaped = $maybe_string;
if (is_string($maybe_string)) {
$escaped = esc_html($maybe_string);
}
return $escaped;
}
/**
* Renders the specified template, giving it access to $variables.
* Strings are escaped.
*
* @param string $name The name (with no .php extension) of a file in
* templates/.
* @param array $variables A list of variables to be used in the
* template.
* @return string
*/
public static function render_template($name, $variables=false, $sanitize=true) {
if ($variables) {
$escaped_variables = $variables;
if ($sanitize) {
$escaped_variables = array_map(array(__CLASS__, 'escape_string'), $variables);
}
extract($escaped_variables, EXTR_SKIP);
}
ob_start();
require(CLEF_TEMPLATE_PATH . $name . '.php');
return ob_get_clean();
}
/**
* Return $_GET[$key] if it exists.
*
* @param string $key
* @return mixed
*/
public static function isset_GET($key) {
return isset($_GET[$key]) ? $_GET[$key] : null;
}
/**
* Return $_POST[$key] if it exists.
*
* @param string $key
* @return mixed
*/
public static function isset_POST($key) {
return isset($_POST[$key]) ? $_POST[$key] : null;
}
/**
* Return $_REQUEST[$key] if it exists.
*
* @param string $key
* @return mixed
*/
public static function isset_REQUEST($key) {
return isset($_REQUEST[$key]) ? $_REQUEST[$key] : null;
}
public static function set_html_content_type() {
return 'text/html';
}
public static function register_script($name, $dependencies=array('jquery')) {
$ident = "wpclef-" . $name;
if (!CLEF_DEBUG) {
$name .= '.min';
}
$name .= '.js';
wp_register_script(
$ident,
CLEF_URL .'assets/dist/js/' . $name,
$dependencies,
CLEF_VERSION,
TRUE
);
wp_localize_script($ident, "clefTranslations", ClefTranslation::javascript());
return $ident;
}
public static function register_style($name) {
$ident = "wpclef-" . $name;
if (!CLEF_DEBUG) {
$name .= '.min';
}
$name .= '.css';
wp_register_style(
$ident,
CLEF_URL . 'assets/dist/css/' . $name,
FALSE,
CLEF_VERSION
);
return $ident;
}
public static function style_has_been_added($name) {
$ident = "wpclef-" . $name;
return wp_style_is($ident, 'enqueued')
|| wp_style_is($ident, 'done')
|| wp_style_is($ident, 'to_do');
}
public static function script_has_been_added($name) {
$ident = "wpclef-" . $name;
return wp_script_is($ident, 'enqueued')
|| wp_script_is($ident, 'done')
|| wp_script_is($ident, 'to_do');
}
public static function user_has_clef($user=false) {
# if no user is provided, defaults to current user
if (!$user) $user = wp_get_current_user();
return !!get_user_meta($user->ID, "clef_id", true);
}
public static function associate_clef_id($clef_id, $user_id=false) {
if (!$user_id) {
$user_id = wp_get_current_user()->ID;
}
$user = get_users(array(
'meta_key' => 'clef_id',
'meta_value' => $clef_id,
'blog_id' => false
));
if (!empty($user)) {
return new WP_Error(
'clef_id_already_associated',
__("The Clef account you're trying to connect is already associated to a different WordPress account", "clef")
);
}
update_user_meta($user_id, 'clef_id', $clef_id);
}
public static function dissociate_clef_id($user_id=false) {
if (!$user_id) {
$user_id = wp_get_current_user()->ID;
}
delete_user_meta($user_id, "clef_id");
}
public static function exchange_oauth_code_for_info($code, $settings=null, $app_id=false, $app_secret=false) {
ClefUtils::verify_state();
if ($settings) {
if (!$app_id) $app_id = $settings->get( 'clef_settings_app_id' );
if (!$app_secret) $app_secret = $settings->get( 'clef_settings_app_secret' );
}
$args = array(
'code' => $code,
'app_id' => $app_id,
'app_secret' => $app_secret,
);
$response = wp_remote_post( CLEF_API_BASE . 'authorize', array( 'method'=> 'POST', 'body' => $args, 'timeout' => 20 ) );
if ( is_wp_error($response) ) {
throw new LoginException(__( "Something went wrong: ", 'clef' ) . $response->get_error_message());
}
$body = json_decode( $response['body'] );
if ( !isset($body->success) || $body->success != 1 ) {
throw new LoginException(__( 'Error retrieving Clef access token: ', 'clef') . $body->error);
}
$access_token = $body->access_token;
// Get info
$response = wp_remote_get( CLEF_API_BASE . "info?access_token={$access_token}" );
if ( is_wp_error($response) ) {
throw new LoginException(__( "Something went wrong: ", 'clef' ) . $response->get_error_message());
}
$body = json_decode( $response['body'] );
if ( !isset($body->success) || $body->success != 1 ) {
throw new LoginException(__('Error retrieving Clef user data: ', 'clef') . $body->error);
}
return $body->info;
}
public static function user_fulfills_role($user, $role) {
$fulfills_role = false;
$role_map = array(
"subscriber",
"contributor",
"author",
"editor",
"administrator",
"super administrator"
);
foreach ($user->roles as &$user_role) {
$rank = array_search($user_role, $role_map);
if ($rank != 0 && $rank >= array_search($role, $role_map)) {
$fulfills_role = true;
break;
}
}
if ($role == "super administrator" && is_super_admin($user->ID)) {
$fulfills_role = true;
}
return $fulfills_role;
}
public static function get_custom_roles() {
$all_roles = get_editable_roles();
$custom_roles = array();
foreach($all_roles as $role => $role_obj) {
if (isset($role_obj['name'])) {
$role_name = $role_obj['name'];
if (!in_array($role_name, self::$default_roles)) {
$custom_roles[$role] = $role_obj;
}
}
}
return $custom_roles;
}
public static function initialize_state($override = false) {
if (!$override && isset($_COOKIE['_clef_state']) && $_COOKIE['_clef_state']) return;
$state = wp_generate_password(24, false);
@setcookie('_clef_state', $state, (time() + 60 * 60 * 24), '/', '', is_ssl(), true);
$_COOKIE['_clef_state'] = $state;
return $state;
}
public static function get_state() {
if (!isset($$_COOKIE['_clef_state']) || !$_COOKIE['_clef_state']) ClefUtils::initialize_state();
return $_COOKIE['_clef_state'];
}
public static function verify_state() {
$request_state = ClefUtils::isset_GET('state') ? ClefUtils::isset_GET('state') : ClefUtils::isset_POST('state');
$correct_state = ClefUtils::get_state();
if ($request_state && $correct_state && $correct_state == $request_state) {
ClefUtils::initialize_state(true);
return true;
} else {
throw new ClefStateException('The state parameter is not verified. This may be due to this page being cached by another WordPress plugin. Please refresh your page and try again');
}
}
}
?>

View File

@@ -0,0 +1,367 @@
<?php if(!class_exists('Settings_API_Util')) {
/**
* Utility class to bring some sanity to WordPress's Settings API
* @version 0.2 - custom w/introHTML
* @author Dave Ross <dave@davidmichaelross.com>
* @license BSD
*/
class Settings_API_Util {
const ICON_THEMES = 'icon-themes';
const ICON_COMMENTS = 'icon-edit-comments';
const ICON_INDEX = 'icon-index';
const ICON_LINKS = 'icon-link-manager';
const ICON_UPLOAD = 'icon-upload';
const ICON_PAGES = 'icon-edit-pages';
const ICON_PLUGIN = 'icon-plugins';
const ICON_TOOLS = 'icon-tools';
const ICON_SETTINGS = 'icon-options-general';
const ICON_POSTS = 'icon-edit';
const ICON_USERS = 'icon-users';
public $id;
public $optionName;
public $values;
private $sections;
public $introHTML;
/**
* Fetch/create a form object with the given ID
* @staticvar SN_Tracker_Admin $instance Stores Singleton instances
* @param string $id Form ID
* @param string $optionName Name to store settings under in the WordPress wp_options table. Defaults to $id.
* @return SN_Tracker_Admin instance
*/
public static function forID($id, $optionName = null, $rest=null) {
if(null === $optionName) {
$optionName = $id;
}
static $instances;
if(!isset($instances)) {
$instances = array();
}
if(!isset($instances[$id])) {
$instances[$id] = new Settings_API_Util($id, $optionName);
}
return $instances[$id];
}
private function __construct($id, $optionName) {
$this->id = $id;
$this->optionName = $optionName;
$this->sections = array();
$this->introHTML = '';
$this->outroHTML = '';
register_setting( $id, $optionName, array(__CLASS__, 'validate'));
$this->values = get_option($optionName);
}
/**
* Basic form validation/sanitization
* @param array $input
* @return array validated/filtered input
*/
public static function validate(array $input) {
$output = array();
foreach($input as $key=>$value) {
$output[$key] = trim($value);
}
return $output;
}
/**
* Render a standard WordPress settings form
* @param type $title Settings page title
* @param type $icon Settings_API_Util::ICON_* constant
* @todo Get HTML out of this PHP code
*/
public function renderBasicForm($title, $icon) {
if (MULTISITE && is_network_admin()) {
$url = "edit.php?action=" . $this->id;
} else {
$url="options.php";
}
echo '<div class="wrap" id="' . $this->id . '">';
echo '<div id="'.$icon.'" class="frmicon icon32"><br></div>';
echo '<h2>'.$title.'</h2>';
if(!empty($this->introHTML)) {
echo '<div class="intro">' . $this->introHTML . '</div>';
}
echo '<form id="' . $this->id . '" action="' . $url . '" method="POST">';
settings_fields($this->id);
do_settings_sections($this->id);
echo '<p class="submit">';
echo '<input type="submit" name="submit" class="button-primary" value="'.__('Save').'" />';
echo '</p>';
echo '</form>';
echo '<div class="outro">' . $this->outroHTML . '</div>';
echo '</div>';
}
/**
* Add a new setting to this form
* @param type $id
* @param type $title
* @param type $sectionHeadCallback
* @return Settings_API_Util_Section
*/
public function addSection($id, $title, $sectionHeadCallback = null) {
$section = new Settings_API_Util_Section($this, $id, $title, $sectionHeadCallback);
$this->sections[$id] = $section;
return $section;
}
/**
* Retrieve an existing section with the given ID
* @param string $id
* @return Settings_API_Util_Section
*/
public function getSection($id) {
return $this->sections[$id];
}
}
class Settings_API_Util_Section {
public $settings;
public $id;
public $title;
private $fields;
function __construct($settings, $id, $title, $sectionHeadCallback = null) {
if($sectionHeadCallback === null) {
$sectionHeadCallback = array(__CLASS__, 'renderEmptySectionHead');
}
$this->settings = $settings;
$this->id = $id;
$this->title = $title;
$this->fields = array();
add_settings_section($id, $title, $sectionHeadCallback, $settings->id);
}
/**
*
* @param string $id
* @param string $title
* @param string $type Settings_API_Util_Field::TYPE_* constant
* @param string $defaultValue
* @return Settings_API_Util_Field
*/
public function addField($id, $title, $type, $defaultValue = null, $additionalArgs = null) {
$field = new Settings_API_Util_Field($this, $id, $title, $type, $defaultValue, $additionalArgs);
$this->fields[$id] = $field;
return $field;
}
/**
* Retrieve an existing field with the given ID
* @param string $id
* @return Settings_API_Util_Field
*/
public function getField($id) {
return $this->fields[$id];
}
/**
* Default callback for rendering the section heading
*/
public static function renderEmptySectionHead() { echo ''; }
}
class Settings_API_Util_Field {
const TYPE_TEXTFIELD = 'textfield';
const TYPE_TEXTAREA = 'textarea';
const TYPE_CHECKBOX = 'checkbox';
const TYPE_RADIO = 'radio';
const TYPE_SELECT = 'select';
const TYPE_HIDDEN = 'hidden';
public $section;
function __construct($section, $id, $title, $type, $defaultValue, $additionalArgs) {
$this->section = $section;
$this->name = $id;
$this->optionID = $section->id.'_'.$id;
$this->defaultValue = $defaultValue;
$this->type = $type;
$values = array(
'id' =>$section->settings->optionName.'_'.$this->optionID,
'name' => $section->settings->optionName.'['.$this->optionID.']',
);
if (isset($this->section->settings->values[$this->optionID])) {
$values['value'] = $this->section->settings->values[$this->optionID];
}
else {
$values['value'] = $defaultValue;
}
if ($additionalArgs) {
$values = array_merge($values, $additionalArgs);
}
$this->values = $values;
add_settings_field($section->id.'_'.$id, __( $title, 'clef' ), array(__CLASS__, $type), $section->settings->id, $section->id, $values);
}
function render($opts = array()) {
call_user_func(array(__CLASS__, $this->type), array_merge($opts, $this->values));
}
///////////////////
// Field renderers
///////////////////
/**
* Textfield
* @param array $options
*/
public static function textfield(array $options) {
if (!isset($options['placeholder'])) $options['placeholder'] = '';
ob_start();
do_action('settings_api_util_pre_textfield');
echo "<input type=\"text\" name=\"{$options['name']}\" value=\"{$options['value']}\" id=\"{$options['name']}\" class=\"regular-text\" placeholder=\"{$options['placeholder']}\" />";
do_action('settings_api_util_after_textfield');
$html = ob_get_contents();
ob_end_clean();
apply_filters('settings_api_util_textfield', $html);
echo $html;
}
/**
* Textarea
* @param array $options
*/
public static function textarea(array $options) {
ob_start();
do_action('settings_api_util_pre_textarea');
echo "<textarea name=\"{$options['name']}\">{$options['value']}</textarea>";
do_action('settings_api_util_after_textarea');
$html = ob_get_contents();
ob_end_clean();
apply_filters('settings_api_util_textarea', $html);
echo $html;
}
/**
* Checkbox
* @param array $options
*/
public static function checkbox(array $options) {
ob_start();
do_action('settings_api_util_pre_checkbox');
if($options['value']) {
echo "<input type=\"checkbox\" name=\"{$options['name']}\" value=\"1\" checked>";
} else {
echo "<input type=\"checkbox\" name=\"{$options['name']}\" value=\"1\">";
}
do_action('settings_api_util_after_checkbox');
$html = ob_get_contents();
ob_end_clean();
apply_filters('settings_api_util_checkbox', $html);
echo $html;
}
/**
* Radio
* @param array $options
*/
public static function radio(array $options) {
ob_start();
do_action('settings_api_util_pre_radio');
echo "<input type=\"radio\" name=\"{$options['name']}\" value=\"{$options['value']}\" />";
do_action('settings_api_util_after_radio');
$html = ob_get_contents();
ob_end_clean();
apply_filters('settings_api_util_radio', $html);
echo $html;
}
/**
* Select
* @param array $options
*/
public static function select(array $options) {
ob_start();
do_action('settings_api_util_pre_select');
echo "<select name=\"{$options['name']}\">";
foreach ($options['options'] as &$opt) {
if (gettype($opt) == "array") {
$display = $opt[0];
$value = $opt[1];
} else {
$display = $value = $opt;
}
if ($value == $options['value']) {
echo "<option selected";
} else {
echo "<option";
}
echo " value='". $value . "'>" . $display . "</option>";
}
echo "</select>";
do_action('settings_api_util_after_select');
$html = ob_get_contents();
ob_end_clean();
apply_filters('settings_api_util_select', $html);
echo $html;
}
/**
* Hidden
* @param array $options
*/
public static function hidden(array $options) {
ob_start();
do_action('settings_api_util_pre_hidden');
echo "<input type=\"hidden\" name=\"{$options['name']}\" value=\"{$options['value']}\"/>";
do_action('settings_api_util_after_hidden');
$html = ob_get_contents();
ob_end_clean();
apply_filters('settings_api_util_hidden', $html);
echo $html;
}
}
}
?>

View File

@@ -0,0 +1,141 @@
<?php
class AjaxSettings {
const VERSION = '0.0.1';
static $DEFAULTS = array(
"network_wide" => false
);
private static $instance = null;
private function __construct( $opts ) {
$this->options = array_merge(self::$DEFAULTS, $opts);
add_action('admin_enqueue_scripts', array($this, "enqueue_scripts"));
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
add_action(
'wp_ajax_ajax_settings_save_' . $this->name(),
array($this, 'handle_settings_save')
);
}
function enqueue_scripts() {
$ident = $this->identifier();
wp_register_script(
$ident,
$this->options['base_url'] . "js/ajax-settings.min.js",
array('backbone'),
self::VERSION,
TRUE
);
wp_localize_script($ident, 'ajaxSetOpt', $this->options);
wp_enqueue_script($ident);
}
function enqueue_styles() {
$ident = $this->identifier();
wp_register_style(
$ident,
$this->options['base_url'] . 'css/ajax-settings.min.css',
false,
self::VERSION
);
wp_enqueue_style($ident);
}
function handle_settings_save() {
global $HTTP_RAW_POST_DATA;
if (!isset($HTTP_RAW_POST_DATA)) {
$HTTP_RAW_POST_DATA = file_get_contents( "php://input" );
}
// strip off any leading characters before json starts and then parse
$stripped = preg_replace('/^.*?{/', '{', $HTTP_RAW_POST_DATA);
$settings = json_decode($stripped, true);
if (!$settings) wp_die(__('Settings could not be parsed — this may be caused by a plugin conflict.'));
$option_page = $settings['option_page'];
$is_network_wide = isset($_REQUEST['network_wide']) && $_REQUEST['network_wide'];
if (!wp_verify_nonce($settings['_wpnonce'], $option_page . "-options")) {
wp_die(__('Cheatin&#8217; uh?'));
}
// if it's network request, we want to check that the current user is
// a network admin
if ($is_network_wide && !is_super_admin()) wp_die(__('Cheatin&#8217; uh?'));
// verify that the user has the permissions to edit the clef page
$capability = 'manage_options';
$capability = apply_filters( "option_page_capability_{$option_page}", $capability );
if ( !current_user_can( $capability ) )
wp_die(__('Cheatin&#8217; uh?'));
$whitelist_options = apply_filters( 'whitelist_options', array() );
$options = $whitelist_options[$settings['option_page']];
if (empty($options[0]) || $options[0] != $this->name()) {
wp_die("You can't do that!");
}
$to_be_saved = array();
foreach ($settings as $key => &$value) {
$match = preg_match('/(.+)\[(.+)\]$/', $key, $output);
if ($match) {
$nester_key = $output[1];
if ($nester_key == $this->name()) {
$nested_key = $output[2];
$to_be_saved[$nested_key] = $value;
}
}
}
$to_be_saved = apply_filters('ajax_settings_pre_save', $to_be_saved);
if ($is_network_wide) {
update_site_option($this->name(), $to_be_saved);
} else {
update_option($this->name(), $to_be_saved);
}
$errors = get_settings_errors();
$response = array();
if (!empty($errors)) {
$error_messages = array();
foreach ($errors as &$error) {
$error_messages[$error['code']] = $error['message'];
}
$response['errors'] = $error_messages;
header('HTTP/1.0 400');
wp_send_json_error($response);
} else {
$response['success'] = true;
wp_send_json($response);
}
}
function identifier() {
return $this->name() . '-ajax-settings';
}
function name() {
return $this->options['options_name'];
}
function update_options($options) {
$this->options = array_merge($this->options, $options);
}
public static function start($options = array()) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($options);
} else {
self::$instance->update_options($options);
}
return self::$instance;
}
}

View File

@@ -0,0 +1 @@
div.ajax-settings-updated{width:20px;height:20px;background:#fff;position:absolute;border-radius:20px;right:0;top:0;margin-left:10px;opacity:1;-webkit-animation:blink 2s infinite;animation:blink 2s infinite}div.ajax-settings-updated.success{background-color:#2ecc71;-webkit-transition:500ms;transition:500ms}@-webkit-keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}@keyframes blink{0%,100%{opacity:1}50%{opacity:.4}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,2 @@
(function(e,t){var i;return i=t.Model.extend({url:ajaxurl+"?action=ajax_settings_save",sync:function(e,i,s){return s=s||{},s.emulateHTTP=!0,t.Model.prototype.sync.call(this,e,i,s)},parse:function(e,t){return t.form?(this.optionsName=t.options_name,this.url=ajaxurl+("?action=ajax_settings_save_"+this.optionsName),t.url&&(this.url=t.url),t.network_wide&&(this.url+="&network_wide=true"),this.$form=t.form,_.extend(e,this.$form.serializeObject())):void 0},isNew:function(){return!1},update:function(t){var i;return i=e(t),i.is(":checkbox")&&(t.value=i.is(":checked")?1:0),this.saving=!0,this.save(t.name,t.value,{success:this.saveSuccess.bind(this),error:this.saveError.bind(this)})},saveSuccess:function(e,t){return this.saving=!1},saveError:function(e,t){return this.saving=!1},findInput:function(e,t){return null==t&&(t={}),this._is||(this._is={}),this._is[e]||(this._is[e]=this.$form.find("input, select, textarea").filter("[name='"+e+"']")),this._is[e]}}),e.fn.serializeObject=function(t){var i,s,n,r,a;for(s={},a=e(this).serializeArray(),n=0,r=a.length;r>n;n++)i=a[n],s[i.name]=i.value;return s},i.extend=t.Model.extend,this.AjaxSettingsModel=i}).call(this,jQuery,Backbone);
(function(e,t){var s;return s=t.View.extend({messageTemplate:_.template("<div class='<%=type%> ajax-settings-msg'><p><%= message %></p></div>"),events:{"change input:not(.ajax-ignore)":"persistChanges","change select:not(.ajax-ignore)":"persistChanges","change textarea:not(.ajax-ignore)":"persistChanges"},modelClass:AjaxSettingsModel,el:'form[action="options.php"]',genericErrorMessage:"Something went wrong: ",successMessageDisplayTime:3e3,initialize:function(t){return this.opts=t,this.opts.formSelector&&this.setElement(e(t.formSelector)),this.hide(),this.model=new this.modelClass({},_.extend({form:this.$el,parse:!0},this.opts)),this.listenTo(this.model,"change",this.render),this.listenTo(this.model,"change",this.startUpdating),this.listenTo(this.model,"sync",this.updated),this.listenTo(this.model,"error",this.error),this.listenTo(this.model,"change",this.clearErrors)},hide:function(){return this.$el.hide()},render:function(){var e,t,s,r,i;r=this.model.attributes,i=[];for(t in r)s=r[t],e=this.model.findInput(t),e?e.is(":checkbox")?i.push(e.prop("checked",parseInt(s))):i.push(e.val(s)):i.push(void 0);return i},show:function(){return this.$el.fadeIn()},persistChanges:function(e){return e.preventDefault(),this.model.update(e.currentTarget)},startUpdating:function(e,t){var s,r,i,n;i=e.changed,n=[];for(s in i)r=i[s],n.push(this.settingUpdateSent(this.model.findInput(s)));return n},updated:function(e,t){var s,r,i,n;this.render(),i=e.changed,n=[];for(s in i)r=i[s],n.push(this.settingUpdateSuccess(this.model.findInput(s)));return n},error:function(e,t){var s,r,i,n;if(!t.responseJSON||!t.responseJSON.data)return void this.showMessage({message:""+this.genericErrorMessage+" "+t.responseText,type:"error"});if(!t.responseJSON.data.errors&&t.responseJSON.data.error)return void this.showMessage({message:t.responseJSON.data.error,type:"error"});i=t.responseJSON.data.errors,n=[];for(s in i)r=i[s],n.push(this.settingsUpdateError(this.model.findInput(""+this.opts.options_name+"["+s+"]"),r));return n},clearErrors:function(e,t){var s,r,i,n,a;this.globalError&&(this.globalError.remove(),this.globalError=null),n=e.changed,a=[];for(r in n)i=n[r],s=this.model.findInput(r),s.data("errorEl")?(s.data("errorEl").remove(),a.push(s.data("errorEl",null))):a.push(void 0);return a},settingUpdateSent:function(e){},settingUpdateSuccess:function(t){var s;if(t.length&&!t.data("successEl"))return s=e(this.messageTemplate({message:"Setting saved.",type:"updated"})).hide(),t.data("successEl",s.insertAfter(t).slideDown()),setTimeout(function(){return s.slideUp(),t.data("successEl",null)},this.successMessageDisplayTime)},settingsUpdateError:function(t,s){var r;return t.data("errorEl")?t.data("errorEl").find("p").html(s):(r=e(this.messageTemplate({message:s,type:"error"})).hide(),t.data("errorEl",r.insertAfter(t).slideDown()))},showMessage:function(t){var s;return s=e(this.messageTemplate(t)).hide(),this.globalError=s.prependTo(this.$el).slideDown(),e("html, body").animate({scrollTop:this.$el.scrollTop()},"slow")}},{extend:t.View.extend}),this.AjaxSettingsView=s,e(document).ready(function(){var e;return ajaxSetOpt.initialize?(e=""+ajaxSetOpt.options_name+"AjaxSettingsView",window[e]=new s(ajaxSetOpt)):void 0}),e.fn.leftPositionWithPadding=function(){var e;return e=this.position().left,e+=this.width(),this.css("padding-left")&&(e+=parseInt(this.css("padding-left"))),this.css("padding-right")&&(e+=parseInt(this.css("padding-right"))),this.css("border-left")&&(e+=parseInt(this.css("border-left"))),this.css("border-right")&&(e+=parseInt(this.css("border-right"))),e}}).call(this,jQuery,Backbone);

View File

@@ -0,0 +1,12 @@
<?php
function plugin_symlink_fix( $url, $path, $plugin ) {
// Do it only for this plugin
if ( strstr( $plugin, 'wpclef' ) ) {
return str_replace( dirname( $plugin ), '/' . basename( dirname( $plugin ) ), $url );
}
return $url;
}
add_filter( 'plugins_url', 'plugin_symlink_fix', 10, 3 );

View File

@@ -0,0 +1,5 @@
<?php
if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
}
?>

View File

@@ -0,0 +1,138 @@
<?php
/**
* Multidimensional ArrayAccess
*
* Allows ArrayAccess-like functionality with multidimensional arrays. Fully supports
* both sets and unsets.
*
* @package WordPress
* @subpackage Session
* @since 3.7.0
*/
/**
* Recursive array class to allow multidimensional array access.
*
* @package WordPress
* @since 3.7.0
*/
class Recursive_ArrayAccess implements ArrayAccess {
/**
* Internal data collection.
*
* @var array
*/
protected $container = array();
/**
* Flag whether or not the internal collection has been changed.
*
* @var bool
*/
protected $dirty = false;
/**
* Default object constructor.
*
* @param array $data
*/
protected function __construct( $data = array() ) {
foreach ( $data as $key => $value ) {
$this[ $key ] = $value;
}
}
/**
* Allow deep copies of objects
*/
public function __clone() {
foreach ( $this->container as $key => $value ) {
if ( $value instanceof self ) {
$this[ $key ] = clone $value;
}
}
}
/**
* Output the data container as a multidimensional array.
*
* @return array
*/
public function toArray() {
$data = $this->container;
foreach ( $data as $key => $value ) {
if ( $value instanceof self ) {
$data[ $key ] = $value->toArray();
}
}
return $data;
}
/*****************************************************************/
/* ArrayAccess Implementation */
/*****************************************************************/
/**
* Whether a offset exists
*
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset An offset to check for.
*
* @return boolean true on success or false on failure.
*/
public function offsetExists( $offset ) {
return isset( $this->container[ $offset ]) ;
}
/**
* Offset to retrieve
*
* @link http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset The offset to retrieve.
*
* @return mixed Can return all value types.
*/
public function offsetGet( $offset ) {
return isset( $this->container[ $offset ] ) ? $this->container[ $offset ] : null;
}
/**
* Offset to set
*
* @link http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*
* @return void
*/
public function offsetSet( $offset, $data ) {
if ( is_array( $data ) ) {
$data = new self( $data );
}
if ( $offset === null ) { // don't forget this!
$this->container[] = $data;
} else {
$this->container[ $offset ] = $data;
}
$this->dirty = true;
}
/**
* Offset to unset
*
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset The offset to unset.
*
* @return void
*/
public function offsetUnset( $offset ) {
unset( $this->container[ $offset ] );
$this->dirty = true;
}
}

View File

@@ -0,0 +1,310 @@
<?php
/**
* WordPress session managment.
*
* Standardizes WordPress session data using database-backed options for storage.
* for storing user session information.
*
* @package WordPress
* @subpackage Session
* @since 3.7.0
*/
/**
* WordPress Session class for managing user session data.
*
* @package WordPress
* @since 3.7.0
*/
final class WP_Session extends Recursive_ArrayAccess implements Iterator, Countable {
/**
* ID of the current session.
*
* @var string
*/
protected $session_id;
/**
* Unix timestamp when session expires.
*
* @var int
*/
protected $expires;
/**
* Unix timestamp indicating when the expiration time needs to be reset.
*
* @var int
*/
protected $exp_variant;
/**
* Singleton instance.
*
* @var bool|WP_Session
*/
private static $instance = false;
/**
* Retrieve the current session instance.
*
* @param bool $session_id Session ID from which to populate data.
*
* @return bool|WP_Session
*/
public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Default constructor.
* Will rebuild the session collection from the given session ID if it exists. Otherwise, will
* create a new session with that ID.
*
* @param $session_id
* @uses apply_filters Calls `wp_session_expiration` to determine how long until sessions expire.
*/
protected function __construct() {
if ( isset( $_COOKIE[WP_SESSION_COOKIE] ) ) {
$cookie = stripslashes( $_COOKIE[WP_SESSION_COOKIE] );
$cookie_crumbs = explode( '||', $cookie );
$this->session_id = $cookie_crumbs[0];
$this->expires = $cookie_crumbs[1];
$this->exp_variant = $cookie_crumbs[2];
// Update the session expiration if we're past the variant time
if ( time() > $this->exp_variant ) {
$this->set_expiration();
delete_option( "_wp_session_expires_{$this->session_id}" );
add_option( "_wp_session_expires_{$this->session_id}", $this->expires, '', 'no' );
}
} else {
$this->session_id = $this->generate_id();
$this->set_expiration();
}
$this->read_data();
$this->set_cookie();
}
/**
* Set both the expiration time and the expiration variant.
*
* If the current time is below the variant, we don't update the session's expiration time. If it's
* greater than the variant, then we update the expiration time in the database. This prevents
* writing to the database on every page load for active sessions and only updates the expiration
* time if we're nearing when the session actually expires.
*
* By default, the expiration time is set to 30 minutes.
* By default, the expiration variant is set to 24 minutes.
*
* As a result, the session expiration time - at a maximum - will only be written to the database once
* every 24 minutes. After 30 minutes, the session will have been expired. No cookie will be sent by
* the browser, and the old session will be queued for deletion by the garbage collector.
*
* @uses apply_filters Calls `wp_session_expiration_variant` to get the max update window for session data.
* @uses apply_filters Calls `wp_session_expiration` to get the standard expiration time for sessions.
*/
protected function set_expiration() {
$this->exp_variant = time() + (int) apply_filters( 'wp_session_expiration_variant', 24 * 60 );
$this->expires = time() + (int) apply_filters( 'wp_session_expiration', 30 * 60 );
}
/**
* Set the session cookie
*/
protected function set_cookie() {
setcookie( WP_SESSION_COOKIE, $this->session_id . '||' . $this->expires . '||' . $this->exp_variant , $this->expires, COOKIEPATH, COOKIE_DOMAIN );
}
/**
* Generate a cryptographically strong unique ID for the session token.
*
* @return string
*/
protected function generate_id() {
require_once( ABSPATH . 'wp-includes/class-phpass.php');
$hasher = new PasswordHash( 8, false );
return md5( $hasher->get_random_bytes( 32 ) );
}
/**
* Read data from a transient for the current session.
*
* Automatically resets the expiration time for the session transient to some time in the future.
*
* @return array
*/
protected function read_data() {
$this->container = get_option( "_wp_session_{$this->session_id}", array() );
return $this->container;
}
/**
* Write the data from the current session to the data storage system.
*/
public function write_data() {
$option_key = "_wp_session_{$this->session_id}";
// Only write the collection to the DB if it's changed.
if ( $this->dirty ) {
if ( false === get_option( $option_key ) ) {
add_option( "_wp_session_{$this->session_id}", $this->container, '', 'no' );
add_option( "_wp_session_expires_{$this->session_id}", $this->expires, '', 'no' );
} else {
delete_option( "_wp_session_{$this->session_id}" );
add_option( "_wp_session_{$this->session_id}", $this->container, '', 'no' );
}
}
}
/**
* Output the current container contents as a JSON-encoded string.
*
* @return string
*/
public function json_out() {
return json_encode( $this->container );
}
/**
* Decodes a JSON string and, if the object is an array, overwrites the session container with its contents.
*
* @param string $data
*
* @return bool
*/
public function json_in( $data ) {
$array = json_decode( $data );
if ( is_array( $array ) ) {
$this->container = $array;
return true;
}
return false;
}
/**
* Regenerate the current session's ID.
*
* @param bool $delete_old Flag whether or not to delete the old session data from the server.
*/
public function regenerate_id( $delete_old = false ) {
if ( $delete_old ) {
delete_option( "_wp_session_{$this->session_id}" );
}
$this->session_id = $this->generate_id();
$this->set_cookie();
}
/**
* Check if a session has been initialized.
*
* @return bool
*/
public function session_started() {
return !!self::$instance;
}
/**
* Return the read-only cache expiration value.
*
* @return int
*/
public function cache_expiration() {
return $this->expires;
}
/**
* Flushes all session variables.
*/
public function reset() {
$this->container = array();
}
/*****************************************************************/
/* Iterator Implementation */
/*****************************************************************/
/**
* Current position of the array.
*
* @link http://php.net/manual/en/iterator.current.php
*
* @return mixed
*/
public function current() {
return current( $this->container );
}
/**
* Key of the current element.
*
* @link http://php.net/manual/en/iterator.key.php
*
* @return mixed
*/
public function key() {
return key( $this->container );
}
/**
* Move the internal point of the container array to the next item
*
* @link http://php.net/manual/en/iterator.next.php
*
* @return void
*/
public function next() {
next( $this->container );
}
/**
* Rewind the internal point of the container array.
*
* @link http://php.net/manual/en/iterator.rewind.php
*
* @return void
*/
public function rewind() {
reset( $this->container );
}
/**
* Is the current key valid?
*
* @link http://php.net/manual/en/iterator.rewind.php
*
* @return bool
*/
public function valid() {
return $this->offsetExists( $this->key() );
}
/*****************************************************************/
/* Countable Implementation */
/*****************************************************************/
/**
* Get the count of elements in the container array.
*
* @link http://php.net/manual/en/countable.count.php
*
* @return int
*/
public function count() {
return count( $this->container );
}
}

View File

@@ -0,0 +1,169 @@
<?php
/**
* WordPress session managment.
*
* Standardizes WordPress session data and uses either database transients or in-memory caching
* for storing user session information.
*
* @package WordPress
* @subpackage Session
* @since 3.7.0
*/
/**
* Return the current cache expire setting.
*
* @return int
*/
function wp_session_cache_expire() {
$wp_session = WP_Session::get_instance();
return $wp_session->cache_expiration();
}
/**
* Alias of wp_session_write_close()
*/
function wp_session_commit() {
wp_session_write_close();
}
/**
* Load a JSON-encoded string into the current session.
*
* @param string $data
*/
function wp_session_decode( $data ) {
$wp_session = WP_Session::get_instance();
return $wp_session->json_in( $data );
}
/**
* Encode the current session's data as a JSON string.
*
* @return string
*/
function wp_session_encode() {
$wp_session = WP_Session::get_instance();
return $wp_session->json_out();
}
/**
* Regenerate the session ID.
*
* @param bool $delete_old_session
*
* @return bool
*/
function wp_session_regenerate_id( $delete_old_session = false ) {
$wp_session = WP_Session::get_instance();
$wp_session->regenerate_id( $delete_old_session );
return true;
}
/**
* Start new or resume existing session.
*
* Resumes an existing session based on a value sent by the _wp_session cookie.
*
* @return bool
*/
function wp_session_start() {
$wp_session = WP_Session::get_instance();
do_action( 'wp_session_start' );
return $wp_session->session_started();
}
add_action( 'plugins_loaded', 'wp_session_start' );
/**
* Return the current session status.
*
* @return int
*/
function wp_session_status() {
$wp_session = WP_Session::get_instance();
if ( $wp_session->session_started() ) {
return PHP_SESSION_ACTIVE;
}
return PHP_SESSION_NONE;
}
/**
* Unset all session variables.
*/
function wp_session_unset() {
$wp_session = WP_Session::get_instance();
$wp_session->reset();
}
/**
* Write session data and end session
*/
function wp_session_write_close() {
$wp_session = WP_Session::get_instance();
$wp_session->write_data();
do_action( 'wp_session_commit' );
}
add_action( 'shutdown', 'wp_session_write_close' );
/**
* Clean up expired sessions by removing data and their expiration entries from
* the WordPress options table.
*
* This method should never be called directly and should instead be triggered as part
* of a scheduled task or cron job.
*/
function wp_session_cleanup() {
global $wpdb;
if ( defined( 'WP_SETUP_CONFIG' ) ) {
return;
}
if ( ! defined( 'WP_INSTALLING' ) ) {
$expiration_keys = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE option_name LIKE '_wp_session_expires_%'" );
$now = time();
$expired_sessions = array();
foreach( $expiration_keys as $expiration ) {
// If the session has expired
if ( $now > intval( $expiration->option_value ) ) {
// Get the session ID by parsing the option_name
$session_id = substr( $expiration->option_name, 20 );
$expired_sessions[] = $expiration->option_name;
$expired_sessions[] = "_wp_session_$session_id";
}
}
// Delete all expired sessions in a single query
if ( ! empty( $expired_sessions ) ) {
$option_names = implode( "','", $expired_sessions );
$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name IN ('$option_names')" );
}
}
// Allow other plugins to hook in to the garbage collection process.
do_action( 'wp_session_cleanup' );
}
add_action( 'wp_session_garbage_collection', 'wp_session_cleanup' );
/**
* Register the garbage collector as a twice daily event.
*/
function wp_session_register_garbage_collection() {
if ( ! wp_next_scheduled( 'wp_session_garbage_collection' ) ) {
wp_schedule_event( time(), 'hourly', 'wp_session_garbage_collection' );
}
}
add_action( 'wp', 'wp_session_register_garbage_collection' );

View File

@@ -0,0 +1,64 @@
<?php
class ClefPro {
const GET_PRO_SERVICES_ACTION = "clef_get_pro_services";
private static $instance = null;
private function __construct($settings) {
$this->settings = $settings;
$this->initialize_hooks();
}
public function initialize_hooks() {
global $clef_ajax;
$clef_ajax->add_action(self::GET_PRO_SERVICES_ACTION, array($this, 'ajax_get_pro_services'));
}
public function add_settings($form) {
$customization = $form->addSection('customization', __('Customization', 'clef'));
$customization->addField(
'message',
__('Message for login page', 'clef'),
Settings_API_Util_Field::TYPE_TEXTAREA
);
$customization->addField(
'logo',
__('Logo for login page', 'clef'),
Settings_API_Util_Field::TYPE_HIDDEN
);
}
public function ajax_get_pro_services() {
$args = array(
'app_id' => $this->settings->get('clef_settings_app_id'),
'app_secret' => $this->settings->get('clef_settings_app_secret')
);
$response = wp_remote_post(
CLEF_API_BASE . 'app/info',
array( 'method' => 'POST', 'body' => $args, 'timeout' => 20 )
);
if ( is_wp_error($response) ) {
return $response;
} else {
$body = json_decode($response['body'], true);
if (isset($body['success']) && $body['success']) {
return $body['services'];
} else {
return new WP_Error($body['error']);
}
}
}
public static function start($settings= array()) {
if (!isset(self::$instance) || self::$instance === null) {
self::$instance = new self($settings);
}
return self::$instance;
}
}