Skip to content

Commit

Permalink
Add guest sessions (#2686)
Browse files Browse the repository at this point in the history
  • Loading branch information
yscik authored Dec 19, 2023
1 parent da757b6 commit 19ae17e
Show file tree
Hide file tree
Showing 11 changed files with 684 additions and 19 deletions.
24 changes: 22 additions & 2 deletions assets/css/ui.notice.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
border: var(--jm-ui-border-size) solid var(--jm-ui-border-light);
background: var(--jm-ui-background-color, transparent);
color: var(--jm-ui-text-color, inherit);
padding: var(--jm-ui-space-m);
padding: var(--jm-ui-space-m) var(--jm-ui-space-ml);
display: flex;
flex-direction: column;
gap: var(--jm-ui-space-xs);
Expand All @@ -23,9 +23,12 @@
margin: var(--jm-ui-space-ml) auto;

&.has-header {
padding: var(--jm-ui-space-ml) var(--jm-ui-space-m);
padding: var(--jm-ui-space-ml) var(--jm-ui-space-ml);
gap: var(--jm-ui-space-sm);
}
&.has-header.has-actions {
gap: var(--jm-ui-space-m);
}

@each $color in (info, error, success, strong) {
&.color-#{$color} {
Expand Down Expand Up @@ -68,6 +71,23 @@
font-size: 16px;
}
}

&.type-dialog {
align-items: center;

padding: var(--jm-ui-space-xxl) var(--jm-ui-space-ml);
gap: var(--jm-ui-space-ml);

.jm-notice__buttons {
gap: var(--jm-ui-space-sm);
}
.jm-notice__button,
.jm-notice__button--outline,
.jm-notice__button--link,
{
padding: var(--jm-ui-space-s2) var(--jm-ui-space-sm);
}
}
}

.jm-notice__icon {
Expand Down
110 changes: 110 additions & 0 deletions includes/class-guest-session.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
/**
* File containing the class Guest_Session.
*
* @package wp-job-manager
*/

namespace WP_Job_Manager;

/**
* Manage the guest user session.
*
* @since $$next-version$$
*/
class Guest_Session {

use Singleton;

const COOKIE_NAME = 'jm-guest-user';
const COOKIE_EXPIRY = 1 * DAY_IN_SECONDS;
const QUERY_VAR = 'jmtoken';

/**
* The current guest user.
*
* @var Guest_User|false
*/
private $user = false;

/**
* Whether the session has been initialized.
*
* @var bool
*/
private bool $initialized = false;

/**
* Get the guest user for the current session.
*
* @return Guest_User|false
*/
public static function get_current_guest() {
$session = self::instance();
$session->init();

return $session->user;
}

/**
* Initialize the guest session based on URL token or session cookie.
* If only a URL token is present, it will be set as a session cookie.
*
* @return void
*/
private function init() {

if ( $this->initialized ) {
return;
}

$this->initialized = true;

$token = $this->get_url_token();
$cookie = $this->get_cookie();
$token = $token ?? $cookie;

if ( ! $token ) {
return;
}

$guest = Guest_User::get_by_token( $token );

$this->user = $guest;

if ( $guest && $cookie !== $token ) {
$this->set_cookie( $token );
}
}

/**
* Get the token from a session cookie.
*
* @return string|null
*/
private function get_cookie() {
return isset( $_COOKIE[ self::COOKIE_NAME ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ self::COOKIE_NAME ] ) ) : null;
}

/**
* Set the token as a session cookie.
*
* @param string $token
*/
private function set_cookie( $token ) {
$secure = 'https' === wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME );
$expire = time() + self::COOKIE_EXPIRY;
setcookie( self::COOKIE_NAME, $token, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure, true );
}

/**
* Get the token from the URL query string.
*
* @return string|null
*/
private static function get_url_token() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- URL token-based login action.
return isset( $_GET[ self::QUERY_VAR ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::QUERY_VAR ] ) ) : null;
}

}
166 changes: 166 additions & 0 deletions includes/class-guest-user.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php
/**
* File containing the class Guest_User.
*
* @package wp-job-manager
*/

