Skip to content
wiki.fftac.org

Collaboration - Source Excerpt 02

Back to Collaboration

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/ns12-manuscript/includes/collaboration.php so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/ns12-manuscript/includes/collaboration.php

$url .= rawurlencode($submission_slug) . '/';
    }

    return $url;
}

function spiralist_book_pages_user_can_collaborate(int $user_id = 0): bool
{
    $user_id = $user_id > 0 ? $user_id : get_current_user_id();
    if ($user_id <= 0) {
        return false;
    }

    if (user_can($user_id, 'manage_options')) {
        return true;
    }

    return user_can($user_id, spiralist_book_pages_get_collaboration_capability());
}

function spiralist_book_pages_get_collaboration_profile_user(string $profile_slug): ?WP_User
{
    $profile_slug = sanitize_title($profile_slug);
    if ($profile_slug === '') {
        return null;
    }

    $user = get_user_by('slug', $profile_slug);

    return $user instanceof WP_User ? $user : null;
}

function spiralist_book_pages_get_collaboration_notice_key(): string
{
    return 'spiralist_book_pages_collaboration_notice_' . get_current_user_id();
}

function spiralist_book_pages_set_collaboration_notice(string $message, string $type = 'success'): void
{
    if (get_current_user_id() <= 0) {
        return;
    }

    set_transient(
        spiralist_book_pages_get_collaboration_notice_key(),
        [
            'message' => sanitize_text_field($message),
            'type' => $type === 'error' ? 'error' : 'success',
        ],
        MINUTE_IN_SECONDS
    );
}

function spiralist_book_pages_get_collaboration_notice(): array
{
    if (get_current_user_id() <= 0) {
        return [];
    }

    $notice = get_transient(spiralist_book_pages_get_collaboration_notice_key());
    if (!is_array($notice)) {
        return [];
    }

    delete_transient(spiralist_book_pages_get_collaboration_notice_key());

    return $notice;
}

function spiralist_book_pages_get_collaboration_attachment_payload(int $attachment_id, string $preferred_alt = ''): array
{
    $attachment_id = max(0, $attachment_id);
    if ($attachment_id <= 0) {
        return [];
    }

    if (!wp_attachment_is_image($attachment_id)) {
        return [];
    }

    $image_url = wp_get_attachment_image_url($attachment_id, 'full');
    if (!is_string($image_url) || $image_url === '') {
        return [];
    }

    $thumbnail_url = wp_get_attachment_image_url($attachment_id, 'large');
    if (!is_string($thumbnail_url) || $thumbnail_url === '') {
        $thumbnail_url = $image_url;
    }

    $alt = trim($preferred_alt);
    if ($alt === '') {
        $alt = trim((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true));
    }

    return [
        'id' => $attachment_id,
        'url' => $image_url,
        'thumbnail_url' => $thumbnail_url,
        'alt' => $alt,
        'caption' => (string) wp_get_attachment_caption($attachment_id),
    ];
}

function spiralist_book_pages_validate_collaboration_image_attachment(int $attachment_id)
{
    $attachment_id = max(0, $attachment_id);
    if ($attachment_id <= 0) {
        return new WP_Error('spiralist_book_pages_missing_image', 'Upload a manuscript image before saving this page.');
    }

    $attachment = get_post($attachment_id);
    if (!$attachment instanceof WP_Post || $attachment->post_type !== 'attachment') {
        return new WP_Error('spiralist_book_pages_invalid_image', 'The selected manuscript image could not be found.');
    }

    if (!wp_attachment_is_image($attachment_id)) {
        return new WP_Error('spiralist_book_pages_invalid_image_type', 'Choose an image file for this manuscript page.');
    }

    $image_url = wp_get_attachment_image_url($attachment_id, 'full');
    if (!is_string($image_url) || $image_url === '') {
        return new WP_Error('spiralist_book_pages_invalid_image', 'The selected manuscript image is no longer available.');
    }

    return $attachment_id;
}

