Xác định quyền và vai trò khi phát triển Plugins WordPress

Để phát triển Plugins một cách hiệu quả, bảo mật, ta cần xác định nhiều yếu tố ngay từ khi bắt tay vào xây dựng chúng, như đơn cử trong bài 1, Plugins cần được tạo từ 3 thành phần

  • Plugins Header: Chứa nhiều thông tay xoay quanh gói ứng dụng đó
  • Các hàm callback được chạy khi kích hoạt hoặc ngừng kích hoạt ứng dụng (tùy vào logic và bối cảnh sử dụng)
  • uninstall.php hoặc uninstall callback: nhằm nhiệm vụ xóa toàn bộ mọi dấu tích của gói mở rộng (bao gồm cả việc tác động vào bảng dữ liệu)

Tiếp đó, ta cần xây dựng các logic nhằm đảm bảo tính bảo mật của các plugins, tránh bị tấn công hoặc khai thác lỗ hổng. Dưới đây là 1 số hàm rất quan trọng mà trong suốt quá trình phát triển plugins chúng ta sẽ cần lưu ý.

Kiểm tra vai trò và quyền

Dưới đây là 1 số ví dụ, hình dung chúng ta sẽ có 1 hệ thống các cấp từ cao xuống thấp

  • Admin
  • Editor (Người biên tập)
  • Author (Tác giả)
  • Contributor (người đóng góp)
  • Người đăng ký (Subscriber)

Không có hạn chế về quyền

/**
 * Generate a Delete link based on the homepage url.
 *
 * @param string $content   Existing content.
 *
 * @return string|null
 */
function wporg_generate_delete_link( $content ) {
	// Run only for single post page.
	if ( is_single() && in_the_loop() && is_main_query() ) {
		// Add query arguments: action, post.
		$url = add_query_arg(
			[
				'action' => 'wporg_frontend_delete',
				'post'   => get_the_ID(),
			], home_url()
		);

		return $content . ' <a href="' . esc_url( $url ) . '">' . esc_html__( 'Delete Post', 'wporg' ) . '</a>';
	}

	return null;
}


/**
 * Request handler
 */
function wporg_delete_post() {
	if ( isset( $_GET['action'] ) && 'wporg_frontend_delete' === $_GET['action'] ) {

		// Verify we have a post id.
		$post_id = ( isset( $_GET['post'] ) ) ? ( $_GET['post'] ) : ( null );

		// Verify there is a post with such a number.
		$post = get_post( (int) $post_id );
		if ( empty( $post ) ) {
			return;
		}

		// Delete the post.
		wp_trash_post( $post_id );

		// Redirect to admin page.
		$redirect = admin_url( 'edit.php' );
		wp_safe_redirect( $redirect );

		// We are done.
		die;
	}
}


/**
 * Add the delete link to the end of the post content.
 */
add_filter( 'the_content', 'wporg_generate_delete_link' );

/**
 * Register our request handler with the init hook.
 */
add_action( 'init', 'wporg_delete_post' );

Dưới đây là một đoạn code cài vào hook init hàm wporg_delete_post

  • Tại hàm wporg_generate_delete_link($content) ta tạo link với url có action là ‘wporg_frontend_delete’ và bắt theo id của bài viết.
  • Tiếp đó ta thực hiện logic với action là ‘wporg_frontend_delete’, kiểm tra post id có tồn tại không, tiến hành xóa bài viết cụ thể đó & đẩy về trang edit.php (trang edit bài viết)
  • Sửa hook the_content, đẩy liên kết xóa vào bài viết (có đoạn return $content . ‘ <a href=”‘ . esc_url( $url ) . ‘”>’ . esc_html__( ‘Delete Post’, ‘wporg’ ) . ‘</a>’; tức là ta chèn link xóa bài viết vào cuối bài)

Có hạn chế về quyền

/**
 * Generate a Delete link based on the homepage url.
 *
 * @param string $content   Existing content.
 *
 * @return string|null
 */
function wporg_generate_delete_link( $content ) {
	// Run only for single post page.
	if ( is_single() && in_the_loop() && is_main_query() ) {
		// Add query arguments: action, post.
		$url = add_query_arg(
			[
				'action' => 'wporg_frontend_delete',
				'post'   => get_the_ID(),
			], home_url()
		);

		return $content . ' <a href="' . esc_url( $url ) . '">' . esc_html__( 'Delete Post', 'wporg' ) . '</a>';
	}

	return null;
}


/**
 * Request handler
 */
function wporg_delete_post() {
	if ( isset( $_GET['action'] ) && 'wporg_frontend_delete' === $_GET['action'] ) {

		// Verify we have a post id.
		$post_id = ( isset( $_GET['post'] ) ) ? ( $_GET['post'] ) : ( null );

		// Verify there is a post with such a number.
		$post = get_post( (int) $post_id );
		if ( empty( $post ) ) {
			return;
		}

		// Delete the post.
		wp_trash_post( $post_id );

		// Redirect to admin page.
		$redirect = admin_url( 'edit.php' );
		wp_safe_redirect( $redirect );

		// We are done.
		die;
	}
}


