diff --git a/classes/Tasks/AttachmentTask.php b/classes/Tasks/AttachmentTask.php index 391fbd49..48902f31 100644 --- a/classes/Tasks/AttachmentTask.php +++ b/classes/Tasks/AttachmentTask.php @@ -37,15 +37,27 @@ protected function updateCurrentPost($post_id) { $this->currentTitle = get_post_field('post_title', $post_id); - $thumb = wp_get_attachment_image_src($post_id, 'thumbnail', true); - if (!empty($thumb)) { - $this->currentThumb = $thumb[0]; - $this->isIcon = (($thumb[1] != 150) && ($thumb[2] != 150)); + if (strpos(get_post_mime_type($post_id), 'video/') === 0) { + $thumb = get_the_post_thumbnail_url($post_id, 'thumbnail'); + if (!empty($thumb)) { + $this->currentThumb = $thumb; + $this->isIcon = false; + } else { + $this->currentThumb = null; + $this->isIcon = false; + } } else { - $this->currentThumb = null; - $this->isIcon = false; + $thumb = wp_get_attachment_image_src($post_id, 'thumbnail', true); + if (!empty($thumb)) { + $this->currentThumb = $thumb[0]; + $this->isIcon = (($thumb[1] != 150) && ($thumb[2] != 150)); + } else { + $this->currentThumb = null; + $this->isIcon = false; + } } + $this->save(); } @@ -87,7 +99,9 @@ public function prepare($options = [], $selectedItems = []) { if (!empty($selectedItems) && is_array($selectedItems)) { foreach($selectedItems as $postId) { - $this->addItem($this->filterItem(['id' => $postId], $options)); + if (!$this->addDataForPost($postId, $options)) { + $this->addItem($this->filterItem(['id' => $postId], $options)); + } } } else { $args = [ @@ -135,7 +149,9 @@ public function prepare($options = [], $selectedItems = []) { } foreach($postIds as $postId) { - $this->addItem($this->filterItem(['id' => $postId], $options)); + if (!$this->addDataForPost($postId, $options)) { + $this->addItem($this->filterItem(['id' => $postId], $options)); + } } } @@ -145,4 +161,8 @@ public function prepare($options = [], $selectedItems = []) { return ($this->totalItems > 0); } + protected function addDataForPost($postId, $options):bool { + return false; + } + } diff --git a/classes/Tasks/Task.php b/classes/Tasks/Task.php index 2e2c2041..57274291 100644 --- a/classes/Tasks/Task.php +++ b/classes/Tasks/Task.php @@ -1023,6 +1023,7 @@ public static function markConfirmed() { //region JSON + #[\ReturnTypeWillChange] public function jsonSerialize() { if ($this->state >= self::STATE_COMPLETE) { $remaining = 0; diff --git a/classes/Tasks/TaskSchedule.php b/classes/Tasks/TaskSchedule.php index 42c56c01..65dc60dc 100644 --- a/classes/Tasks/TaskSchedule.php +++ b/classes/Tasks/TaskSchedule.php @@ -383,6 +383,7 @@ public static function hasScheduledTaskOfType($type) { //endregion //region JSON + #[\ReturnTypeWillChange] public function jsonSerialize() { return [ 'id' => $this->id, diff --git a/classes/Tools/Crop/CropTool.php b/classes/Tools/Crop/CropTool.php index a950e976..b146157d 100644 --- a/classes/Tools/Crop/CropTool.php +++ b/classes/Tools/Crop/CropTool.php @@ -127,67 +127,50 @@ private function hookupUI() return $content; },10,2); - add_action( 'wp_enqueue_media', function () { - remove_action('admin_footer', 'wp_print_media_templates'); - - add_action('admin_footer', function(){ - ob_start(); - wp_print_media_templates(); - $result=ob_get_clean(); - echo $result; - - - $sizes=ilab_get_image_sizes(); - $sizeKeys=array_keys($sizes); - - ob_start(); - ?> - - editPageURL($post->ID).'" title="Edit Image">'.__('Edit Image').''; + if (strpos($post->post_mime_type, 'image') === 0) { + $meta = wp_get_attachment_metadata($post->ID); + if (empty(arrayPath($meta, 's3', null))) { + return $actions; + } + + $newaction['ilab_edit_image'] = '' . __('Edit Image') . ''; + return array_merge($actions, $newaction); + } - return array_merge($actions, $newaction); + return $actions; }, 10, 2); - add_action('wp_enqueue_media', function() { - remove_action('admin_footer', 'wp_print_media_templates'); - - add_action('admin_footer', function() { - ob_start(); - wp_print_media_templates(); - $result = ob_get_clean(); - echo $result; - - - ?> - - options_group, $option); } - /** - * Registers an option with a text input UI - * @param $option_name - * @param $title - * @param $settings_slug - * @param null $description - * @param null $placeholder - * @param null $conditions - */ - protected function registerTextFieldSetting($option_name, $title, $settings_slug, $description=null, $placeholder=null, $conditions=null, $default=null) { - add_settings_field($option_name, - $title, - [$this,'renderTextFieldSetting'], - $this->options_page, - $settings_slug, - ['option'=>$option_name, 'description'=>$description, 'placeholder' => $placeholder, 'conditions' => $conditions, 'default' => $default]); - } + /** + * Registers an option with a text input UI + * @param $option_name + * @param $title + * @param $settings_slug + * @param null $description + * @param null $placeholder + * @param null $conditions + */ + protected function registerTextFieldSetting($option_name, $title, $settings_slug, $description=null, $placeholder=null, $conditions=null, $default=null) { + add_settings_field($option_name, + $title, + [$this,'renderTextFieldSetting'], + $this->options_page, + $settings_slug, + ['option'=>$option_name, 'description'=>$description, 'placeholder' => $placeholder, 'conditions' => $conditions, 'default' => $default]); + } - /** - * Renders a text field - * @param $args - */ - public function renderTextFieldSetting($args) { - $value = Environment::Option($args['option']); - if (empty($value) && !empty($args['default'])) { - $value = $args['default']; - } + /** + * Registers an option with a text input UI + * @param $option_name + * @param $title + * @param $settings_slug + * @param null $description + * @param null $placeholder + * @param null $conditions + */ + protected function registerColorFieldSetting($option_name, $title, $settings_slug, $description=null, $conditions=null, $default=null) { + add_settings_field($option_name, + $title, + [$this,'renderColorFieldSetting'], + $this->options_page, + $settings_slug, + ['option'=>$option_name, 'description'=>$description, 'conditions' => $conditions, 'default' => $default]); + } - echo View::render_view('base/fields/text-field.php',[ - 'value' => $value, - 'name' => $args['option'], - 'placeholder' => $args['placeholder'], - 'conditions' => $args['conditions'], - 'description' => (isset($args['description'])) ? $args['description'] : false - ]); - } + /** + * Renders a text field + * @param $args + */ + public function renderTextFieldSetting($args) { + $value = Environment::Option($args['option']); + if (empty($value) && !empty($args['default'])) { + $value = $args['default']; + } + + echo View::render_view('base/fields/text-field.php',[ + 'value' => $value, + 'name' => $args['option'], + 'placeholder' => $args['placeholder'], + 'conditions' => $args['conditions'], + 'description' => (isset($args['description'])) ? $args['description'] : false + ]); + } + + /** + * Renders a text field + * @param $args + */ + public function renderColorFieldSetting($args) { + $value = Environment::Option($args['option']); + if (empty($value) && !empty($args['default'])) { + $value = $args['default']; + } + + echo View::render_view('base/fields/color.php',[ + 'value' => $value, + 'name' => $args['option'], + 'conditions' => $args['conditions'], + 'description' => (isset($args['description'])) ? $args['description'] : false + ]); + } /** * Registers an option with a text input UI diff --git a/classes/Tools/Storage/Driver/Supabase/SupabaseStorage.php b/classes/Tools/Storage/Driver/Supabase/SupabaseStorage.php new file mode 100644 index 00000000..031be69c --- /dev/null +++ b/classes/Tools/Storage/Driver/Supabase/SupabaseStorage.php @@ -0,0 +1,466 @@ +settings = new SupabaseStorageSettings(); + } + //endregion + + //region Static Information Methods + public static function identifier() { + return 'supabase'; + } + + public static function name() { + return 'Supabase Storage (Beta)'; + } + + public static function endpoint() { + return null; + } + + public static function pathStyleEndpoint() { + return null; + } + + public static function defaultRegion() { + return null; + } + + public static function forcedRegion() { + return null; + } + + public static function bucketLink($bucket) { + return null; + } + + public function pathLink($bucket, $key) { + return null; + } + //endregion + + //region Enabled/Options + public function usesSignedURLs($type = null) { + return false; + } + + public function supportsDirectUploads() { + return false; + } + + public function supportsWildcardDirectUploads() { + return false; + } + + public function supportsBrowser() { + return true; + } + //endregion + + //region API Requests + + /** + * @param $method + * @param $path + * @param $data + * @param $contentType + * + * @return \MediaCloud\Vendor\Psr\Http\Message\ResponseInterface + * @throws \MediaCloud\Vendor\GuzzleHttp\Exception\GuzzleException + */ + protected function request($method, $path, $data = [], $contentType = 'application/json') { + $handler = new CurlHandler(); + $stack = HandlerStack::create($handler); + + $c = new Client([ + 'base_uri' => $this->settings->storageUrl.'/storage/v1/', + 'verify' => false, + 'handler' => $stack + ]); + + $args = [ + 'headers' => [ + 'Authorization' => "Bearer {$this->settings->key}", + 'apiKey' => "{$this->settings->key}", + 'Content-Type' => $contentType, + ] + ]; + + if ($data) { + if ($contentType === 'application/json') { + $args['body'] = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + } else { + $args['body'] = $data; + } + } + + if (MCLOUD_DEBUGGING) { + $args['curl'] = [ + CURLOPT_PROXY => 'host.docker.internal', + CURLOPT_PROXYPORT => 8888, + ]; + + if ($contentType === 'application/json') { + $tapMiddleware = Middleware::tap(function($request) { + Logger::info("Guzzle Debug:\n".$request->getBody(), [], __METHOD__, __LINE__); + }); + $args['handler'] = $tapMiddleware($handler); + } + } + + return $c->request($method, $path, $args); + } + //endregion + + //region Settings related functions + + /** + * @param ErrorCollector|null $errorCollector + * @return bool|void + */ + public function validateSettings($errorCollector = null) { + delete_option('mcloud-storage-supabase-settings-error'); + $this->settings->settingsError = false; + + $valid = false; + + try { + if($this->enabled()) { + $res = $this->request('GET', 'bucket/'.$this->settings->bucket); + if ($res->getStatusCode() !== 200) { + Logger::error("Error validating supabase storage settings. ".$res->get_error_message(), [], __METHOD__, __LINE__); + $valid = false; + } else { + $valid = true; + } + + if(!$valid) { + $this->settings->settingsError = true; + update_option('mcloud-storage-supabase-settings-error', true); + } + } else { + if ($errorCollector) { + $errorCollector->addError("Supabase settings are missing or incorrect."); + } + } + } catch (\Exception $ex) { + } + + return $valid; + } + + public function settings() { + return $this->settings; + } + + public function enabled() { + if(!($this->settings->key && $this->settings->storageUrl && $this->settings->bucket)) { + $adminUrl = admin_url('admin.php?page=media-cloud-settings&tab=storage'); + NoticeManager::instance()->displayAdminNotice('error', "To start using Cloud Storage, you will need to supply your Supabase credentials..", true, 'ilab-cloud-storage-setup-warning', 'forever'); + + return false; + } + + if($this->settings->settingsError) { + NoticeManager::instance()->displayAdminNotice('error', 'Your Supabase settings are incorrect or the bucket does not exist. Please verify your settings and update them.'); + + return false; + } + + return true; + } + + public function settingsError() { + return $this->settings->settingsError; + } + //endregion + + //region File Functions + + public function bucket() { + return $this->settings->bucket; + } + + public function region() { + return null; + } + + public function isUsingPathStyleEndPoint() { + return false; + } + + public function acl($key) { + return null; + } + + public function insureACL($key, $acl) { + } + + public function updateACL($key, $acl) { + } + + public function canUpdateACL() { + return false; + } + + public function exists($key) { + try { + $res = $this->request('HEAD', 'object/public/'.trailingslashit($this->settings->bucket).$key); + return $res->getStatusCode() < 400; + } catch (\Exception $ex) { + return false; + } + } + + public function copy($sourceKey, $destKey, $acl, $mime = false, $cacheControl = false, $expires = false) { + try { + $this->request('POST', 'object/copy', [ + 'bucketId' => $this->settings->bucket, + 'sourceKey' => $sourceKey, + 'destKey' => $destKey, + ]); + } catch (\Exception $ex) { + Logger::error("Error copying files. ".$ex->getMessage(), [], __METHOD__, __LINE__); + } + } + + public function upload($key, $fileName, $acl, $cacheControl=null, $expires=null, $contentType=null, $contentEncoding=null, $contentLength=null, $tries = 1) { + try { + Logger::startTiming("Start Upload", [], __METHOD__, __LINE__); + if (empty($contentType)) { + $mime = wp_check_filetype_and_ext($fileName, pathinfo($fileName, PATHINFO_BASENAME)); + $contentType = !empty($mime['type']) ? $mime['type'] : 'application/octet-stream'; + } + $res = $this->request('POST', 'object/'.trailingslashit($this->settings->bucket).$key, fopen($fileName, 'r'), $contentType); + Logger::endTiming("End Upload", [], __METHOD__, __LINE__); + } catch (\Exception $ex) { + Logger::error("Error uploading file to Supabase. ".$ex->getMessage(), [], __METHOD__, __LINE__); + } + } + + public function delete($key) { + try { + $this->request('DELETE', 'object/'.untrailingslashit($this->settings->bucket), [ + 'prefixes' => [ltrim($key, '/')], + ]); + } catch (\Exception $ex) { + Logger::error("Error deleting file from Supabase. ".$ex->getMessage(), [], __METHOD__, __LINE__); + } + } + + public function createDirectory($key) { + } + + public function deleteDirectory($key) { + $files = $this->ls($key, '/', -1, null, true); + foreach($files['files'] as $file) { + $this->delete($file); + } + } + + public function dir($path = '', $delimiter = '/', $limit = -1, $next = null) { + try { + $next = $next ?? intval(0); + $res = $this->request('POST', 'object/list/'.untrailingslashit($this->settings->bucket), [ + 'limit' => $limit, + 'offset' => intval($next), + 'prefix' => ltrim($path, '/'), + ]); + + if ($res->getStatusCode() === 200) { + $data = json_decode($res->getBody(), true); + $files = []; + $rootFileCount = 0; + + foreach($data as $datum) { + $rootFileCount++; + if (empty($datum['id'])) { + if ($datum['name'] === '.emptyFolderPlaceholder') { + continue; + } + + $files[] = new StorageFile('DIR', trailingslashit($path).trailingslashit($datum['name'])); + } else { + if ($datum['name'] === '.emptyFolderPlaceholder') { + continue; + } + + $files[] = new StorageFile('FILE', trailingslashit($path).$datum['name'], null, arrayPath($datum, 'created_at', null), arrayPath($datum, 'metadata/size', 0), $this->url(trailingslashit($path).$datum['name'])); + } + } + + return [ + 'next' => $rootFileCount === $limit ? $next + $limit : null, + 'files' => $files, + ]; + + } + } catch (\Exception $ex) { + Logger::error("Error listing files from Supabase. ".$ex->getMessage(), [], __METHOD__, __LINE__); + } + + return [ + 'next' => null, + 'files' => [], + ]; + } + + public function ls($path = '', $delimiter = '/', $limit = -1, $next = null, $recursive = false) { + try { + $next = $next ?? intval(0); + $res = $this->request('POST', 'object/list/'.untrailingslashit($this->settings->bucket), [ + 'limit' => $limit === -1 ? 1000 : $limit, + 'offset' => $next, + 'next' => $next, + 'prefix' => ltrim($path, '/'), + ]); + + if ($res->getStatusCode() === 200) { + $data = json_decode($res->getBody(), true); + $files = []; + $rootFileCount = 0; + + foreach($data as $datum) { + $rootFileCount++; + if (empty($datum['id'])) { + if ($datum['name'] === '.emptyFolderPlaceholder') { + continue; + } + +// $files[] = trailingslashit($path).trailingslashit($datum['name']); + if ($recursive) { + $ls = $this->ls(trailingslashit($path).trailingslashit($datum['name']), $delimiter, $limit, $next, $recursive); + $files = array_merge($files, $ls['files']); + if ($limit === -1) { + while(!empty($ls['next'])) { + $ls = $this->ls(trailingslashit($path).trailingslashit($datum['name']), $delimiter, $limit, $ls['next'], $recursive); + $files = array_merge($files, $ls['files']); + } + } + } + } else { + if ($datum['name'] === '.emptyFolderPlaceholder') { + continue; + } + + $files[] = trailingslashit($path).$datum['name']; + } + } + + return [ + 'next' => $rootFileCount === $limit ? $next + $limit : null, + 'files' => $files, + ]; + + } + } catch (\Exception $ex) { + Logger::error("Error listing files from Supabase. ".$ex->getMessage(), [], __METHOD__, __LINE__); + } + + return [ + 'next' => null, + 'files' => [], + ]; + } + + public function info($key) { + $res = $this->request('HEAD', 'object/public/'.trailingslashit($this->settings->bucket).$key); + if ($res->getStatusCode() !== 200) { + throw new StorageException("Unable to retrieve file info for $key", 400); + } + + $length = arrayPath($res, 'headers/Content-Length', 0); + $type = arrayPath($res, 'headers/Content-Type', 'application/octet-stream'); + $url = $this->url($key); + $size = null; + if(strpos($type, 'image/') === 0) { + $faster = new FasterImage(); + $result = $faster->batch([$url]); + $result = $result[$url]; + $size = $result['size']; + } + + $fileInfo = new FileInfo($key, $url, $url, $length, $type, $size); + return $fileInfo; + } + //endregion + + //region URLs + public function presignedUrl($key, $expiration = 0, $options = []) { + return $this->url($key); + } + + public function url($key, $type = null) { + return trailingslashit($this->settings->storageUrl).'storage/v1/object/public/'.trailingslashit($this->settings->bucket).$key; + } + + public function signedURLExpirationForType($type = null) { + return null; + } + //endregion + + //region Direct Uploads + public function uploadUrl($key, $acl, $mimeType = null, $cacheControl = null, $expires = null) { + } + + public function enqueueUploaderScripts() { + } + //endregion + + + //region Optimization + public function prepareOptimizationInfo() { + return [ + ]; + } + //endregion + public function client() { + return $this; + } +} diff --git a/classes/Tools/Storage/Driver/Supabase/SupabaseStorageSettings.php b/classes/Tools/Storage/Driver/Supabase/SupabaseStorageSettings.php new file mode 100644 index 00000000..56eb226b --- /dev/null +++ b/classes/Tools/Storage/Driver/Supabase/SupabaseStorageSettings.php @@ -0,0 +1,41 @@ + ['mcloud-storage-supabase-url', 'MCLOUD_SUPABASE_URL', null], + 'key' => ['mcloud-storage-supabase-key', 'MCLOUD_SUPABASE_KEY', null], + 'bucket' => ['mcloud-storage-supabase-bucket', 'MCLOUD_SUPABASE_BUCKET', null], + 'settingsError' => ['mcloud-storage-supabase-settings-error', null, false], + ]; +} \ No newline at end of file diff --git a/classes/Tools/Storage/StorageContentHooks.php b/classes/Tools/Storage/StorageContentHooks.php index 3741317a..8df14fc5 100644 --- a/classes/Tools/Storage/StorageContentHooks.php +++ b/classes/Tools/Storage/StorageContentHooks.php @@ -18,8 +18,6 @@ use MediaCloud\Plugin\Tasks\TaskReporter; use MediaCloud\Plugin\Tools\Debugging\DebuggingToolSettings; -use MediaCloud\Plugin\Tools\Storage\Driver\S3\S3StorageSettings; -use MediaCloud\Plugin\Utilities\Environment; use MediaCloud\Plugin\Utilities\Logging\Logger; use function MediaCloud\Plugin\Utilities\anyEmpty; use function MediaCloud\Plugin\Utilities\arrayPath; diff --git a/classes/Tools/Storage/StorageTool.php b/classes/Tools/Storage/StorageTool.php index dfba79ca..127c07a2 100644 --- a/classes/Tools/Storage/StorageTool.php +++ b/classes/Tools/Storage/StorageTool.php @@ -2775,7 +2775,7 @@ private function hookMediaList() add_filter( 'manage_media_columns', function ( $cols ) { $cols["cloud"] = 'Cloud'; return $cols; - } ); + }, PHP_INT_MAX ); add_action( 'manage_media_custom_column', function ( $column_name, $id ) { @@ -2794,13 +2794,31 @@ function ( $column_name, $id ) { $lockIcon = $this->lockIcon(); //ILAB_PUB_IMG_URL.'/ilab-icon-lock.svg'; $lockImg = ( !empty($privacy) && $privacy !== StorageConstants::ACL_PUBLIC_READ ? "" : '' ); - echo "{$lockImg}" ; + echo "
" ; + } + } + + }, + PHP_INT_MAX, 2 ); add_filter( 'bulk_actions-upload', function ( $actions ) { @@ -2886,9 +2904,20 @@ function ( $redirect_to, $action_name, $post_ids ) { if ( get_current_screen()->base == 'upload' ) { ?> diff --git a/classes/Tools/Tool.php b/classes/Tools/Tool.php index bbb28e29..edf95183 100644 --- a/classes/Tools/Tool.php +++ b/classes/Tools/Tool.php @@ -501,6 +501,9 @@ public function registerSettings() { case 'text-field': $this->registerTextFieldSetting($option,$optionInfo['title'],$group, $description, $placeholder, $conditions, isset($optionInfo['default']) ? $optionInfo['default'] : null); break; + case 'color': + $this->registerColorFieldSetting($option, $optionInfo['title'], $group, $description, $conditions, isset($optionInfo['default']) ? $optionInfo['default'] : null); + break; case 'webhook': $this->registerWebhookSetting($option, $optionInfo['title'], $group, !empty($optionInfo['editable']), $description, $conditions, isset($optionInfo['default']) ? $optionInfo['default'] : null); break; diff --git a/classes/Tools/ToolsManager.php b/classes/Tools/ToolsManager.php index 06c2e9c7..8f5fc25d 100644 --- a/classes/Tools/ToolsManager.php +++ b/classes/Tools/ToolsManager.php @@ -196,13 +196,7 @@ function ( $value, $option, $old_value ) { 'mcloud-no-mbstring' ); } - NoticeManager::instance()->displayAdminNotice( - 'info', - "The team behind Media Cloud is launching a new product in April 2022 that's going to change the way you work with media in WordPress. Sign up to be notified when Preflight for WordPress is released.", - true, - 'mcloud-preflight-beta-sales-pitch', - 10 * 365 - ); + // NoticeManager::instance()->displayAdminNotice('info', "The team behind Media Cloud is launching a new product in April 2022 that's going to change the way you work with media in WordPress. Sign up to be notified when Preflight for WordPress is released.", true, 'mcloud-preflight-beta-sales-pitch', 10 * 365); add_action( 'admin_enqueue_scripts', function () { wp_enqueue_script( 'mcloud-admin-js', @@ -287,6 +281,9 @@ protected function setup() foreach ( $this->tools as $key => $tool ) { $tool->setup(); } + if ( is_admin() ) { + $this->hookMediaDetailButtons(); + } do_action( 'mediacloud/tasks/register' ); // MigrationsManager::instance()->displayMigrationErrors(); } @@ -714,13 +711,7 @@ public function addMenus( $networkMode, $networkAdminMenu ) 'https://support.mediacloud.press/' ); } - add_submenu_page( - 'media-cloud', - 'Preflight Beta', - 'Preflight Beta', - 'manage_options', - 'https://preflight.ju.mp' - ); + // add_submenu_page('media-cloud', 'Preflight Beta', 'Preflight Beta', 'manage_options', 'https://preflight.ju.mp'); foreach ( $this->tools as $key => $tool ) { $tool->registerHelpMenu( 'media-cloud', $networkMode, $networkAdminMenu ); } @@ -1077,5 +1068,148 @@ private function handlePinTool() 'link' => admin_url( "admin.php?page=media-cloud-settings&tool={$tool}" ), ] ); } + + //endregion + //region Media Library UI + private function hookMediaDetailButtons() + { + add_action( 'wp_enqueue_media', function () { + remove_action( 'admin_footer', 'wp_print_media_templates' ); + add_action( 'admin_footer', function () { + $mediaButtons = apply_filters( 'mediacloud/ui/media-detail-buttons', [] ); + $mediaLinks = apply_filters( 'mediacloud/ui/media-detail-links', [] ); + $toRemove = apply_filters( 'mediacloud/ui/media-detail-remove', [] ); + $renderedButtons = []; + foreach ( $mediaButtons as $button ) { + $dataAttrs = ''; + if ( isset( $button['data'] ) ) { + foreach ( $button['data'] as $key => $value ) { + $dataAttrs .= "data-{$key}='{$value}' "; + } + } + $thickbox = ( arrayPath( $button, 'thickbox', true ) === true ? 'ilab-thickbox' : '' ); + $classes = arrayPath( $button, 'class', '' ); + $style = arrayPath( $button, 'style', '' ); + + if ( arrayPath( $button, 'button_type', 'link' ) === 'button' ) { + $buttonHTML = ""; + } else { + $buttonHTML = "{$button['label']}"; + } + + if ( $button['type'] !== 'any' ) { + + if ( isset( $button['cloudonly'] ) && $button['cloudonly'] === true ) { + $buttonHTML = "<# if (data.s3 && data.type === '{$button['type']}') { #>\\n{$buttonHTML}\\n<# } #>"; + } else { + $buttonHTML = "<# if (data.type === '{$button['type']}') { #>\\n{$buttonHTML}\\n<# } #>"; + } + + } + $renderedButtons[] = $buttonHTML; + } + $mediaButtonsHTML = implode( ' ', $renderedButtons ); + $renderedLinks = []; + foreach ( $mediaLinks as $link ) { + $classes = arrayPath( $link, 'class', '' ); + $style = arrayPath( $link, 'style', '' ); + $dataAttrs = ''; + if ( isset( $link['data'] ) ) { + foreach ( $link['data'] as $key => $value ) { + $dataAttrs .= "data-{$key}='{$value}' "; + } + } + $thickbox = ( arrayPath( $link, 'thickbox', true ) === true ? 'ilab-thickbox' : '' ); + $linkHTML = "{$link['label']} |"; + if ( $link['type'] !== 'any' ) { + + if ( isset( $link['cloudonly'] ) && $link['cloudonly'] === true ) { + $linkHTML = "<# if (data.s3 && data.type === '{$link['type']}') { #>\\n{$linkHTML}\\n<# } #>"; + } else { + $linkHTML = "<# if (data.type === '{$link['type']}') { #>\\n{$linkHTML}\\n<# } #>"; + } + + } + $renderedLinks[] = $linkHTML; + } + $mediaLinksHTML = implode( '', $renderedLinks ); + $detailLinks = []; + foreach ( $mediaLinks as $link ) { + $classes = arrayPath( $link, 'class', '' ); + $style = arrayPath( $link, 'style', '' ); + $dataAttrs = ''; + if ( isset( $link['data'] ) ) { + foreach ( $link['data'] as $key => $value ) { + $dataAttrs .= "data-{$key}='{$value}' "; + } + } + $linkHTML = "{$link['label']}"; + if ( $link['type'] !== 'any' ) { + $linkHTML = "<# if (data.type === '{$link['type']}') { #>\\n{$linkHTML}\\n<# } #>"; + } + $detailLinks[] = $linkHTML; + } + $mediaDetailLinksHTML = implode( "\\n", $detailLinks ); + ob_start(); + wp_print_media_templates(); + $result = ob_get_clean(); + echo $result ; + ob_start(); + ?> + + ] + * : The maximum number of items to process, default is infinity. + * + * [--offset=