function spiralist_book_pages_normalize_collaboration_copy($copy, ?WP_Post $submission = null): array
{
    if (!is_array($copy)) {
        return [];
    }

    $title = sanitize_text_field((string) ($copy['title'] ?? ($submission instanceof WP_Post ? $submission->post_title : '')));
    $summary = sanitize_textarea_field((string) ($copy['summary'] ?? ''));
    $alt = sanitize_text_field((string) ($copy['alt'] ?? ''));
    $image_id = max(0, (int) ($copy['image_id'] ?? 0));
    $attachment = spiralist_book_pages_get_collaboration_attachment_payload($image_id, $alt);

    return [
        'title' => $title,
        'summary' => $summary,
        'alt' => (string) ($attachment['alt'] ?? $alt),
        'image_id' => $image_id,
        'image_url' => (string) ($attachment['url'] ?? ''),
        'thumbnail_url' => (string) ($attachment['thumbnail_url'] ?? ''),
        'caption' => (string) ($attachment['caption'] ?? ''),
        'updated_at' => sanitize_text_field((string) ($copy['updated_at'] ?? '')),
        'submitted_at' => sanitize_text_field((string) ($copy['submitted_at'] ?? '')),
        'approved_at' => sanitize_text_field((string) ($copy['approved_at'] ?? '')),
        'version' => max(0, (int) ($copy['version'] ?? 0)),
    ];
}

function spiralist_book_pages_collaboration_copies_match(array $left, array $right): bool
{
    foreach (['title', 'summary', 'alt'] as $field) {
        if ((string) ($left[$field] ?? '') !== (string) ($right[$field] ?? '')) {
            return false;
        }
    }

    return max(0, (int) ($left['image_id'] ?? 0)) === max(0, (int) ($right['image_id'] ?? 0));
}

function spiralist_book_pages_generate_collaboration_share_token(): string
{
    return wp_generate_password(32, false, false);
}

function spiralist_book_pages_ensure_collaboration_share_token(int $submission_id): string
{
    $token = sanitize_text_field(
        (string) get_post_meta($submission_id, spiralist_book_pages_get_collaboration_share_token_meta_key(), true)
    );

    if ($token === '') {
        $token = spiralist_book_pages_generate_collaboration_share_token();
        update_post_meta($submission_id, spiralist_book_pages_get_collaboration_share_token_meta_key(), $token);
    }

    return $token;
}

function spiralist_book_pages_get_collaboration_submission_record($submission): array
{
    $post = $submission instanceof WP_Post ? $submission : get_post((int) $submission);
    if (!$post instanceof WP_Post || $post->post_type !== spiralist_book_pages_get_collaboration_post_type()) {
        return [];
    }

    $working_copy = spiralist_book_pages_normalize_collaboration_copy(
        get_post_meta($post->ID, spiralist_book_pages_get_collaboration_working_copy_meta_key(), true),
        $post
    );
    $public_copy = spiralist_book_pages_normalize_collaboration_copy(
        get_post_meta($post->ID, spiralist_book_pages_get_collaboration_public_copy_meta_key(), true),
        $post
    );
    $status = spiralist_book_pages_normalize_collaboration_status(
        (string) get_post_meta($post->ID, spiralist_book_pages_get_collaboration_status_meta_key(), true)
    );

    if (!empty($public_copy)) {
        $status = spiralist_book_pages_collaboration_copies_match($working_copy, $public_copy)
            ? 'published'
            : ($status === 'pending_review' || $status === 'changes_requested' ? $status : 'published_with_draft');
    }

    $author = get_userdata((int) $post->post_author);
    $share_token = spiralist_book_pages_ensure_collaboration_share_token($post->ID);
    $submitted_at = sanitize_text_field(
        (string) get_post_meta($post->ID, spiralist_book_pages_get_collaboration_submitted_at_meta_key(), true)
    );
    $approved_at = sanitize_text_field(
        (string) get_post_meta($post->ID, spiralist_book_pages_get_collaboration_approved_at_meta_key(), true)
    );
    $public_version = max(
        0,
        (int) get_post_meta($post->ID, spiralist_book_pages_get_collaboration_public_version_meta_key(), true)
    );

    $record = [
        'id' => $post->ID,
        'slug' => (string) $post->post_name,
        'status' => $status,
        'status_label' => spiralist_book_pages_get_collaboration_status_label($status),
        'status_description' => spiralist_book_pages_get_collaboration_status_description($status),
        'status_tone' => spiralist_book_pages_get_collaboration_status_tone($status),
        'working_copy' => $working_copy,
        'public_copy' => $public_copy,
        'live_copy' => !empty($public_copy) ? $public_copy : $working_copy,
        'has_public_copy' => !empty($public_copy),
        'has_pending_changes' => !empty($public_copy) && !spiralist_book_pages_collaboration_copies_match($working_copy, $public_copy),
        'share_token' => $share_token,
        'review_notes' => sanitize_textarea_field(
            (string) get_post_meta($post->ID, spiralist_book_pages_get_collaboration_review_notes_meta_key(), true)
        ),
        'submitted_at' => $submitted_at,