diff --git a/app/Console/Commands/MigrateOSCIPublicationOne.php b/app/Console/Commands/MigrateOSCIPublicationOne.php index 05607a3a1..edb7e2c10 100644 --- a/app/Console/Commands/MigrateOSCIPublicationOne.php +++ b/app/Console/Commands/MigrateOSCIPublicationOne.php @@ -29,43 +29,87 @@ class MigrateOSCIPublicationOne extends Command */ protected $description = 'Migrate an OSCI publication from a migration file to a website DigitalPublication'; + private function mediaFactory($imageData, $caption_html, $fallback_url) + { + + // Media uploads and relations + $imageUrl = isset($imageData->static_url) ? $imageData->static_url : $fallback_url ; + + $imageUuid = (string) Str::uuid(); + $imageUrlPath = parse_url($imageUrl, PHP_URL_PATH); + $imageExt = pathinfo($imageUrlPath, PATHINFO_EXTENSION); + $imageFilename = Str::random(10) . $imageExt; + + $imageName = $imageUuid . '/' . $imageFilename; + + $ctx = stream_context_create(array( + 'http' => + array( + 'timeout' => 120, + ) + )); + $imageContent = file_get_contents($imageUrl, false, $ctx); + + Storage::disk('s3')->put($imageName, $imageContent); + + // TODO: Use the CMS's onboard h/w helpers + $media = new Media([ + 'uuid' => $imageName, + 'width' => $imageData->width, + 'height' => $imageData->height, + 'filename' => $imageFilename, + 'locale' => 'en' + ]); + + $media->alt_text = 'Alt text for the image'; + $media->caption = $caption_html; + $media->save(); + + return $media; + } /** * configure $block (a Block model) with $data * */ private function configureFigBlock($figure, $block) { - switch ($figure->figure_type) { - case 'html_figure': + + $layers = isset($figure->layers_data) ? json_decode($figure->layers_data) : []; + + // TODO: if html_figure and html_content_src, that's a video block bby + switch (true) { + // Non-video embeds (HTML, mostly) become media_embed + case $figure->figure_type === 'html_figure' && !isset($figure->html_content_src): $block->type = 'media_embed'; + $block->content = [ + "size" => "m", + "embed_type" => "html", + "embed_code" => $figure->html_content, + "embed_height" => "400px", + "disable_placeholder" => true, + "caption" => $figure->caption_html + ]; + $block->save(); - if (isset($figure->html_content_src)) { - $block->content = [ - "size" => "m", - "embed_type" => "url", - "embed_url" => $figure->html_content_src, - "embed_height" => "400px", - "disable_placeholder" => true - ]; + break; - } else { - $block->content = [ - "size" => "m", - "embed_type" => "html", - "embed_code" => $figure->html_content, - "embed_height" => "400px", - "disable_placeholder" => true - ]; - } + // HTML embeds with a src are videos + case $figure->figure_type === 'html_figure' && isset($figure->html_content_src): + $block->type = 'video'; + $block->content = [ + "size" => "m", + "media_type" => "youtube", + "url" => $figure->html_content_src + ]; $block->save(); break; - case 'layered_image': - $block->type = 'layered_image_viewer'; - - // TODO: Instantiate the layers using fig_layer data, attaching each using fig_layer.data to get titles + layer # / order + // OSCI's layered_image with only one layer should just be an image + // TODO: Move IIP handling to a func and handle iip_asset in this `case` + case 'layered_image' && count($layers) === 1: + $block->type = 'image'; $block->content = [ "is_modal" => false, "is_zoomable" => false, @@ -77,21 +121,99 @@ private function configureFigBlock($figure, $block) "caption" => $figure->caption_html, "alt_text" => "" ]; + $block->save(); + + $imageData = Arr::first($layers); + $media = $this->mediaFactory($imageData, $figure->caption_html, $figure->fallback_url); + + // TODO: Use converted crop params + $block->medias()->attach($media->id, [ + 'locale' => 'en', + 'media_id' => $media->id, + 'metadatas' => '{"caption":null,"captionTitle": null, "altText":null,"video":null}', + 'role' => 'image', + 'crop' => 'desktop', + 'crop_x' => 0, + 'crop_y' => 0, + 'crop_w' => $imageData->width, + 'crop_h' => $imageData->height + ]); $block->save(); break; - case 'iip_asset': - $block->type = 'image'; + case 'layered_image': + $block->type = 'layered_image_viewer'; + $block->editor_name = 'default'; + $block->content = [ + "size" => "m", + "caption" => $figure->caption_html, + ]; + + $block->save(); + + foreach ($layers as $imageData) { + // Each layer (image or overview) is a child block of the layered image viewer block, so: + // - Create media record + move the asset (if not moved already) + // - Configure a layer block for this layer (image or overview) + // - Attach it to the layer viewer block + + $media = $this->mediaFactory($imageData, $figure->caption_html, $figure->fallback_url); + + $layerBlock = new Block(); + $layerBlock->blockable_id = $block->blockable_id; + $layerBlock->blockable_type = 'digitalPublicationArticles'; + $layerBlock->position = 1; // TODO: order + $layerBlock->parent_id = $block->id; + + // OSCI doesn't expose overlay vs image data so parse the URL filename + $imageUrl = isset($imageData->static_url) ? $imageData->static_url : $figure->fallback_url ; + $imageUrlPath = parse_url($imageUrl, PHP_URL_PATH); + $imageExt = pathinfo($imageUrlPath, PATHINFO_EXTENSION); + switch ($imageExt) { + case 'svg': + $layerBlock->child_key = 'layered_image_viewer_overlay'; + $layerBlock->type = 'layered_image_viewer_overlay'; + break; - // Media uploads and relations - $imagePath = isset($figure->static_url) ? $figure->static_url : $figure->fallback_url ; - $imageUuid = (string) Str::uuid(); - $imageFilename = Str::random(10) . '.jpg'; - $imageName = $imageUuid . '/' . $imageFilename; + default: + $layerBlock->child_key = 'layered_image_viewer_img'; + $layerBlock->type = 'layered_image_viewer_img'; + break; + } - Storage::disk('s3')->put($imageName, file_get_contents($imagePath)); + + $layerBlock->content = [ + "label" => $imageData->title + ]; + + $layerBlock->save(); + + // TODO: Use converted crop params + $layerBlock->medias()->attach($media->id, [ + 'locale' => 'en', + 'media_id' => $media->id, + 'metadatas' => '{"caption":null,"captionTitle": null, "altText":null,"video":null}', + 'role' => 'image', + 'crop' => 'desktop', + 'crop_x' => 0, + 'crop_y' => 0, + 'crop_w' => $imageData->width, + 'crop_h' => $imageData->height + ]); + $layerBlock->save(); + + $block->children()->save($layerBlock); + $block->save(); + } + + $block->save(); + break; + + case 'iip_asset': + // TODO: Properly handle the IIP asset (finally!) + $block->type = 'image'; $block->content = [ "is_modal" => false, "is_zoomable" => false, @@ -106,18 +228,11 @@ private function configureFigBlock($figure, $block) $block->save(); - $media = new Media([ - 'uuid' => $imageName, - 'width' => 2246, - 'height' => 1469, - 'filename' => $imageFilename, - 'locale' => 'en' - ]); + $imageData = Arr::first($layers); - $media->alt_text = 'Alt text for the image'; - $media->caption = $figure->caption_html; - $media->save(); + $media = $this->mediaFactory($imageData, $figure->caption_html, $figure->fallback_url); + // TODO: Use converted crop params $block->medias()->attach($media->id, [ 'locale' => 'en', 'media_id' => $media->id, @@ -126,8 +241,8 @@ private function configureFigBlock($figure, $block) 'crop' => 'desktop', 'crop_x' => 0, 'crop_y' => 0, - 'crop_w' => 2246, - 'crop_h' => 1469 + 'crop_w' => $imageData->width, + 'crop_h' => $imageData->height ]); $block->save(); @@ -135,7 +250,7 @@ private function configureFigBlock($figure, $block) case '360_slider': $block->type = '360_embed'; - $block->content = [ + $block->content = [ "size" => "m", "caption" => $figure->caption_html ]; @@ -147,7 +262,7 @@ private function configureFigBlock($figure, $block) case 'rti_viewer': // TODO: insert reader_url, figure # for this text + figure $block->type = 'video'; - $block->content = [ + $block->content = [ "caption" => $figure->caption_html ]; @@ -185,12 +300,13 @@ public function handle() // NB!: For each of these types in the admin UI it's impossible to validate the save without setting a date! + // Fetch this publication's title by its package name (eg, `caillebotte`) + // TODO: Handle italics / etc in the title + // TODO: Set publication date metadata + $pubs = DB::connection('osci_migration')->select("SELECT json_extract(publications.data,'$._title') as title FROM publications LEFT JOIN tocs ON tocs.package=publications.pub_id WHERE pub_id=:id", ['id' => $pubId]); $pub = Arr::first($pubs); - // TODO: Handle italics / etc in the title - // TODO: Set any other publication level metadata (date?) - $webPub = new DigitalPublication(); $webPub->title = 'Migrated ' . date('M j, Y') . ' | ' . $pub->title; $webPub->published = false; @@ -199,24 +315,50 @@ public function handle() $webPubId = $webPub->id; + // Now select all this pub's texts $texts = DB::connection('osci_migration')->select("SELECT coalesce(json_extract(data,'$._title'),'FIXME') as title,text_id FROM texts WHERE package=:pubId", ['pubId' => $pubId]); // Initialize the left value $lft = 1; - $blockQuery = "SELECT json_extract(blk.value,'$.html') AS html," . - "blk.id AS position," . - "json_extract(blk.value,'$.blockType') AS type," . - "json_extract(blk.value,'$.figure_type') AS figure_type," . - "json_extract(blk.value,'$.static_url') AS static_url," . - "json_extract(blk.value,'$.fallback_url') AS fallback_url," . - "json_extract(blk.value,'$.html_content') AS html_content," . - "json_extract(blk.value,'$.html_content_src') AS html_content_src," . - "coalesce(json_extract(blk.value,'$.caption_html'),'') AS caption_html " . - "FROM texts," . - "json_each(texts.data,'$.sections') AS sects," . - "json_each(sects.value,'$.blocks') AS blk " . - "WHERE texts.text_id=:textId"; + // Fetch sections and blocks by text ID (joined to their figure assets) + // NB: `layers_data` is a JSON array of layer asset objects + + // TODO: Add `toc_position`, `toc_parent` via json_tree against toc data + $blockQuery = <<_lft = $lft++; $webArticle->_rgt = $lft++; - // TODO: Join toc position via json_tree against toc data $webArticle->position = 0; $webArticle->save();