File "rest-api.php"

Full path: /home/webcknlt/admissiontell.com/wp-content/plugins/publishpress-authors/src/modules/rest-api/rest-api.php
File size: 22.85 B (22.85 KB bytes)
MIME-type: text/x-php
Charset: utf-8

Download   Open   Edit   Advanced Editor &nnbsp; Back

<?php
/**
 * @package PublishPress Authors
 * @author  PublishPress
 *
 * Copyright (C) 2018 PublishPress
 *
 * This file is part of PublishPress Authors
 *
 * PublishPress Authors is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * PublishPress is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PublishPress.  If not, see <http://www.gnu.org/licenses/>.
 */

use MultipleAuthors\Classes\Legacy\Module;
use MultipleAuthors\Classes\Legacy\Util;
use MultipleAuthors\Classes\Objects\Author;
use MultipleAuthors\Classes\Author_Editor;
use MultipleAuthors\Factory;

if (!class_exists('MA_REST_API')) {
    /**
     * class MA_REST_API
     */
    class MA_REST_API extends Module
    {
        const SETTINGS_SLUG = 'ppma-settings';

        public $module_name = 'rest_api';

        /**
         * Instance for the module
         *
         * @var stdClass
         */
        public $module;
        public $module_url;


        /**
         * Construct the MA_REST_API class
         */
        public function __construct()
        {
            $this->module_url = $this->get_module_url(__FILE__);

            // Register the module with PublishPress
            $args = [
                'title' => __('Rest API', 'publishpress-authors'),
                'short_description' => __('Rest API support', 'publishpress-authors'),
                'extended_description' => __('Rest API support', 'publishpress-authors'),
                'module_url' => $this->module_url,
                'icon_class' => 'dashicons dashicons-feedback',
                'slug' => 'rest-api',
                'default_options' => [
                    'enabled' => 'on'
                ],
                'options_page' => false,
                'autoload' => true,
            ];

            // Apply a filter to the default options
            $args['default_options'] = apply_filters('pp_rest_api_default_options', $args['default_options']);

            $legacyPlugin = Factory::getLegacyPlugin();

            $this->module = $legacyPlugin->register_module($this->module_name, $args);

            parent::__construct();
        }

        /**
         * Initialize the module. Conditionally loads if the module is enabled
         */
        public function init()
        {
            add_action('rest_api_init', [$this, 'initRestAPI']);
        }

        public function initRestAPI()
        {
            register_rest_field(
                'post',
                'authors',
                [
                    'get_callback' => [$this, 'getPostAuthorsCallback'],
                    'schema' => [
                        'description' => __('Authors.'),
                        'type' => 'array'
                    ],
                ]
            );

            register_rest_route('publishpress-authors/v1', '/authors', [
                [
                    'methods' => 'POST',
                    'callback' => [$this, 'createAuthorCallback'],
                    'permission_callback' => [$this, 'checkCreatePermissions'],
                    'args' => $this->getCreateAuthorArgs()
                ]
            ]);

            register_rest_route('publishpress-authors/v1', '/authors/(?P<id>\d+)', [
                [
                    'methods' => 'GET',
                    'callback' => [$this, 'getAuthorCallback'],
                    'permission_callback' => [$this, 'checkReadPermissions'],
                    'args' => [
                        'id' => [
                            'required' => true,
                            'type' => 'integer',
                            'sanitize_callback' => 'absint'
                        ]
                    ]
                ],
                [
                    'methods' => 'PUT',
                    'callback' => [$this, 'updateAuthorCallback'],
                    'permission_callback' => [$this, 'checkUpdatePermissions'],
                    'args' => $this->getUpdateAuthorArgs()
                ],
                [
                    'methods' => 'PATCH',
                    'callback' => [$this, 'patchAuthorCallback'],
                    'permission_callback' => [$this, 'checkUpdatePermissions'],
                    'args' => $this->getPatchAuthorArgs()
                ]
            ]);
        }

        public function checkCreatePermissions($request)
        {
            return current_user_can('ppma_manage_authors');
        }

        public function checkUpdatePermissions($request)
        {
            return current_user_can('ppma_manage_authors');
        }

        public function checkReadPermissions($request)
        {
            return true;
        }

        public function getCreateAuthorArgs()
        {
            return [
                'display_name' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                    'validate_callback' => function ($param) {
                        return !empty(trim($param));
                    }
                ],
                'user_email' => [
                    'required' => false,
                    'type' => 'string',
                    'format' => 'email',
                    'sanitize_callback' => 'sanitize_email'
                ],
                'user_id' => [
                    'required' => false,
                    'type' => 'integer',
                    'sanitize_callback' => 'absint'
                ],
                'slug' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_title'
                ],
                'author_fields' => [
                    'required' => false,
                    'type' => 'object',
                    'sanitize_callback' => function ($param) {
                        return is_array($param) ? $param : [];
                    }
                ]
            ];
        }

        public function getUpdateAuthorArgs()
        {
            $args = $this->getCreateAuthorArgs();
            $args['display_name']['required'] = false;
            return $args;
        }

        public function getPatchAuthorArgs()
        {
            return $this->getUpdateAuthorArgs();
        }

        public function getPostAuthorsCallBack($postData)
        {
            $legacyPlugin = Factory::getLegacyPlugin();

            $selectedPostTypes = array_values(Util::get_post_types_for_module($legacyPlugin->modules->multiple_authors));

            $post = get_post($postData['id']);

            if (!in_array($post->post_type, $selectedPostTypes)) {
                return [];
            }

            $authors = get_post_authors($postData['id']);

            $authorsData = [];

            $authors_fields = Author_Editor::get_fields(false);
            $authors_fields = apply_filters('multiple_authors_author_fields', $authors_fields, false);
            $authors_fields = array_keys($authors_fields);

            $excluded_fields = ['user_id', 'user_email', 'avatar'];
            $excluded_fields = apply_filters('ppma_rest_api_authors_meta_excluded_fields', $excluded_fields);

            foreach ($authors as $author) {
                $isGuest = 0;
                if (is_a($author, Author::class)) {
                    $isGuest = $author->is_guest() ? 1 : 0;
                }

                if (!is_object($author) || is_wp_error($author)) {
                    continue;
                }

                //add default fields
                $currentAuthorData = [
                    'term_id' => (int) $author->term_id,
                    'user_id' => (int) $author->user_id,
                    'is_guest' => $isGuest,
                    'slug' => $author->slug,
                    'display_name' => $author->display_name,
                    'avatar_url' => $author->get_avatar_url(),
                ];

                //add other fields
                foreach ($authors_fields as $authors_field) {
                    if (in_array($authors_field, $excluded_fields)) {
                        continue;
                    }
                    $currentAuthorData[$authors_field] = $author->$authors_field;
                }

                $authorsData[] = $currentAuthorData;
            }

            return $authorsData = apply_filters('ppma_rest_api_authors_data', $authorsData);
        }
        public function createAuthorCallback($request)
        {
            $legacyPlugin = Factory::getLegacyPlugin();
            $enable_guest_author_user = $legacyPlugin->modules->multiple_authors->options->enable_guest_author_user === 'yes';
            $enable_guest_author_account = $legacyPlugin->modules->multiple_authors->options->enable_guest_author_acount === 'yes';

            $params = $request->get_params();
            $display_name = $params['display_name'];
            $user_email = !empty($params['user_email']) ? $params['user_email'] : '';
            $user_id = !empty($params['user_id']) ? $params['user_id'] : 0;
            $author_fields = !empty($params['author_fields']) ? $params['author_fields'] : [];

            if ($user_id > 0) {
                $author_type = 'existing_user';
            } elseif (!empty($user_email)) {
                $author_type = 'new_user';
            } else {
                $author_type = 'guest_author';
            }

            $slug = !empty($params['slug']) ? $params['slug'] : sanitize_title($display_name);

            if ($author_type === 'guest_author' && !$enable_guest_author_user) {
                return new WP_Error(
                    'guest_authors_disabled',
                    __('Guest authors without user accounts is not enabled.', 'publishpress-authors'),
                    ['status' => 400]
                );
            }

            if ($author_type === 'new_user' && !$enable_guest_author_account) {
                return new WP_Error(
                    'guest_user_accounts_disabled',
                    __('Guest author with user accounts is not enabled.', 'publishpress-authors'),
                    ['status' => 400]
                );
            }

            if (get_term_by('slug', $slug, 'author')) {
                return new WP_Error(
                    'slug_exists',
                    __('An author with this slug already exists.', 'publishpress-authors'),
                    ['status' => 409]
                );
            }

            if ($author_type === 'existing_user') {
                $existing_author = Author::get_by_user_id($user_id);
                $remove_single_user_map_restriction = $legacyPlugin->modules->multiple_authors->options->remove_single_user_map_restriction === 'yes';

                if (!$remove_single_user_map_restriction && $existing_author) {
                    return new WP_Error(
                        'user_already_mapped',
                        __('This WordPress user is already mapped to another author.', 'publishpress-authors'),
                        ['status' => 409]
                    );
                }
            }

            try {
                $mapped_user_id = 0;

                if ($author_type === 'new_user') {
                    if (!get_role('ppma_guest_author')) {
                        add_role('ppma_guest_author', 'Guest Author', []);
                    }

                    $user_data = [
                        'user_login' => $slug,
                        'display_name' => $display_name,
                        'user_email' => $user_email,
                        'user_pass' => wp_generate_password(),
                        'role' => 'ppma_guest_author',
                    ];

                    $new_user_id = wp_insert_user($user_data);

                    if (is_wp_error($new_user_id)) {
                        return new WP_Error(
                            'user_creation_failed',
                            $new_user_id->get_error_message(),
                            ['status' => 500]
                        );
                    }

                    $mapped_user_id = $new_user_id;
                } elseif ($author_type === 'existing_user') {
                    $mapped_user_id = $user_id;
                }

                $term_data = wp_insert_term($display_name, 'author', [
                    'slug' => $slug
                ]);

                if (is_wp_error($term_data)) {
                    return new WP_Error(
                        'author_creation_failed',
                        $term_data->get_error_message(),
                        ['status' => 500]
                    );
                }

                $term_id = $term_data['term_id'];

                if ($mapped_user_id > 0) {
                    update_term_meta($term_id, 'user_id', $mapped_user_id);
                    update_term_meta($term_id, 'user_id_' . $mapped_user_id, $mapped_user_id);
                }

                if (!empty($user_email)) {
                    update_term_meta($term_id, 'user_email', $user_email);
                }

                $this->setAuthorFields($term_id, $author_fields);

                $author = Author::get_by_term_id($term_id);

                if (!$author) {
                    return new WP_Error(
                        'author_retrieval_failed',
                        __('Author was created but could not be retrieved.', 'publishpress-authors'),
                        ['status' => 500]
                    );
                }

                return $this->formatAuthorResponse($author, $author_type, 201);

            } catch (Exception $e) {
                return new WP_Error(
                    'unexpected_error',
                    $e->getMessage(),
                    ['status' => 500]
                );
            }
        }

        public function getAuthorCallback($request)
        {
            $author_id = $request->get_param('id');
            $author = Author::get_by_term_id($author_id);

            if (!$author) {
                return new WP_Error(
                    'author_not_found',
                    __('Author not found.', 'publishpress-authors'),
                    ['status' => 404]
                );
            }

            return $this->formatAuthorResponse($author, 'existing', 200);
        }

        public function updateAuthorCallback($request)
        {
            $author_id = $request->get_param('id');
            $author = Author::get_by_term_id($author_id);

            if (!$author) {
                return new WP_Error(
                    'author_not_found',
                    __('Author not found.', 'publishpress-authors'),
                    ['status' => 404]
                );
            }

            $params = $request->get_params();
            $author_fields = !empty($params['author_fields']) ? $params['author_fields'] : [];

            return $this->updateExistingAuthor($author, $params, $author_fields);
        }

        public function patchAuthorCallback($request)
        {
            $author_id = $request->get_param('id');
            $author = Author::get_by_term_id($author_id);

            if (!$author) {
                return new WP_Error(
                    'author_not_found',
                    __('Author not found.', 'publishpress-authors'),
                    ['status' => 404]
                );
            }

            $params = $request->get_params();
            $author_fields = !empty($params['author_fields']) ? $params['author_fields'] : [];

            return $this->updateExistingAuthor($author, $params, $author_fields, true);
        }

        private function updateExistingAuthor($author, $params, $author_fields, $partial_update = false)
        {
            $term_id = $author->term_id;

            $author_user_id = $author->user_id;

            if (!empty($author_user_id)) {
                $user = get_user_by('id', $author_user_id);

                if ($user && (int)$author_user_id !== get_current_user_id()) {
                    // Prevent editing administrators completely
                    if (in_array('administrator', $user->roles)) {
                        return new WP_Error(
                            'cannot_edit_administrator',
                            __('You cannot edit author mapped to administrator account.', 'publishpress-authors'),
                            ['status' => 403]
                        );
                    }

                    // Check if the user lacks the necessary capabilities
                    if (!current_user_can(get_taxonomy('author')->cap->manage_terms)
                        || !current_user_can('edit_user', $author_user_id)) {
                        return new WP_Error(
                            'insufficient_permissions',
                            __('You do not have permission to edit this author.', 'publishpress-authors'),
                            ['status' => 403]
                        );
                    }
                }
            }

            // update user args data
            $updated_args = [];
            if ($author_user_id) {
                $updated_args['ID'] = $author_user_id;
            }

            // update terms args data
            $update_data = [];
            if (!empty($params['display_name'])) {
                $update_data['name'] = $params['display_name'];
                if ($author_user_id) {
                    $updated_args['display_name'] = $params['display_name'];
                }
            }

            if (!empty($params['slug'])) {
                $update_data['slug'] = sanitize_title($params['slug']);
            }

            if (!empty($update_data)) {
                $result = wp_update_term($term_id, 'author', $update_data);
                if (is_wp_error($result)) {
                    return new WP_Error(
                        'author_update_failed',
                        $result->get_error_message(),
                        ['status' => 500]
                    );
                }
            }

            // Update email and sync to user if mapped
            if (!empty($params['user_email'])) {
                update_term_meta($term_id, 'user_email', sanitize_email($params['user_email']));
                if ($author_user_id) {
                    update_user_meta($author_user_id, 'user_email', sanitize_email($params['user_email']));
                    $updated_args['user_email'] = sanitize_email($params['user_email']);
                }
            }

            $this->setAuthorFields($term_id, $author_fields, $author_user_id, $updated_args);

            if ($author_user_id && count($updated_args) > 1) {
                wp_update_user($updated_args);
            }

            // Sync slug with user nicename if mapped
            if ($author_user_id) {
                $user = get_user_by('id', $author_user_id);
                if ($user && $author->slug !== $user->user_nicename) {
                    global $wpdb;
                    $wpdb->query(
                        $wpdb->prepare(
                            "UPDATE {$wpdb->terms} SET slug=%s WHERE term_id = %d",
                            $user->user_nicename,
                            $term_id
                        )
                    );
                }
            }

            $updated_author = Author::get_by_term_id($term_id);

            return $this->formatAuthorResponse($updated_author, 'existing', 200);
        }

        private function setAuthorFields($term_id, $author_fields, $user_id = null, &$updated_args = []) {
            $available_fields = Author_Editor::get_fields(false);
            $available_fields = apply_filters('multiple_authors_author_fields', $available_fields, false);

            foreach ($author_fields as $field_name => $field_value) {
                if (isset($available_fields[$field_name])) {
                    $field_config = $available_fields[$field_name];
                    $sanitized_value = $this->sanitizeFieldValue($field_value, $field_config);
                    update_term_meta($term_id, $field_name, $sanitized_value);

                    // Also update user meta if there's a mapped user
                    if ($user_id) {
                        update_user_meta($user_id, $field_name, $sanitized_value);
                        $updated_args[$field_name] = $sanitized_value;
                    }
                }
            }
        }

        private function formatAuthorResponse($author, $author_type, $status_code = 200)
        {
            $available_fields = Author_Editor::get_fields(false);
            $available_fields = apply_filters('multiple_authors_author_fields', $available_fields, false);

            $response_data = [
                'term_id' => (int) $author->term_id,
                'user_id' => (int) $author->user_id,
                'is_guest' => $author->is_guest() ? 1 : 0,
                'slug' => $author->slug,
                'display_name' => $author->display_name,
                'avatar_url' => $author->get_avatar_url(),
                'author_type' => $author_type,
                'edit_link' => get_edit_term_link($author->term_id, 'author')
            ];

            // Add all author fields to response
            foreach (array_keys($available_fields) as $field_name) {
                if (!in_array($field_name, ['user_id', 'avatar'])) {
                    $response_data[$field_name] = $author->$field_name;
                }
            }

            return new WP_REST_Response($response_data, $status_code);
        }

        private function sanitizeFieldValue($value, $field_config)
        {
            $field_type = isset($field_config['type']) ? $field_config['type'] : 'text';

            switch ($field_type) {
                case 'textarea':
                case 'wysiwyg':
                    return wp_kses_post($value);
                case 'text_email':
                    return sanitize_email($value);
                case 'text_url':
                    return esc_url_raw($value);
                case 'text':
                default:
                    return sanitize_text_field($value);
            }
        }
    }
}