# fluent-boards/2.0.10/app/Services/Helper.php

FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration, version 2.0.10. 744 lines.

- Page: https://pluginprobe.com/plugins/fluent-boards/2.0.10/code/app/Services/Helper.php
- Raw: https://pluginprobe.com/plugins/fluent-boards/2.0.10/raw/app/Services/Helper.php
- Modified: 2026-08-12T09:55:32+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/fluent-boards/2.0.10/code/app/Services/Helper.php#L10-L20`.

```php
<?php

namespace FluentBoards\App\Services;

use FluentBoards\App\Models\Activity;
use FluentBoards\App\Models\Stage;
use FluentBoards\App\Models\Webhook;
use FluentBoards\Framework\Support\Arr;
use FluentBoards\Framework\Support\Str;
use FluentBoards\App\Models\Board;
use FluentBoards\App\Services\Parsedown;
use FluentBoards\App\Services\PermissionManager;

class Helper
{
    public static function snake_case($string)
    {
        return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $string));
    }

    public static function slugify($text, $id = '', $length = 20)
    {
        $text = substr($text, 0, $length); // limiting to max 20 chars
        $text = Str::slug($text);
        if ($id) {
            $text = $id . '-' . $text;
        }

        return $text;
    }

    public static function loadView($template, $data)
    {
        extract($data, EXTR_OVERWRITE);

        $template = sanitize_file_name($template);

        $template = str_replace('.', DIRECTORY_SEPARATOR, $template);

        ob_start();
        include FLUENT_BOARDS_PLUGIN_PATH . 'app/Views/' . $template . '.php';

        return ob_get_clean();
    }

    private static function sanitizeData($data, $fieldMaps)
    {
        foreach ($data as $key => $value) {
            if ($value && isset($fieldMaps[$key]) && !is_array($value)) {
                $data[$key] = call_user_func($fieldMaps[$key], $value);
            }
        }

        return $data;
    }

    /**
     * Normalize a nullable datetime value so task flows can safely persist NULL.
     *
     * @param mixed $value
     * @return mixed|null
     */
    public static function normalizeDateValue($value)
    {
        if ($value === null || is_bool($value)) {
            return null;
        }

        if (is_string($value)) {
            $value = trim($value);

            if ($value === '') {
                return null;
            }

            $normalizedValue = strtolower($value);

            if (in_array($normalizedValue, ['none', 'null'], true)) {
                return null;
            }

            if (in_array($value, ['0000-00-00', '0000-00-00 00:00:00'], true)) {
                return null;
            }

            if (preg_match('/^(\d{4})-/', $value, $matches) && (int) $matches[1] < 1900) {
                return null;
            }

            if (strtotime($value) === false) {
                return null;
            }
        }

        return $value;
    }

    /**
     * Normalize a list of nullable datetime keys inside an attribute array.
     *
     * @param array $data
     * @param array $dateKeys
     * @return array
     */
    public static function normalizeDates($data, $dateKeys = [])
    {
        foreach ($dateKeys as $dateKey) {
            if (array_key_exists($dateKey, $data)) {
                $data[$dateKey] = self::normalizeDateValue($data[$dateKey]);
            }
        }

        return $data;
    }

    public static function sanitizeTask($data)
    {
        $fieldMaps = [
            'title'          => 'sanitize_text_field',
            'board_id'       => 'intval',
            'parent_id'      => 'intval',
            'crm_contact_id' => 'intval',
            'type'      => 'sanitize_text_field',
            'stage'          => 'sanitize_text_field',
            'reminder_type'  => 'sanitize_text_field',
            'priority'       => 'sanitize_text_field',
            'lead_value'     => 'doubleval',
            'remind_at'      => 'sanitize_text_field',
            'scope'          => 'sanitize_text_field',
            'source'         => 'sanitize_text_field',
            'source_id'      => 'sanitize_text_field',
            'description'    => 'fluent_boards_sanitize_description',
            'due_date'       => 'sanitize_text_field',
            'start_at'       => 'sanitize_text_field',
            'log_minutes'    => 'sanitize_text_field',
            'last_completed' => 'sanitize_text_field',
            'assignees'      => 'intval',
            'is_archived'    => 'intval',
            'previous_stage' => 'sanitize_text_field',
            'new_stage'      => 'sanitize_text_field',
            'new_index'      => 'intval',
            'old_index'      => 'intval',
            'new_board_id'   => 'intval',
            'position'       => 'intval'

        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function sanitizeBoard($data)
    {
        $fieldMaps = [
            'board_id'        => 'intval',
            'title'           => 'sanitize_text_field',
            'parent_id'       => 'intval',
            'type'            => 'sanitize_text_field',
            'description'     => 'fluent_boards_sanitize_description',
            'currency'        => 'sanitize_text_field',
            'image_url'       => 'sanitize_url',
            'is_auth_require' => 'intval',
            'crm_contact_id'  => 'intval',
            'id'              => 'sanitize_text_field',
            'is_image'        => 'rest_sanitize_boolean',
            'color'           => 'sanitize_text_field', // sanitize_hex_color doesn't work when color code is greater than 6 characters
            'reset'           => 'rest_sanitize_boolean',
            'created_by'      => 'intval',
        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function sanitizeStage($data)
    {
        $fieldMaps = [
            'title'  => 'sanitize_text_field',
            'slug'   => 'sanitize_text_field',
            'type'   => 'sanitize_text_field',
            'status' => 'sanitize_text_field',
        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function sanitizeComment($data)
    {
        $fieldMaps = [
            'description' => 'wp_kses_post',
            'created_by'  => 'intval',
            'task_id'     => 'intval',
            'type'        => 'sanitize_text_field',
        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function sanitizeLabel($data)
    {
        $fieldMaps = [
            'bg_color'   => 'sanitize_text_field',
            'color'      => 'sanitize_text_field',
            'label'      => 'sanitize_text_field',
            'boardId'    => 'intval',
            'task_id'    => 'intval',
            'meta_value' => 'intval',
        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function sanitizeSubtask($data)
    {
        $fieldMaps = [
            'title'       => 'sanitize_text_field',
            'stage'       => 'sanitize_text_field',
            'newPosition' => 'intval',
            'priority'    => 'sanitize_text_field',
            'type'   => 'sanitize_text_field',
            'description' => 'fluent_boards_sanitize_description',
            'group_id'    => 'intval',
            'board_id'    => 'intval',
            'created_by'  => 'intval',
            'due_date'    => 'sanitize_text_field',
            'due_at'      => 'sanitize_text_field',
            'started_at'  => 'sanitize_text_field',
            'reminder_type' => 'sanitize_text_field',
            'remind_at'   => 'sanitize_text_field',
            'add_to_top'  => 'rest_sanitize_boolean',
        ];

        $data = self::sanitizeData($data, $fieldMaps);

        if (!empty($data['assignees']) && is_array($data['assignees'])) {
            $data['assignees'] = array_slice(array_filter(array_map('intval', $data['assignees'])), 0, 1);
        }

        if (!empty($data['labels']) && is_array($data['labels'])) {
            $data['labels'] = array_filter(array_map('intval', $data['labels']));
        }

        return $data;
    }

    public static function createActivity($data)
    {
        return Activity::create($data);
    }

    public static function sanitizeTaskMeta($data)
    {
        $fieldMaps = [
            'title' => 'sanitize_text_field',
            'url'   => 'sanitize_url',

        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function getFormattedStagesByBoardId($boardId)
    {
        if (!$boardId) {
            return [];
        }

        $board = Board::findOrFail($boardId);

        $stages = $board->stages()->get();
//        return static::formateStage($stages);
        return $stages;
    }

    public static function getStagesByBoardId($boardId)
    {
        if (!$boardId) {
            return [];
        }

        $board = Board::findOrFail($boardId);

        $stages = $board->stages()->get();
        return static::formateStage($stages);
//        return $stages;
    }

    public static function formateStage($stages)
    {
        if (!$stages) return [];

        $formattedStages = [];
        foreach ($stages as $stage) {
            $boardName = $stage->board->title;
            $formattedStages[] = [
                'id'    => strval($stage->id),
                'title' => $boardName . ' - ' . $stage->title
            ];
        }
        return $formattedStages;
    }

    public static function getIdTitleArray($data)
    {
        $array = [];
        foreach ($data as $item) {
            $array[] = [
                'id'    => $item->id,
                'title' => $item->title
            ];
        }
        return $array;
    }

    // get Task Url by task id and board id
    public static function getTaskUrl($taskId, $boardId): string
    {
        if (!$taskId || !$boardId) {
            return '';
        }
        return fluent_boards_page_url() . 'boards/' . $boardId . '/tasks/' . $taskId;
    }

    public static function getTaskUrlByTask($task): string
    {
        if (!$task) {
            return '';
        }
        return fluent_boards_page_url() . 'boards/' . $task->board_id . '/tasks/' . $task->id;
    }

    public static function getBoardUrl($boardId): string
    {
        if (!$boardId) {
            return '';
        }
        return fluent_boards_page_url() . 'boards/' . $boardId;
    }

    public static function crm_contact($id)
    {
        if (!defined('FLUENTCRM')) {
            return '';
        }

        $contact = \FluentCrm\App\Models\Subscriber::with(['tags', 'lists'])->find($id);

        if (!$contact) {
            return null;
        }

        return [
            'id'              => $contact->id,
            'email'           => $contact->email,
            'first_name'      => $contact->first_name,
            'last_name'       => $contact->last_name,
            'full_name'       => $contact->full_name,
            'avatar'          => $contact->avatar,
            'photo'           => $contact->photo,
            'status'          => $contact->status,
            'contact_type'    => $contact->contact_type,
            'last_activity'   => $contact->last_activity,
            'life_time_value' => $contact->life_time_value,
            'total_points'    => $contact->total_points,
            'user_id'         => $contact->user_id,
            'created_at'      => $contact->created_at,
            'tags'            => Helper::getIdTitleArray($contact->tags),
            'lists'           => Helper::getIdTitleArray($contact->lists)

        ];

    }

    public static function getStagesByBoardGroup()
    {
        $boards = self::getBoards();

        $groups = [];
        foreach ($boards as $board) {
            $groups[] = [
                'title'   => $board->title,
                'slug'    => 'aaa' . '_' . $board->id,
                'options' => self::getStagesByBoardId($board->id)
            ];
        }
        return $groups;
    }

    public static function getBoards()
    {
        return Board::orderBy('created_at', 'ASC')->get();
    }

    public static function getStage($stageId)
    {
        return Stage::findOrFail($stageId);
    }

    public static function getBoardByStageId($stageId)
    {
        $stage = self::getStage($stageId);
        return $stage->board;
    }

    public static function sanitizeUserCollections($users)
    {
        if (empty($users)) {
            return $users;
        }

        foreach ($users as $key => $user) {
            if (is_object($user) && isset($user->pivot)) {
                $settings = maybe_unserialize($user->pivot->settings);
                $user->role = Arr::get($settings, 'is_admin')
                    ? 'Admin'
                    : (Arr::has($settings, 'is_viewer_only') && Arr::get($settings, 'is_viewer_only')
                        ? 'Viewer'
                        : 'Member');
            }
        }
        
        if (current_user_can('list_users')) {
            return $users;
        }

        if (is_object($users) && method_exists($users, 'makeHidden')) {
            $users->makeHidden(['user_email', 'user_nicename', 'user_registered', 'user_url', 'user_status']);
        } elseif (is_array($users)) {
            foreach ($users as &$user) {
                if (is_array($user)) {
                    unset($user['user_email'], $user['user_nicename'], $user['user_registered'], $user['user_url'], $user['user_status']);
                }
            }
            unset($user);
        }

        return $users;
    }

    public static function sanitizeUsersArray($users, $boardId = null)
    {
        if (current_user_can('list_users')) {
            return $users;
        }

        $sanitizedUsers = [];

        if(!PermissionManager::isBoardManager($boardId)) //Todo: may create permission security issue, will be modified later
        {
            $currentUser = wp_get_current_user();
            if($currentUser && isset($currentUser->user_email)){
                $currentUserEmail = $currentUser->user_email;
            }
            foreach ($users as $user) {

                if($user['email'] === $currentUserEmail){
                    $sanitizedUsers[] = $user;
                    continue;
                }

                if (isset($user['email'])) {
                    $user['email'] = self::obfuscateEmail($user['email']);
                }
                if (isset($user['user_email'])) {
                    $user['user_email'] = self::obfuscateEmail($user['user_email']);
                }

                $sanitizedUsers[] = $user;
            }
        } else {
            foreach ($users as $user) {
                $sanitizedUsers[] = $user;
            }
        }

        return $sanitizedUsers;
    }

    public static function obfuscateEmail($email)
    {
        if (!is_string($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            return $email; // Not a valid email, return as is
        }

        list($name, $domain) = explode('@', $email, 2);

        // Local part: show first 3 chars, then fixed ****
        $visibleLocal = substr($name, 0, 3);
        $maskedLocal  = $visibleLocal . '****';

        // Domain: mask the main label to **** + last 2 chars, keep rest (TLDs) intact
        $domainParts = explode('.', $domain);
        $mainLabel   = $domainParts[0] ?? '';
        $tail        = strlen($mainLabel) >= 2 ? substr($mainLabel, -2) : $mainLabel;
        $domainParts[0] = '****' . $tail; // e.g., example.com -> ****le.com
        $maskedDomain = implode('.', $domainParts);

        return $maskedLocal . '@' . $maskedDomain;
    }

    public static function getPriorityOptions()
    {
        return [
            [
                'id' => '',
                'title' => 'No priority'
            ],
            [
                'id' => 'urgent',
                'title' => 'Urgent'
            ],
            [
                'id' => 'high',
                'title' => 'High'
            ],
            [
                'id' => 'medium',
                'title' => 'Medium'
            ],
            [
                'id' => 'low',
                'title' => 'Low'
            ],
        ];
    }

    public static function dueDateConversion($due_time, $unit)
    {
        if($due_time <= 0) {
            return null;
        }
        $currentTime = current_time('mysql');
        $readyString = '+' . $due_time . ' ' . $unit;
        return gmdate('Y-m-d H:i:s',strtotime($readyString,strtotime($currentTime)));
    }

    public static function searchWordPressUsers($searchQuery, $limit = 20)
    {
        $search = sanitize_text_field($searchQuery);

        // Search by user login, email, and nicename
        $args = array(
            'role'   => '',
            'search' => '*' . $search . '*',
            'number' => $limit,
        );

        // Get users by login, email, and nicename
        $user_query = new \WP_User_Query($args);
        $users_by_login = $user_query->get_results();

        // Search by first name and last name
        $meta_query_args = array(
            'relation' => 'OR',
            array(
                'key'     => 'first_name',
                'value'   => $search,
                'compare' => 'LIKE',
            ),
            array(
                'key'     => 'last_name',
                'value'   => $search,
                'compare' => 'LIKE',
            ),
        );

        $meta_query = array(
            'role'       => '',
            'meta_query' => $meta_query_args,
            'number'     => $limit,
        );

        // Get users by first name and last name
        $users_by_meta = get_users($meta_query);

        // Merge and remove duplicates
        $users = array_merge($users_by_login, $users_by_meta);
        $users = array_unique($users, SORT_REGULAR);

        return $users;

    }

    public static function sanitizeTaskAttachment($data)
    {
        $fieldMaps = [
            'title' => 'sanitize_text_field',
            'url'   => 'sanitize_url',
        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    public static function sanitizeTaskRepeatData($data)
    {
        $fieldMaps = [
            'create_new'              => 'intval',
            'repeat_in'               => 'intval',
            'repeat_type'             => 'sanitize_text_field',
            'repeat_when_complete'    => 'intval',
            'selected_month'          => 'sanitize_text_field',
            'selected_stage'          => 'intval',
            'board_id'                => 'intval',
            'time'                    => 'sanitize_text_field',
            'time_zone'               => 'sanitize_text_field',
            'next_repeat_date'        => 'sanitize_text_field',
            'repeat_in_month_type'    => 'sanitize_text_field',
        ];

        return self::sanitizeData($data, $fieldMaps);
    }

    /**
     * Sanitize the author snapshot supplied by an external task integration.
     *
     * @param mixed $author
     * @return array
     */
    private static function sanitizeExternalTaskAuthor($author)
    {
        if (!is_array($author)) {
            return [];
        }

        return array_filter([
            'name'  => sanitize_text_field($author['name'] ?? ''),
            'email' => sanitize_email($author['email'] ?? ''),
            'photo' => esc_url_raw($author['photo'] ?? ''),
        ]);
    }

    public static function sanitizeTaskForWebHook($data)
    {
        $fieldMaps = [
            'title'          => 'sanitize_text_field',
            'board_id'       => 'intval',
            'parent_id'      => 'intval',
            'crm_contact_id' => 'intval',
            'type'      => 'sanitize_text_field',
            'stage'          => 'sanitize_text_field',
            'reminder_type'  => 'sanitize_text_field',
            'priority'       => 'sanitize_text_field',
            'lead_value'     => 'doubleval',
            'remind_at'      => 'sanitize_text_field',
            'scope'          => 'sanitize_text_field',
            'source'         => 'sanitize_text_field',
            'source_id'      => 'sanitize_text_field',
            'description'    => 'fluent_boards_sanitize_description',
            'due_date'       => 'sanitize_text_field',
            'start_at'       => 'sanitize_text_field',
            'log_minutes'    => 'sanitize_text_field',
            'last_completed' => 'sanitize_text_field',
            'is_archived'    => 'intval',
            'previous_stage' => 'sanitize_text_field',
            'new_stage'      => 'sanitize_text_field',
            'new_index'      => 'intval',
            'old_index'      => 'intval',
            'new_board_id'   => 'intval',
            'position'       => 'intval'

        ];

        $data = self::sanitizeData($data, $fieldMaps);

        if (isset($data['settings']) && is_array($data['settings']) && isset($data['settings']['author'])) {
            $data['settings'] = [
                'author' => self::sanitizeExternalTaskAuthor($data['settings']['author']),
            ];
        } else {
            unset($data['settings']);
        }

        return $data;
    }


    public static function taskReminderTypes()
    {
        $allowedTypes = [
                            '30_minutes_before' => __('30 minutes before', 'fluent-boards'),
                            '1_hour_before'     => __('1 hour before', 'fluent-boards'),
                            '2_hours_before'    => __('2 hours before', 'fluent-boards'),
                            '1_day_before'      => __('1 day before', 'fluent-boards'),
                            '2_days_before'     => __('2 days before', 'fluent-boards'),
                            '1_week_before'     => __('1 week before', 'fluent-boards'),
                        ];

        $allowedTypes = apply_filters('fluent_boards/task_reminder_types', $allowedTypes);

        return $allowedTypes;
    }

    public static function translateActivities($activities)
    {
        $actionTranslations = [
            'changed'  => __('changed', 'fluent-boards'),
            'updated'  => __('updated', 'fluent-boards'),
            'added'    => __('added', 'fluent-boards'),
            'removed'  => __('removed', 'fluent-boards'),
            'created'  => __('created', 'fluent-boards'),
            'closed'   => __('closed', 'fluent-boards'),
            'reopened' => __('reopened', 'fluent-boards'),
            'joined'   => __('joined', 'fluent-boards'),
            'left'     => __('left', 'fluent-boards'),
            'cloned'   => __('cloned', 'fluent-boards'),
            'deleted'  => __('deleted', 'fluent-boards'),
            'archived' => __('archived', 'fluent-boards'),
            'restored' => __('restored', 'fluent-boards'),
            'set'      => __('set', 'fluent-boards'),
        ];

        $columnTranslations = [
            'task'                 => __('task', 'fluent-boards'),
            'description'          => __('description', 'fluent-boards'),
            'board'                => __('board', 'fluent-boards'),
            'assignee'             => __('assignee', 'fluent-boards'),
            'label'                => __('label', 'fluent-boards'),
            'Due Date'             => __('Due Date', 'fluent-boards'),
            'Start Date'           => __('Start Date', 'fluent-boards'),
            'priority'             => __('priority', 'fluent-boards'),
            'comment'              => __('comment', 'fluent-boards'),
            'a reply'              => __('a reply', 'fluent-boards'),
            'subtask'              => __('subtask', 'fluent-boards'),
            'subtask group'        => __('subtask group', 'fluent-boards'),
            'subtask group title'  => __('subtask group title', 'fluent-boards'),
            'stage'                => __('stage', 'fluent-boards'),
            'the associate email'  => __('the associate email', 'fluent-boards'),
            'attachment'           => __('attachment', 'fluent-boards'),
            'repeat task'          => __('repeat task', 'fluent-boards'),
            'Repeat Task'          => __('Repeat Task', 'fluent-boards'),
        ];

        foreach ($activities as $activity) {
            $activity->action_key = $activity->action;
            $activity->column_key = $activity->column;

            if (isset($actionTranslations[$activity->action])) {
                $activity->action = $actionTranslations[$activity->action];
            }
            if (isset($columnTranslations[$activity->column])) {
                $activity->column = $columnTranslations[$activity->column];
            }
        }
    }
}

```