namespace WP_Job_Manager;

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* A guest user is a site visitors without a full account, identified by their email address.
*
* @since $$next-version$$
*/
class Guest_User {
const TOKEN_EXPIRY = 35 * DAY_IN_SECONDS;
const TOKEN_PREFIX = 'jm';
const TOKEN_SEPARATOR = '-';

/**
* The post object for the guest user.
*
* @var \WP_Post
*/
private $post;

/**
* The post ID.
*
* @readonly
* @var int
*/
public int $ID;

/**
* The user email address.
*
* @readonly
* @var string
*/
public string $user_email;

/**
* Constructor.
*
* @param \WP_Post $guest The guest user post object.
*/
public function __construct( \WP_Post $guest ) {
$this->post = $guest;
$this->ID = $guest->ID;
$this->user_email = $guest->post_title;
}

/**
* The post object for the guest user.
*
* @param int|string $guest_id Post ID of guest user, or their email.
*
* @return self|false
*/
public static function load( $guest_id ) {

if ( is_numeric( $guest_id ) ) {
$guest = get_post( $guest_id );
} elseif ( is_email( $guest_id ) ) {
$email = sanitize_email( $guest_id );
$guest = get_posts(
[
'post_type' => \WP_Job_Manager_Post_Types::PT_GUEST_USER,
'title' => $email,
]
);
$guest = empty( $guest ) ? false : $guest[0];
}

if ( empty( $guest ) ) {
return false;
}

return new self( $guest );
}

/**
* Create a new guest user for the given email. Returns existing guest user if one exists for the email.
*
* @param string $email
*
* @return self|false
*/
public static function create( string $email ) {

$email = sanitize_email( $email );

if ( ! is_email( $email ) ) {
return false;
}

$existing = self::load( $email );

if ( $existing ) {
return $existing;
}

$guest_id = wp_insert_post(
[
'post_title' => $email,
'post_type' => \WP_Job_Manager_Post_Types::PT_GUEST_USER,
'post_status' => 'publish',
]
);

return self::load( $guest_id );

}

/**
* Get a URL token for the guest user. Includes the guest user ID and an access token.
*
* @return string
*/
public function create_token() {

$at = new Access_Token( [ 'guest_id' => $this->ID ] );
$access_token = $at->create( time() + self::TOKEN_EXPIRY );

return implode( self::TOKEN_SEPARATOR, [ self::TOKEN_PREFIX, $this->ID, $access_token ] );

}

/**
* Verify a URL token and return the guest user if valid.
*
* @param string $token URL token.
*
* @return Guest_User|false
*/
public static function get_by_token( $token ) {

if ( ! is_string( $token ) ) {
return false;
}

$token_parts = explode( self::TOKEN_SEPARATOR, $token );
if ( count( $token_parts ) !== 3 ) {
return false;
}

[ $prefix, $guest_id, $access_token ] = $token_parts;

if ( self::TOKEN_PREFIX !== $prefix ) {
return false;
}

$at = new Access_Token( [ 'guest_id' => (int) $guest_id ] );

if ( ! $at->verify( $access_token ) ) {
return false;
}

return self::load( $guest_id );
}
}
6 changes: 3 additions & 3 deletions includes/class-wp-job-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static function instance() {
*/
public function __construct() {
// Includes.
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/trait-singleton.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-install.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-post-types.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-ajax.php';
Expand All @@ -77,9 +78,8 @@ public function __construct() {
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-com-api.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/promoted-jobs/class-wp-job-manager-promoted-jobs.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-access-token.php';

include_once JOB_MANAGER_PLUGIN_DIR . '/includes/trait-singleton.php';

include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-guest-user.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-guest-session.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/ui/class-ui.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/ui/class-ui-settings.php';

Expand Down
Loading

0 comments on commit 19ae17e

Please sign in to comment.