/**
 * Add delete post ability
 */
add_action('plugins_loaded', 'wporg_add_delete_post_ability');
 
function wporg_add_delete_post_ability() {    
    if ( current_user_can( 'edit_others_posts' ) ) {
        /**
         * Add the delete link to the end of the post content.
         */
        add_filter( 'the_content', 'wporg_generate_delete_link' );
      
        /**
         * Register our request handler with the init hook.
         */
        add_action( 'init', 'wporg_delete_post' );
    }
}

Bạn để ý thì với logic tương tự, nhưng hàm này chỉ chạy khi người dùng có quyền “edit_others_posts” mà thôi (Từ cấp editors trở lên)

Cách tạo một quyền mới

function wporg_simple_role() {
	add_role(
		'simple_role',
		'Simple Role',
		array(
			'read'         => true,
			'edit_posts'   => true,
			'upload_files' => true,
		),
	);
}

// Add the simple_role.
add_action( 'init', 'wporg_simple_role' );

Như đã thấy ở đây thì ta có thể truyền vào hook init một hàm wporg_simple_role với hàm được viết sẵn add_role với tên, label là gì và nó có những quyền gì?

Tạo thêm những quyền mới

function wporg_simple_role_caps() {
	// Gets the simple_role role object.
	$role = get_role( 'simple_role' );

	// Add a new capability.
	$role->add_cap( 'edit_others_posts', true );
}

// Add simple_role capabilities, priority must be after the initial role definition.
add_action( 'init', 'wporg_simple_role_caps', 11 );

Tuy vậy theo Nam thì để phát triển plugins, themes một cách đơn giản, ta có thể sử dụng các Plugins phụ trợ dành cho Dev như User Role Editor

Nếu bạn ưa thích việc tự lập trình thì trong Plugin Handbook cũng gợi ý một số hàm rất tiện lợi như:

  • Get Role: get_role($role)
  • Tạo thêm các quyền như xem bài viết, đăng bài… user_can( $user, $capability );

Current User can

Trong việc phát triển themes, bạn cũng có thể định nghĩa các điều kiện nếu người dùng hiện tại có khả năng làm gì, thì ta sẽ bổ sung nội dung mong muốn, ví dụ như:

if ( current_user_can( 'edit_posts' ) ) {
	edit_post_link( esc_html__( 'Edit', 'wporg' ), '<p>', '</p>' );
}

Nếu người dùng có thể edit_posts thì ngay tại đoạn đó ta đẩy ra một link để người dùng có thể sửa bài ngay.

Cách tùy biến các trường thông tin User

Về bản chất thì người dùng sẽ lưu trữ thông tin tại bảng wp_users, ta cũng có thể dễ dàng bổ sung các trường có sẵn, hãy cùng tham khảo trong plugin handbook để đẩy ra dữ liệu người dùng

WordPress hỗ trợ 2 cách để sửa thông tin người dùng, 1 là qua wp-admin dashboard, 2 là sử dụng cách như các mã nguồn code tay thông thường,

Sử dụng Wp-admin Dashboard

/**
 * The field on the editing screens.
 *
 * @param $user WP_User user object
 */
function wporg_usermeta_form_field_birthday( $user ) {
    ?>
    <h3>It's Your Birthday</h3>
    <table class="form-table">
        <tr>
            <th>
                <label for="birthday">Birthday</label>
            </th>
            <td>
                <input type="date"
                       class="regular-text ltr"
                       id="birthday"
                       name="birthday"
                       value="<?= esc_attr( get_user_meta( $user->ID, 'birthday', true ) ) ?>"
                       title="Please use YYYY-MM-DD as the date format."
                       pattern="(19[0-9][0-9]|20[0-9][0-9])-(1[0-2]|0[1-9])-(3[01]|[21][0-9]|0[1-9])"
                       required>
                <p class="description">
                    Please enter your birthday date.
                </p>
            </td>
        </tr>
    </table>
    <?php
}
 
/**
 * The save action.
 *
 * @param $user_id int the ID of the current user.
 *
 * @return bool Meta ID if the key didn't exist, true on successful update, false on failure.
 */
function wporg_usermeta_form_field_birthday_update( $user_id ) {
    // check that the current user have the capability to edit the $user_id
    if ( ! current_user_can( 'edit_user', $user_id ) ) {
        return false;
    }
 
    // create/update user meta for the $user_id
    return update_user_meta(
        $user_id,
        'birthday',
        $_POST['birthday']
    );
}
 
// Add the field to user's own profile editing screen.
add_action(
    'show_user_profile',
    'wporg_usermeta_form_field_birthday'
);
 
// Add the field to user profile editing screen.
add_action(
    'edit_user_profile',
    'wporg_usermeta_form_field_birthday'
);
 
// Add the save action to user's own profile editing screen update.
add_action(
    'personal_options_update',
    'wporg_usermeta_form_field_birthday_update'
);
 
// Add the save action to user profile editing screen update.
add_action(
    'edit_user_profile_update',
    'wporg_usermeta_form_field_birthday_update'
);

Ở đoạn này khá dài dòng, tuy vậy hiểu đơn giản là ta đã đẩy 1 form để người dùng cập nhật ngày sinh nhật và cắm vào hook edit_user_profile.

Khi bấm save thì sẽ kích hoạt hook personal_options_update và ta truyền vào đó một hàm wporg_usermeta_form_field_birthday_update, sau đó ta sẽ áp dụng hàm save ở 2 hook

  • personal_options_update
  • edit_user_profile_update

Tự xây dựng logic như mã nguồn code tay thông thường

WordPress cung cấp các hàm sửa xóa đa dạng cho việc khởi tạo người dùng, điều này rất tiện lợi cho việc xây dựng các custom dashboard dành riêng cho khách hàng, đây là tính năng Nam đang thắc mắc bởi đang xây dựng cho client khá nhiều những logic, tạo user mà người dùng không trực tiếp vào wp-admin

Tạo mới

add_user_meta(
    int $user_id,
    string $meta_key,
    mixed $meta_value,
    bool $unique = false
);

Cập nhật

update_user_meta(
    int $user_id,
    string $meta_key,
    mixed $meta_value,
    mixed $prev_value = ''
);

Xóa

delete_user_meta(
    int $user_id,
    string $meta_key,
    mixed $meta_value = ''
);

Lấy thông tin người dùng

get_user_meta(
    int $user_id,
    string $key = '',
    bool $single = false
);

Giả dụ bạn muốn hiển thị ở dưới chân trang 1 đoạn chứa thông tin tác giả, bạn có thể dùng hàm như sau:

   <!-- Author & Bio -->
   <div class="blog-author d-flex align-items-center">
                        <div class="flex-shrink-0 me-3">
                            <?php echo get_avatar( get_the_author_meta('email'), '80', $default='', $alt='', array( 'class' => array( 'img-thumbnail rounded-circle' ) ) ); ?>
                        </div>
                        <div class="author-bio">
                            <h4><?php the_author_posts_link(); ?></h4>
                            <?php the_author_meta('description'); ?>
                        </div>
</div>

Cách làm việc với Users

Tạo người dùng mới

// check if the username is taken
$user_id = username_exists( $user_name );

// check that the email address does not belong to a registered user
if ( ! $user_id && email_exists( $user_email ) === false ) {
	// create a random password
	$random_password = wp_generate_password( 12, false );
	// create the user
	$user_id = wp_create_user(
		$user_name,
		$random_password,
		$user_email
	);
}

Hàm này ta hoàn toàn có thể truyền vào 1 hook khi submit thông tin nhằm tạo người dùng mới, nên nhớ đây chỉ là 1 đoạn trong function thôi nên trường $user_email, $user_name có thể được truyền vào form khi submit

Tạo người dùng dạng Insert

$username  = $_POST['username'];
$password  = $_POST['password'];
$website   = $_POST['website'];
$user_data = [
	'user_login' => $username,
	'user_pass'  => $password,
	'user_url'   => $website,
];

$user_id = wp_insert_user( $user_data );

// success
if ( ! is_wp_error( $user_id ) ) {
	echo 'User created: ' . $user_id;
}

Hình dung khi bạn cập nhật và gửi lệnh post cho các fields với tên là

  • username
  • password
  • website

Nếu người dùng không trùng lặp, form sẽ tạo mới người dùng

Một ví dụ rất ổn từ Copilot AI

Đoạn này mình thấy rất tốt nên đã copy vào để đỡ bỏ lỡ ý hay

Certainly! Handling form submissions in WordPress involves creating a custom form, processing the submitted data, and ensuring proper validation. Let’s dive into how you can achieve this using PHP and WordPress.

  1. Creating the Form:
  2. Handling Form Submissions in WordPress:
  3. Example Implementation:
    • Suppose you want to handle a custom form submission in a WordPress plugin:
      • Create a form in the admin area of your plugin.
      • Specify a custom action name (e.g., “my_custom_form_action”) in the form.
      • In your plugin code, add the following:// Hook into the form submission add_action('admin_post_my_custom_form_action', 'handle_custom_form_submission'); add_action('admin_post_nopriv_my_custom_form_action', 'handle_custom_form_submission'); // Form handler function function handle_custom_form_submission() { // Retrieve form data $name = sanitize_text_field($_POST['name']); $email = sanitize_email($_POST['email']); // Process the data (e.g., save to database, send emails, etc.) // Redirect back to the form page wp_redirect(admin_url('admin.php?page=my-plugin-page&success=true')); exit; }
    • Adjust the code according to your specific requirements and plugin structure.

Remember that this approach is applicable not only to admin forms but also to public-facing areas of your WordPress site. If you need further assistance or have additional questions, feel free to ask! 🌟