diff --git a/.env.example b/.env.example index 40a44412c5..a5d318c9af 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,9 @@ DB_DATABASE='homestead' DB_USERNAME='homestead' DB_PASSWORD='secret' +# Comma-delimited list +HIDE_FROM_TOURS= + TOURS_DB_CONNECTION='mysql' TOURS_DB_HOST='127.0.0.1' TOURS_DB_PORT=3306 diff --git a/app/Console/Commands/MigrateOSCIPublication.php b/app/Console/Commands/MigrateOSCIPublication.php new file mode 100644 index 0000000000..053a097eb0 --- /dev/null +++ b/app/Console/Commands/MigrateOSCIPublication.php @@ -0,0 +1,53 @@ +publications as $pub) { + $this->call('migrate:osci-publication-one', ['id' => $pub]); + } + } +} diff --git a/app/Console/Commands/MigrateOSCIPublicationMedia.php b/app/Console/Commands/MigrateOSCIPublicationMedia.php new file mode 100644 index 0000000000..a22c6d332a --- /dev/null +++ b/app/Console/Commands/MigrateOSCIPublicationMedia.php @@ -0,0 +1,53 @@ +publications as $pub) { + $this->call('migrate:osci-media-one', ['id' => $pub]); + } + } +} diff --git a/app/Console/Commands/MigrateOSCIPublicationMediaOne.php b/app/Console/Commands/MigrateOSCIPublicationMediaOne.php new file mode 100644 index 0000000000..0c1a7fd59b --- /dev/null +++ b/app/Console/Commands/MigrateOSCIPublicationMediaOne.php @@ -0,0 +1,98 @@ +argument('id'); + $this->comment('Starting ' . $pubId . '...'); + + # TODO: Query 360s too + $assetsQuery = <<select($assetsQuery, ['pubId' => $pubId]); + + foreach ($assets as $asset) { + // TODO: Check the asset type and handle 360 spins differently + $key = trim($asset->image_ident, '/'); + $asset_key = preg_replace('/^\/?osci\//', '', $asset->image_ident); + $jpg_key = preg_replace('/\.ptif$/', '.jpg', $key); + + if (!Storage::disk('osci_s3')->fileExists($asset_key)) { + $this->info("OSCI_S3_BUCKET did not contain {$asset_key}!"); + continue; + } + + if (Storage::disk('osci_s3')->fileExists($jpg_key)) { + $this->info("OSCI_S3_BUCKET already contains {$jpg_key}, skipping this asset"); + continue; + } + + $this->info("Compressing {$asset_key}"); + + $content = Storage::disk('osci_s3')->get($asset_key); + + $image = new Imagick(); + $image->readImageBlob($content); + + // Iterate zoom layers to find a 10k-pixels-a-side page geometry + while ($image->hasPreviousImage()) { + $size = $image->getImagePage(); + + if ($size['height'] > 10000 && $size['width'] > 10000) { + $image->nextImage(); + break; + } + $image->previousImage(); + } + + $image->setImageFormat('jpeg'); + $compressed = $image->getImageBlob(); + + $res = Storage::disk('osci_s3')->put($jpg_key, $compressed); + + if (!$res) { + $this->info("There was an error uploading the compressed file to {$jpg_key}"); + continue; + } + + echo "Uploaded {$jpg_key}\n"; + } + $this->comment('...' . $pubId . ' done!'); + } +} diff --git a/app/Console/Commands/MigrateOSCIPublicationOne.php b/app/Console/Commands/MigrateOSCIPublicationOne.php index fca40b5bbb..60c680e1c0 100644 --- a/app/Console/Commands/MigrateOSCIPublicationOne.php +++ b/app/Console/Commands/MigrateOSCIPublicationOne.php @@ -3,10 +3,16 @@ namespace App\Console\Commands; use Illuminate\Console\Command; -use App\Repositories\Api\PublicationRepository; +use Illuminate\Http\Client\ConnectionException; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\Http; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; use App\Models\DigitalPublication; use App\Models\DigitalPublicationArticle; use App\Models\Vendor\Block; +use A17\Twill\Models\Media; class MigrateOSCIPublicationOne extends Command { @@ -15,26 +21,353 @@ class MigrateOSCIPublicationOne extends Command * * @var string */ - protected $signature = 'migrate:osci-publication {id : Publication ID}'; + protected $signature = 'migrate:osci-publication-one {id : Publication ID}'; /** * The console command description. * * @var string */ - protected $description = 'Migrate an OSCI publication to a website DigitalPublication'; + protected $description = 'Migrate an OSCI publication from a migration file to a website DigitalPublication'; - protected $repository; + /** + * mediaFactory + * + * Fetches asset for this figure layer and registers it as Media, + * applying caption and using OSCI's fallback URL + * + * @param imageData - JSON of image data for this layer + * @param caption_html - HTML of caption data + * @param fallback_url - Fallback image to use for this layer + * + */ + private function mediaFactory($imageData, $caption_html, $fallback_url) + { + $imageUrl = ""; + $imageContent = false; + + switch ($imageData["type"]) { + case "iip": + $img_ident = trim($imageData["image_ident"], '/'); + $img_key = preg_replace('/\.ptif$/', '.jpg', $img_ident); + + $bucket = config('aic.osci_s3_bucket'); + $imageUrl = "s3://{$bucket}/{$img_key}"; + + if (!Storage::disk('osci_s3')->fileExists($img_key)) { + echo "Error -- could not fetch {$imageUrl}. Has `migrate:osci-media` been run for this publication? Skipping this media\n"; + return null; + } + + $imageContent = Storage::disk('osci_s3')->get($img_key); + break; + + case "image": + $imageUrl = $imageData["static_url"]; + break; + + case "svg": + $imageUrl = $imageData["svg_path"]; + break; + + default: + $imageUrl = $fallback_url; + break; + } + + // TODO: Load 360-zip URL from this from disk('osci_s3') + + // Construct the UUID for this asset and preserve filename info + $imageUuid = (string) Str::uuid(); + $imageUrlPath = parse_url($imageUrl, PHP_URL_PATH); + $imageFilename = pathinfo($imageUrlPath, PATHINFO_FILENAME); + $imageFilename = Str::random(10) . '-' . sanitizeFilename($imageFilename); + + $imageName = $imageUuid . '/' . $imageFilename; + + $retries = 0; + while ($imageContent === false && $retries < 5) { + // Fetch the URL, retrying if it's not a 404 + try { + $resp = Http::get($imageUrl); + } catch (ConnectionException $e) { + $retries++; + usleep(20); + continue; + } + + if ($resp->notFound()) { + break; + } + + if ($resp->serverError()) { + $retries++; + usleep(20); + continue; + } + + $imageContent = $resp->body(); + } + + if ($imageContent === false || $imageContent === "") { + echo "Error could not fetch {$imageUrl} after {$retries} attempts! This media won't be migrated"; + return null; + } + + if ($imageData['type'] == 'svg') { + // TODO: Apply converted PNGs from S3? + // Manually apply h/w for SVGs + $width = ceil($imageData['width']); + $height = ceil($imageData['height']); + } else { + [$width, $height] = getimagesizefromstring($imageContent); + } + + Storage::disk('s3')->put($imageName, $imageContent); + + $media = new Media([ + 'uuid' => $imageName, + 'width' => $width, + 'height' => $height, + 'filename' => $imageFilename, + 'locale' => 'en' + ]); + + $media->alt_text = 'Alt text for the image'; + $media->caption = $caption_html; + $media->save(); + $media->addTag('Publication Image'); + $media->save(); + + return $media; + } + + private function configureImageFigure($block, $data, $layers) + { + $block->type = 'image'; + $block->content = [ + "is_modal" => false, + "is_zoomable" => false, + "size" => "s", + "use_contain" => true, + "use_alt_background" => true, + "image_link" => null, + "hide_figure_number" => false, + "caption" => $data->caption_html, + "alt_text" => "" + ]; + + $block->save(); + + $imageData = Arr::first($layers); + $media = $this->mediaFactory($imageData, $data->caption_html, $data->fallback_url); + + if ($media === null) { + return; + } + + // 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' => $media->width, + 'crop_h' => $media->height + ]); + + $block->save(); + } + + private function configureVideoFigure($block, $data) + { + $block->type = 'video'; + $block->content = [ + "size" => "s", + "media_type" => "youtube", + "url" => $data->html_content_src + ]; + + $block->save(); + } + + private function configureHTMLFigure($block, $data) + { + $block->type = 'media_embed'; + $block->content = [ + "size" => "s", + "embed_type" => "html", + "embed_code" => $data->html_content, + "embed_height" => "400px", + "disable_placeholder" => true, + "caption" => $data->caption_html + ]; + + $block->save(); + } + + // Attaches a new image layer based on `data` + private function createAttachImageLayer($block, $figure_data, $layer_data) + { + // Each layer (image or overlay) 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 + + $layerBlock = new Block(); + $layerBlock->blockable_id = $block->blockable_id; + $layerBlock->blockable_type = 'digitalPublicationArticles'; + $layerBlock->parent_id = $block->id; + + // Configure this as an image or overlay and set position + + if ($layer_data['type'] == 'svg' || pathinfo($layer_data['static_url'], PATHINFO_EXTENSION) === 'png') { + $layerBlock->child_key = 'layered_image_viewer_overlay'; + $layerBlock->type = 'layered_image_viewer_overlay'; + } else { + $layerBlock->child_key = 'layered_image_viewer_img'; + $layerBlock->type = 'layered_image_viewer_img'; + } + + $layerBlock->position = $layer_data['layer_num']; + $layerBlock->content = [ + "label" => $layer_data['title'] + ]; + + $layerBlock->save(); + + $media = $this->mediaFactory($layer_data, $figure_data->caption_html, $figure_data->fallback_url); + if ($media !== null) { + // 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' => $media->width, + 'crop_h' => $media->height + ]); + + $layerBlock->save(); + } + + $block->children()->save($layerBlock); + $block->save(); + } + + private function configureLayeredImageFigure($block, $data, $layers) + { + $block->type = 'layered_image_viewer'; + $block->editor_name = 'default'; + $block->content = [ + "size" => "s", + "caption" => $data->caption_html, + ]; + + $block->save(); + + $figure_opts = json_decode($data->figure_opts, true); + $imageLayerIds = $figure_opts['baseLayerPreset'] ?? []; + $overlayLayerIds = $figure_opts['annotationPreset'] ?? []; + + foreach ($layers as $layerData) { + $this->createAttachImageLayer($block, $data, $layerData); + } + $block->save(); + } + + private function configure360EmbedFigure($block, $data) + { + $block->type = '360_embed'; + $block->content = [ + "size" => "s", + "caption" => $data->caption_html + ]; + + // TODO: use this->mediaFactory to fetch + attach 360 zip + $block->save(); + } + + /** + * configures $block (a Block model) with $figure + * + **/ + private function configureFigureBlock($block, $figure) + { + $layers = isset($figure->layers_data) ? json_decode($figure->layers_data, true) : []; + + // Type-sense the figure and apply the appropriate CMS block type -- each just returns early + switch (true) { + // Non-video embeds (HTML, mostly) become media_embed + case $figure->figure_type === 'html_figure' + && !isset($figure->html_content_src): + $this->configureHTMLFigure($block, $figure); + return; + + // HTML embeds with a src are videos + case $figure->figure_type === 'html_figure' + && isset($figure->html_content_src): + $this->configureVideoFigure($block, $figure); + return; + + // OSCI's layered_image with only one layer should just be an image + case $figure->figure_type === 'layered_image' + && count($layers) === 1: + $this->configureImageFigure($block, $figure, $layers); + return; + + // Layered images are just that + case $figure->figure_type === 'layered_image': + $this->configureLayeredImageFigure($block, $figure, $layers); + return; + + // IIP Asset images are also images + case $figure->figure_type === 'iip_asset': + $this->configureImageFigure($block, $figure, $layers); + return; + + case $figure->figure_type === '360_slider': + $this->configure360EmbedFigure($block, $figure); + return; + + // RTI_Viewers will need other handling, so leave a placeholder.. + case $figure->figure_type === 'rti_viewer': + // TODO: insert reader_url, figure # for this text + figure + $block->type = 'video'; + $block->content = [ + "caption" => $figure->caption_html + ]; + + $block->save(); + return; + + default: + return; + } + } /** - * Create a new command instance. + * configure $block (a Block model) with $data * - * @return void + * NB: "figure" here can be many different block types */ - public function __construct(PublicationRepository $repository) + private function configureBlock($data, $block) { - $this->repository = $repository; - parent::__construct(); + switch ($data->type) { + case 'figure': + $this->configureFigureBlock($block, $data); + break; + default: + $block->content = [ 'paragraph' => $data->html ]; + $block->type = 'paragraph'; + break; + } } /** @@ -46,37 +379,144 @@ public function handle() { $pubId = $this->argument('id'); - $apiPub = $this->repository->getById($pubId); + // 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: webPub->header_title_display = italicized OSCI 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); + + if (!$pub) { + $this->error('No publication with the ID: ' . $pubId); + return; + } + $webPub = new DigitalPublication(); - $webPub->title = 'Migrated ' . date('M j, Y') . ' | ' . $apiPub->title; + $webPub->title = 'Migrated ' . date('M j, Y') . ' | ' . $pub->title; $webPub->published = false; $webPub->is_dsc_stub = false; $webPub->save(); + $this->comment('Starting ' . $pub->title . '...'); + + $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; - foreach ($apiPub->articles as $apiArticle) { + // 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 = <<title = $apiArticle->title; + $webArticle->type = "text"; + $webArticle->title = $text->title; $webArticle->published = false; - $webArticle->digital_publication_id = $webPub->id; - $webArticle->position = $apiArticle->weight; + $webArticle->digital_publication_id = $webPubId; + $webArticle->date = date('M j, Y'); + $webArticle->updated_at = date('M j, Y'); + $webArticle->created_at = date('M j, Y'); + $webArticle->_lft = $lft++; + $webArticle->_rgt = $lft++; + + $webArticle->position = 0; + $webArticle->save(); - $block = new Block(); - $block->blockable_id = $webArticle->id; - $block->blockable_type = 'App\Models\DigitalPublicationArticle'; + $blocks = DB::connection('osci_migration')->select($blockQuery, ['textId' => $text->text_id]); + + $order = 0; + + $heroMedia = false; + $heroMediaArray = []; + + foreach ($blocks as $blk) { + $block = new Block(); + $block->blockable_id = $webArticle->id; + $block->blockable_type = 'App\Models\DigitalPublicationArticle'; - /* If we decide to break up the text into multiple paragraph blocks, increment - * the `position` value to keep the order in tact. - */ - $block->position = 0; + $block->position = $order; - $block->content = ['paragraph' => str_replace(['content)]; - $block->type = 'paragraph'; - $block->save(); - $webArticle->blocks()->save($block); + $this->configureBlock($blk, $block); + + // If this is the first image block, save the image to add as the article hero image later + if ($block->type == 'image' && !$heroMedia) { + $heroMedia = $block->medias->first(); + if ($heroMedia) { + $heroMediaArray['locale'] = 'en'; + $heroMediaArray['role'] = 'hero'; + $heroMediaArray['media_id'] = $heroMedia->pivot->media_id; + $heroMediaArray['metadatas'] = $heroMedia->pivot->metadatas; + $heroMediaArray['crop'] = 'default'; + $heroMediaArray['crop_x'] = $heroMedia->pivot->crop_x; + $heroMediaArray['crop_y'] = $heroMedia->pivot->crop_y; + $heroMediaArray['crop_w'] = $heroMedia->pivot->crop_w; + $heroMediaArray['crop_h'] = $heroMedia->pivot->crop_h; + } + } + $webArticle->blocks()->save($block); + + $order += 1; + } + + $webArticle->save(); + + // If we grabbed an image to use for the hero, save it + if ($heroMedia) { + $webArticle->medias()->attach($heroMedia->id, $heroMediaArray); + $webArticle->save(); + } $webPub->articles()->save($webArticle); + $this->info($pub->title . ': ' . $webArticle->title); } + $webPub->save(); + $this->comment('...' . $pub->title . ' done!'); } } diff --git a/app/Helpers/StringHelpers.php b/app/Helpers/StringHelpers.php index 085896788b..7e95df30e5 100644 --- a/app/Helpers/StringHelpers.php +++ b/app/Helpers/StringHelpers.php @@ -205,6 +205,82 @@ public static function convertReferenceLinks($text, $_collectedReferences) return [$text, $_collectedReferences]; } + public static function convertReferenceText($dom, $node) + { + global $_paragraphCount; + + $_paragraphCount++; + + $newNode = $dom->createElement('p'); + $newNode->setAttribute('id', 'p-' . $_paragraphCount); + $newNode->setAttribute('class', 'p--linked'); + + $refSpan = $dom->createElement('span'); + $refSpan->setAttribute('class', 'p--linked__ref'); + + $textSpan = $dom->createElement('span'); + $textSpan->setAttribute('class', 'p--linked__text'); + + $refAnchor = $dom->createElement('a'); + $refAnchor->setAttribute('href', '#p-' . $_paragraphCount); + $refAnchor->setAttribute('class', 'reset'); + + $refText = $dom->createTextNode($_paragraphCount); + $refAnchor->appendChild($refText); + $refSpan->appendChild($refAnchor); + + foreach ($node->childNodes as $childNode) { + $textSpan->appendChild(clone $childNode); + } + + $newNode->appendChild($textSpan); + $newNode->appendChild($refSpan); + + return $newNode; + } + + public static function parseFootnotes($oldContent, $limiter = null) + { + global $_collectedReferences; + if (!isset($_collectedReferences)) { + $_collectedReferences = []; + } + list($newContent, $_collectedReferences) = static::convertReferenceLinks($oldContent, $_collectedReferences); + + global $_paragraphCount; + + if (isset($_paragraphCount)) { + $oldInternalErrors = libxml_use_internal_errors(true); + + $dom = new \DomDocument(); + $dom->loadHTML('' . $newContent, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); + + $xpath = new \DOMXpath($dom); + + // If a limiter is set, restrict to paragraphs within that id + if ($limiter) { + $nodes = $xpath->query("//div[@id='$limiter']//p"); + } else { + // No limiter, select all p elements + $nodes = $xpath->query('//p'); + } + + $wrapper = $dom->createElement('div'); + $wrapper->setAttribute('class', 'wrapper'); + + foreach ($nodes as $node) { + $newNode = static::convertReferenceText($dom, $node); + $node->parentNode->replaceChild($newNode, $node); + } + + $newContent = $dom->saveHTML($dom); + + libxml_clear_errors(); + libxml_use_internal_errors($oldInternalErrors); + } + return $newContent; + } + public static function properTitleCase($string) { // Exceptions in lowercase will be converted to lowercase diff --git a/app/Http/Controllers/Admin/DigitalPublicationArticleController.php b/app/Http/Controllers/Admin/DigitalPublicationArticleController.php index 25b0d44d9b..8b175b0387 100644 --- a/app/Http/Controllers/Admin/DigitalPublicationArticleController.php +++ b/app/Http/Controllers/Admin/DigitalPublicationArticleController.php @@ -5,6 +5,7 @@ use A17\Twill\Http\Controllers\Admin\NestedModuleController; use App\Repositories\DigitalPublicationRepository; use App\Http\Controllers\Admin\Behaviors\IsNestedModule; +use Illuminate\Support\Collection; class DigitalPublicationArticleController extends NestedModuleController { @@ -27,9 +28,9 @@ class DigitalPublicationArticleController extends NestedModuleController 'edit_link' => true, 'field' => 'title', ], - 'type' => [ + 'article_type' => [ 'title' => 'Type', - 'field' => 'type', + 'field' => 'articleType', 'present' => true, ], ]; @@ -75,6 +76,37 @@ protected function indexData($request) ); } + protected function getBrowserData($prependScope = []) + { + $query = $this->repository->withDepth()->defaultOrder()->where('article_type', '!=', 'grouping'); + + $search = $this->request->get('search', $prependScope['search'] ?? null); + if ($search) { + $query->whereRaw('LOWER(title) LIKE ?', ['%' . strtolower($search) . '%']); + } + + $digitalPublicationId = $this->request->get('digitalPublication', $prependScope['digitalPublication'] ?? null); + if ($digitalPublicationId) { + $query->where('digital_publication_id', $digitalPublicationId); + } + + $articles = $query->get(); + + $formattedArticles = $articles->map(function ($article) use ($digitalPublicationId) { + return [ + 'id' => $article->id, + 'name' => $digitalPublicationId ? $article->title : ($article->digitalPublication ? $article->digitalPublication->title . ' - ' . $article->title : $article->title), + 'edit' => route('admin.collection.articles_publications.digitalPublications.articles.edit', [ + 'digitalPublication' => $article->digital_publication_id, + 'article' => $article->id + ]), + 'endpointType' => 'digitalPublicationArticles', + 'thumbnail' => $article->defaultCmsImage(['w' => 100, 'h' => 100]), + ]; + }); + + return ['data' => $formattedArticles->values()->toArray()]; + } protected function transformIndexItems($items) { // If we're in the browser, don't transform the items diff --git a/app/Http/Controllers/Admin/DigitalPublicationController.php b/app/Http/Controllers/Admin/DigitalPublicationController.php index 35dd2aeebc..56af88f4df 100644 --- a/app/Http/Controllers/Admin/DigitalPublicationController.php +++ b/app/Http/Controllers/Admin/DigitalPublicationController.php @@ -30,17 +30,8 @@ protected function formData($request) { $item = $this->repository->getById(request('digitalPublication') ?? request('id')); $baseUrl = '//' . config('app.url') . '/digital-publications/' . $item->id . '/'; - $heroBackgroundColors = collect([ - '#282829', - '#422E22', - '#284725', - '#1E3F49', - '#1C2454', - '#35295A', - '#711F2A', - '#983820', - '#E19E26', - ])->mapWithKeys(fn ($hexColor) => [$hexColor => $hexColor]); + $heroBackgroundColors = collect(config('aic.branding.digital_publications.colors')) + ->mapWithKeys(fn ($hexColor) => [$hexColor => $hexColor]); return [ 'baseUrl' => $baseUrl, diff --git a/app/Http/Controllers/DigitalPublicationArticleController.php b/app/Http/Controllers/DigitalPublicationArticleController.php index 3c1749fa4c..d2b4deed9d 100644 --- a/app/Http/Controllers/DigitalPublicationArticleController.php +++ b/app/Http/Controllers/DigitalPublicationArticleController.php @@ -49,8 +49,9 @@ public function show($pubId, $pubSlug, $id, $slug = null) 'item' => $item, 'contrastHeader' => false, 'borderlessHeader' => false, - 'unstickyHeader' => false, + 'unstickyHeader' => true, 'canonicalUrl' => $canonicalPath, + 'bgcolor' => $item->digitalPublication->bgcolor, ]); } } diff --git a/app/Http/Controllers/InteractiveFeatureExperiencesController.php b/app/Http/Controllers/InteractiveFeatureExperiencesController.php index 85889be3b5..c6a1673a1e 100644 --- a/app/Http/Controllers/InteractiveFeatureExperiencesController.php +++ b/app/Http/Controllers/InteractiveFeatureExperiencesController.php @@ -85,7 +85,7 @@ protected function show($slug) { if (in_array('kiosk', request()->segments())) { return redirect()->action( - 'InteractiveFeatureExperiencesController@showKiosk', + [self::class, 'showKiosk'], ['slug' => $slug] ); } @@ -132,9 +132,7 @@ protected function showKiosk($slug) $this->seo->setTitle($experience->title); - $view = 'site.experienceDetailKiosk'; - - return view($view, [ + return view('site.experienceDetailKiosk', [ 'contrastHeader' => true, 'experience' => $experience ]); diff --git a/app/Http/Controllers/MagazineIssueController.php b/app/Http/Controllers/MagazineIssueController.php index a90912d0f6..fd94dfdb14 100644 --- a/app/Http/Controllers/MagazineIssueController.php +++ b/app/Http/Controllers/MagazineIssueController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Repositories\MagazineIssueRepository; +use Illuminate\Support\Carbon; class MagazineIssueController extends FrontController { @@ -24,13 +25,7 @@ public function latest() public function show($id, $slug = null, $isRequestForLatest = false) { - $issues = $this->repository->published()->ordered()->get(); - $item = $issues->where('id', (int) $id)->first(); - - if (!$item) { - abort(404); - } - + $item = $this->repository->findOrFail($id); $canonicalPath = route('magazine-issues.show', ['id' => $item->id, 'slug' => $item->getSlug()]); if (!$isRequestForLatest) { @@ -43,11 +38,28 @@ public function show($id, $slug = null, $isRequestForLatest = false) $this->seo->setDescription($item->meta_description ?: $item->list_description); // Issues have no blocks $this->seo->setImage($item->imageFront('hero')); + $issuesByYear = $this->repository + ->published() + ->ordered() + ->get() + ->mapToGroups(function ($issue) { + $year = (new Carbon($issue->publish_start_date))->year; + return [$year => $issue]; + }); + $issueArchive = ['title' => 'Archive', 'items' => []]; + foreach ($issuesByYear as $year => $yearIssues) { + $byYear = ['title' => (string)$year]; + foreach ($yearIssues as $issue) { + $byYear['items'][] = ['title' => $issue->title, 'url' => route('magazine-issues.show', [$issue])]; + } + $issueArchive['items'][] = $byYear; + } + return view('site.magazineIssueDetail', [ 'item' => $item, 'contrastHeader' => false, 'borderlessHeader' => false, - 'issues' => $issues, + 'issues' => [$issueArchive], 'welcomeNote' => $this->repository->getWelcomeNote($item), 'canonicalUrl' => $canonicalPath, ]); diff --git a/app/Http/Requests/Admin/DigitalPublicationArticleRequest.php b/app/Http/Requests/Admin/DigitalPublicationArticleRequest.php index 6ada31959a..28dff938f4 100644 --- a/app/Http/Requests/Admin/DigitalPublicationArticleRequest.php +++ b/app/Http/Requests/Admin/DigitalPublicationArticleRequest.php @@ -14,6 +14,7 @@ public function rulesForCreate() public function rulesForUpdate() { return [ + 'article_type' => 'required', 'date' => 'required', ]; } diff --git a/app/Http/Requests/Admin/SlideRequest.php b/app/Http/Requests/Admin/SlideRequest.php index ceafaf86d8..b2557a1fa7 100644 --- a/app/Http/Requests/Admin/SlideRequest.php +++ b/app/Http/Requests/Admin/SlideRequest.php @@ -19,7 +19,7 @@ public function rulesForUpdate() return [ 'attract_title' => 'max:150', 'attract_subhead' => 'max:150', - 'section_title' => 'max:150', + 'article_title' => 'max:150', 'caption' => 'max:150', 'interstitial_headline' => 'max:150', 'body_copy' => 'max:500', diff --git a/app/Http/Resources/Slide.php b/app/Http/Resources/Slide.php index d0309a3c37..13214aa355 100644 --- a/app/Http/Resources/Slide.php +++ b/app/Http/Resources/Slide.php @@ -121,10 +121,10 @@ protected function getInterstitalAttributes() $this->media = $this->interstitialExperienceImage; return [ - 'title' => $this->section_title, + 'title' => $this->article_title, 'copy' => $this->body_copy, '__option_body_copy' => !empty($this->body_copy), - '__option_section_title' => !empty($this->section_title), + '__option_article_title' => !empty($this->article_title), '__option_background_image' => count($this->interstitialExperienceImage) > 0, '__option_headline' => !empty($this->interstitial_headline), ]; diff --git a/app/Models/Api/Asset.php b/app/Models/Api/Asset.php index 884595a599..e379f1d9d2 100644 --- a/app/Models/Api/Asset.php +++ b/app/Models/Api/Asset.php @@ -148,7 +148,7 @@ public function getIconAfterAttribute() public function scopeMultimediaAssets($query) { $params = [ - 'resources' => ['images', 'sounds', 'texts', 'videos', 'sections', 'sites'] + 'resources' => ['images', 'sounds', 'texts', 'videos', 'articles', 'sites'] ]; return $query->rawQuery($params); diff --git a/app/Models/Api/Exhibition.php b/app/Models/Api/Exhibition.php index 5afd146c93..a15f74613f 100644 --- a/app/Models/Api/Exhibition.php +++ b/app/Models/Api/Exhibition.php @@ -136,11 +136,6 @@ public function getDateEndAttribute() public function getIsClosingSoonAttribute() { - // If the start and end dates are overriden, don't consider this exhibition as closing soon - if ($this->date_display_override) { - return false; - } - if (!empty($this->dateEnd)) { if (empty($this->dateStart) || Carbon::now()->gt($this->dateStart->endOfDay())) { return Carbon::now()->between($this->dateEnd->endOfDay()->subWeeks(2), $this->dateEnd->endOfDay()); diff --git a/app/Models/Api/Publication.php b/app/Models/Api/Publication.php index 37e4161427..1eccb6402d 100644 --- a/app/Models/Api/Publication.php +++ b/app/Models/Api/Publication.php @@ -12,8 +12,8 @@ class Publication extends BaseApiModel 'search' => '/api/v1/publications/search' ]; - public function sections() + public function articles() { - return $this->hasMany(\App\Models\Api\Section::class, 'section_ids'); + return $this->hasMany(\App\Models\Api\Section::class, 'article_ids'); } } diff --git a/app/Models/Behaviors/HasApiRelations.php b/app/Models/Behaviors/HasApiRelations.php index 3ba4ceaa5e..ae6dc067dd 100644 --- a/app/Models/Behaviors/HasApiRelations.php +++ b/app/Models/Behaviors/HasApiRelations.php @@ -106,8 +106,11 @@ public function getLocalApiMapping($items, $apiElements) { // Find locally selected objects return $items->filter(function ($relatedElement) use ($apiElements) { - $apiRelationElement = \App\Models\ApiRelation::where('id', $relatedElement->related_id)->first(); - $result = $apiElements->where('id', $apiRelationElement->datahub_id)->first(); + $apiRelationElement = \App\Models\ApiRelation::where('id', $relatedElement->related_id) + ->first() + ?->datahub_id; + + $result = $apiElements->where('id', $apiRelationElement)->first(); if ($result) { $result->position = $relatedElement->position; diff --git a/app/Models/Behaviors/HasAutoRelated.php b/app/Models/Behaviors/HasAutoRelated.php index 6286c628de..b95aac9701 100644 --- a/app/Models/Behaviors/HasAutoRelated.php +++ b/app/Models/Behaviors/HasAutoRelated.php @@ -27,6 +27,8 @@ public function related($item) 'genericPages', 'events', 'exhibitions', + 'artworks', + 'digitalPublicationArticles' ]; // Get all blocks that have this model as a related item @@ -40,8 +42,15 @@ public function related($item) }); foreach ($relatedBlockItems as $relatedBlockItem) { - if ($relatedBlockItem->blockable && method_exists($relatedBlockItem->blockable, "getApiModel") && $relatedBlockItem->blockable->getApiModel()) { - $relatedBlockable = $relatedBlockItem->blockable->getApiModelFilledCached(); + $relatedBlockable = null; + + if ($relatedBlockItem->blockable) { + if (method_exists($relatedBlockItem->blockable, "getApiModel")) { + $apiModel = $relatedBlockItem->blockable->getApiModel(); + if ($apiModel) { + $relatedBlockable = $relatedBlockItem->blockable->getApiModelFilledCached(); + } + } } $relatedItems[] = $relatedBlockable ?? $relatedBlockItem->blockable; @@ -55,7 +64,8 @@ public function related($item) 'exhibitions', 'experiences', 'digitalPublications', - 'videos' + 'digitalPublicationsArticles', + 'videos', ]; // Set scope to only include sidebar items that can be related to diff --git a/app/Models/Behaviors/HasFeaturedRelated.php b/app/Models/Behaviors/HasFeaturedRelated.php index 852f3fa252..6bd978c7fa 100644 --- a/app/Models/Behaviors/HasFeaturedRelated.php +++ b/app/Models/Behaviors/HasFeaturedRelated.php @@ -8,6 +8,7 @@ use App\Models\Api\Exhibition; use App\Models\Experience; use App\Models\DigitalPublication; +use App\Models\DigitalPublicationArticle; use App\Models\Video; use Illuminate\Support\Facades\Cache; use Carbon\Carbon; @@ -72,6 +73,7 @@ public function getCustomRelatedItems() 'exhibitions' => true, // API! 'experiences' => false, 'digitalPublications' => false, + 'digitalPublicationArticles' => false, 'videos' => false, ]) ?? collect([]); @@ -185,6 +187,12 @@ private function getLabeledRelatedItems($relatedItems) $label = 'Digital Publication'; $type = 'generic'; + break; + case DigitalPublicationArticle::class: + // No tag + $label = 'Digital Publication Article'; + $type = 'generic'; + break; case Video::class: // Tag is "Video" diff --git a/app/Models/DigitalPublication.php b/app/Models/DigitalPublication.php index 7ba7365a69..70aeef218b 100644 --- a/app/Models/DigitalPublication.php +++ b/app/Models/DigitalPublication.php @@ -40,9 +40,7 @@ class DigitalPublication extends AbstractModel 'sponsor_display', 'welcome_note_display', 'cite_as', - 'header_title_display', 'header_subtitle_display', - 'sidebar_title_display', 'bgcolor', 'toggle_autorelated', ]; diff --git a/app/Models/DigitalPublicationArticle.php b/app/Models/DigitalPublicationArticle.php index 0697a63b38..a16a6bfd67 100644 --- a/app/Models/DigitalPublicationArticle.php +++ b/app/Models/DigitalPublicationArticle.php @@ -37,7 +37,7 @@ class DigitalPublicationArticle extends AbstractModel implements Sortable 'hide_title', 'list_description', 'date', - 'type', + 'article_type', 'listing_display', 'suppress_listing', 'heading', @@ -62,12 +62,12 @@ class DigitalPublicationArticle extends AbstractModel implements Sortable 'date' => 'date', 'publish_start_date' => 'date', 'published' => 'boolean', - 'type' => DigitalPublicationArticleType::class, + 'article_type' => DigitalPublicationArticleType::class, ]; public $attributes = [ 'published' => false, - 'type' => 'entry' + 'article_type' => 'text', ]; public $mediasParams = [ @@ -78,6 +78,12 @@ class DigitalPublicationArticle extends AbstractModel implements Sortable 'ratio' => 16 / 9, ], ], + 'square' => [ + [ + 'name' => 'default', + 'ratio' => 1, + ], + ], 'special' => [ [ 'name' => 'default', @@ -151,6 +157,15 @@ public function getUrlAttribute() return $this->present()->getCanonicalUrl(); } + public function getUrlWithoutSlugAttribute() + { + return route('collection.publications.digital-publications-articles.show', [ + 'pubId' => $this->digital_publication_id, + 'pubSlug' => $this->digitalPublication->slug, + 'id' => $this->id, + ]); + } + protected function transformMappingInternal() { return [ @@ -211,11 +226,11 @@ protected function transformMappingInternal() } ], [ - 'name' => 'type', + 'name' => 'article_type', 'doc' => 'Type of Article', 'type' => 'string', 'value' => function () { - return $this->present()->type; + return $this->present()->articleType; }, ], [ diff --git a/app/Models/Exhibition.php b/app/Models/Exhibition.php index e9bbd854d8..e48c288ffe 100644 --- a/app/Models/Exhibition.php +++ b/app/Models/Exhibition.php @@ -57,6 +57,7 @@ class Exhibition extends AbstractModel 'exhibition_message', 'exhibition_location', 'status_override', + 'type_override', 'list_description', 'cms_exhibition_type', 'hero_caption', diff --git a/app/Models/Experience.php b/app/Models/Experience.php index cce9156480..c116fc534e 100644 --- a/app/Models/Experience.php +++ b/app/Models/Experience.php @@ -15,12 +15,14 @@ use App\Models\Behaviors\HasUnlisted; use App\Helpers\ImageHelpers; use App\Helpers\StringHelpers; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Experience extends AbstractModel implements Sortable { use HasBlocks; use HasSlug; use HasMedias; + use HasFactory; use HasFiles; use HasRevisions; use HasPosition; diff --git a/app/Models/GenericPage.php b/app/Models/GenericPage.php index 570cf9190d..35c9ab544f 100644 --- a/app/Models/GenericPage.php +++ b/app/Models/GenericPage.php @@ -197,6 +197,11 @@ public function categories() return $this->belongsToMany('App\Models\PageCategory'); } + public function sponsors() + { + return $this->belongsToMany(\App\Models\Sponsor::class)->withPivot('position')->orderBy('position'); + } + protected function transformMappingInternal() { return [ diff --git a/app/Models/InteractiveFeature.php b/app/Models/InteractiveFeature.php index 943fd1b8c7..397072619b 100644 --- a/app/Models/InteractiveFeature.php +++ b/app/Models/InteractiveFeature.php @@ -9,9 +9,11 @@ use App\Models\Behaviors\HasMediasEloquent; use App\Models\Behaviors\HasApiRelations; use App\Models\Behaviors\HasRelated; +use Illuminate\Database\Eloquent\Factories\HasFactory; class InteractiveFeature extends AbstractModel { + use HasFactory; use HasRevisions; use HasSlug; use HasMedias; diff --git a/app/Presenters/Admin/DigitalPublicationArticlePresenter.php b/app/Presenters/Admin/DigitalPublicationArticlePresenter.php index f80ddb8cec..b1da0d847a 100644 --- a/app/Presenters/Admin/DigitalPublicationArticlePresenter.php +++ b/app/Presenters/Admin/DigitalPublicationArticlePresenter.php @@ -4,17 +4,38 @@ use Illuminate\Support\Str; use App\Presenters\BasePresenter; +use App\Enums\DigitalPublicationArticleType; class DigitalPublicationArticlePresenter extends BasePresenter { public function getCanonicalUrl() { - return $this->getArticleUrl($this->entity->digitalPublication); + if ($this->entity->article_type === DigitalPublicationArticleType::Grouping) { + return route( + 'collection.publications.digital-publications.showListing', + [ + 'id' => $this->entity->digitalPublication->id, + 'slug' => $this->entity->digitalPublication->getSlug() + ] + ) . '#' . Str::kebab($this->entity->title); + } else { + return $this->getArticleUrl($this->entity->digitalPublication); + } + } + + public function articleType() + { + return $this->entity->article_type->name; } public function type() { - return $this->entity->type->name; + return 'Digital Publication Article'; + } + + public function subtype() + { + return 'From The Digital Publication'; } public function pdfDownloadPath() @@ -40,10 +61,11 @@ public function getArticleUrl($digitalPublication, $article = null) public function getBrowseMoreLink($showAll = false) { - if ($this->entity->children->count() > 0 && !$showAll) { + if ($this->entity->children->count() > 0 && !$showAll) { + $totalChildrenCount = $this->countAllChildren($this->entity); return [ [ - 'label' => 'Browse all ' . $this->entity->children->count() . ' ' . $this->entity->title, + 'label' => 'Browse all ' . $totalChildrenCount . ' ' . $this->entity->title, 'href' => route( 'collection.publications.digital-publications.showListing', [ @@ -57,6 +79,17 @@ public function getBrowseMoreLink($showAll = false) return ''; } + public function countAllChildren($entity) + { + $descendants = $entity->descendants; + + $filteredDescendants = $descendants->filter(function ($descendant) { + return $descendant->article_type !== DigitalPublicationArticleType::Grouping; + }); + + return $filteredDescendants->count(); + } + public function references() { if (empty($this->entity->references)) { @@ -74,4 +107,17 @@ public function citeAs() return $this->addCssClass($this->entity->cite_as, 'f-secondary'); } + + public function isArticleInTree($items) + { + foreach ($items as $childItem) { + if ($childItem->id === $this->entity->id) { + return true; + } + if (count($childItem->children) > 0 && $this->isArticleInTree($childItem->children)) { + return true; + } + } + return false; + } } diff --git a/app/Presenters/Admin/DigitalPublicationPresenter.php b/app/Presenters/Admin/DigitalPublicationPresenter.php index 67c6146b6c..f526fd639c 100644 --- a/app/Presenters/Admin/DigitalPublicationPresenter.php +++ b/app/Presenters/Admin/DigitalPublicationPresenter.php @@ -51,7 +51,7 @@ public function getArticles($type = null) if (!isset($this->articles[$type])) { $this->articles[$type] = $this->articles['all'] ->filter(function ($article) use ($type) { - return $article->type->value === $type; + return $article->article_type->value === $type; }) ->values(); } @@ -71,7 +71,7 @@ public function topLevelArticles() public function headerTitle() { - return $this->entity->header_title_display ?? $this->entity->title_display; + return $this->entity->title_display ?? $this->entity->title; } public function headerSubtitle() diff --git a/app/Presenters/Admin/ExhibitionPresenter.php b/app/Presenters/Admin/ExhibitionPresenter.php index 5f7e418776..c41aed8d25 100644 --- a/app/Presenters/Admin/ExhibitionPresenter.php +++ b/app/Presenters/Admin/ExhibitionPresenter.php @@ -92,7 +92,7 @@ public function position() public function exhibitionType() { - return $this->entity->isOngoing ? 'Ongoing' : 'Exhibition'; + return $this->entity->type_override ? $this->entity->type_override : ($this->entity->isOngoing ? 'Ongoing' : 'Exhibition'); } /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index b91fd82dae..7f14514f75 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -155,272 +155,263 @@ public function register(): void private function composeTemplatesViews() { - if (!\Schema::hasTable('hours')) { - /* - If the project is being initialized or not all of the migrations - have been run yet, the `hours` table may not exist, so skip this - function. - */ - return; - } - $hour = app()->environment() === 'testing' ? null : Hour::today()->first(); - // WEB-2269: Consider moving some of this to a config? - view()->composer('*', function ($view) use ($hour) { - $view->with([ - 'hour' => $hour, - '_pages' => [ - 'visit' => route('pages.slug', ['slug' => 'visit']), - 'hours' => route('pages.slug', ['slug' => 'visit']) . '#hours', - 'directions' => route('pages.slug', ['slug' => 'visit']) . '#directions', - 'buy' => 'https://sales.artic.edu/admissions', - 'become-a-member' => 'https://sales.artic.edu/memberships', - 'shop' => 'https://shop.artic.edu/', - 'collection' => route('collection'), - 'exhibitions' => route('exhibitions'), - 'events' => route('events'), 'about-us' => '/about-us', - 'about-us-mission-and-history' => '/about-us/mission-and-history', - 'about-us-leadership' => '/about-us/leadership', - 'about-us-departments' => '/about-us/departments', - 'about-us-financial-reporting' => '/about-us/financial-reporting', - 'support-us' => '/support-us', - 'support-us-membership' => '/support-us/membership', - 'support-us-luminary' => '/support-us/membership/luminary-levels', - 'support-us-planned-giving' => '/support-us/ways-to-give/planned-giving', - 'support-us-corporate-sponsorship' => '/support-us/ways-to-give/corporate-sponsorship', - 'learn' => '/learn-with-us', - 'learn-families' => '/learn-with-us/families', - 'learn-teens' => '/learn-with-us/teens', - 'learn-adults' => '/visit/whos-visiting/college-and-university-groups-2', - 'learn-educators' => '/learn-with-us/educators', - 'learn-rlc' => '/ryan-learning-center', - 'follow-facebook' => 'https://www.facebook.com/artic', - 'follow-twitter' => 'https://twitter.com/artinstitutechi', - 'follow-instagram' => 'https://www.instagram.com/artinstitutechi/', - 'follow-youtube' => 'https://www.youtube.com/user/ArtInstituteChicago', - 'legal-articles' => route('articles'), - 'legal-employment' => '/employment', - 'legal-venue-rental' => '/venue-rental', - 'legal-contact' => '/contact', 'legal-press' => '/press', - 'legal-terms' => '/terms', - 'legal-image-licensing' => '/image-licensing', - 'legal-saic' => 'https://www.saic.edu', - ], - 'primaryNav' => [ - [ - 'name' => 'Visit', - 'description' => 'Find all the information you need—plus helpful tips—to plan your visit', - 'cta' => 'Start planning', - 'image' => 'https://artic-web.imgix.net/b55a24a5-ab1c-453e-9ecd-e30ee5473f6e/navigation-thumbnail-visit.jpg', - 'url' => route('pages.slug', ['slug' => 'visit']), - 'children' => [ - [ - 'name' => 'Hours', - 'url' => route('pages.slug', ['slug' => 'visit']) . '#hours', - ], - [ - 'name' => 'Admission', - 'url' => route('pages.slug', ['slug' => 'visit']) . '#admission', - ], - [ - 'name' => 'Plan Your Visit', - 'url' => route('pages.slug', ['slug' => 'visit']) . '#plan-your-visit', - 'children' => [ - [ - 'name' => 'Museum Map', - 'url' => '/visit/explore-on-your-own/museum-floor-plan', - ], - [ - 'name' => 'Free Daily Tours', - 'url' => route('events', ['audience' => 3, 'type' => 6]), - ], - [ - 'name' => 'My Museum Tour', - 'url' => route('pages.slug', ['slug' => 'my-museum-tour']), - ], - [ - 'name' => 'What to See in an Hour', - 'url' => route('highlights.show', ['id' => 3, 'slug' => 'what-to-see-in-an-hour']), - ], - [ - 'name' => 'Shopping and Dining', - 'url' => '/visit/dining-and-shopping', - ], - [ - 'name' => 'Accessibility', - 'url' => '/visit/accessibility', - ], + view()->composer('*', function ($view) { + $view->with(\Cache::remember('navArray', 3600, function () { + return [ + '_pages' => [ + 'visit' => route('pages.slug', ['slug' => 'visit']), + 'hours' => route('pages.slug', ['slug' => 'visit']) . '#hours', + 'directions' => route('pages.slug', ['slug' => 'visit']) . '#directions', + 'buy' => 'https://sales.artic.edu/admissions', + 'become-a-member' => 'https://sales.artic.edu/memberships', + 'shop' => 'https://shop.artic.edu/', + 'collection' => route('collection'), + 'exhibitions' => route('exhibitions'), + 'events' => route('events'), 'about-us' => '/about-us', + 'about-us-mission-and-history' => '/about-us/mission-and-history', + 'about-us-leadership' => '/about-us/leadership', + 'about-us-departments' => '/about-us/departments', + 'about-us-financial-reporting' => '/about-us/financial-reporting', + 'support-us' => '/support-us', + 'support-us-membership' => '/support-us/membership', + 'support-us-luminary' => '/support-us/membership/luminary-levels', + 'support-us-planned-giving' => '/support-us/ways-to-give/planned-giving', + 'support-us-corporate-sponsorship' => '/support-us/ways-to-give/corporate-sponsorship', + 'learn' => '/learn-with-us', + 'learn-families' => '/learn-with-us/families', + 'learn-teens' => '/learn-with-us/teens', + 'learn-adults' => '/visit/whos-visiting/college-and-university-groups-2', + 'learn-educators' => '/learn-with-us/educators', + 'learn-rlc' => '/ryan-learning-center', + 'follow-facebook' => 'https://www.facebook.com/artic', + 'follow-twitter' => 'https://twitter.com/artinstitutechi', + 'follow-instagram' => 'https://www.instagram.com/artinstitutechi/', + 'follow-youtube' => 'https://www.youtube.com/user/ArtInstituteChicago', + 'legal-articles' => route('articles'), + 'legal-employment' => '/employment', + 'legal-venue-rental' => '/venue-rental', + 'legal-contact' => '/contact', 'legal-press' => '/press', + 'legal-terms' => '/terms', + 'legal-image-licensing' => '/image-licensing', + 'legal-saic' => 'https://www.saic.edu', + ], + 'primaryNav' => [ + [ + 'name' => 'Visit', + 'description' => 'Find all the information you need—plus helpful tips—to plan your visit', + 'cta' => 'Start planning', + 'image' => 'https://artic-web.imgix.net/b55a24a5-ab1c-453e-9ecd-e30ee5473f6e/navigation-thumbnail-visit.jpg', + 'url' => route('pages.slug', ['slug' => 'visit']), + 'children' => [ + [ + 'name' => 'Hours', + 'url' => route('pages.slug', ['slug' => 'visit']) . '#hours', ], - ], - [ - 'name' => 'Who's Visiting?', - 'url' => route('pages.slug', ['slug' => 'visit']) . '#whos-visiting', - 'children' => [ - [ - 'name' => 'First-Time Visitors', - 'url' => '/visit/whos-visiting/first-time-visitors', - ], - [ - 'name' => 'Families', - 'url' => '/visit/whos-visiting/families-2', - ], - [ - 'name' => 'Members', - 'url' => '/visit/whos-visiting/members', - ], - [ - 'name' => 'Teens', - 'url' => '/visit/whos-visiting/teens-2', - ], - [ - 'name' => 'Educators', - 'url' => '/visit/whos-visiting/educators-2', + [ + 'name' => 'Admission', + 'url' => route('pages.slug', ['slug' => 'visit']) . '#admission', + ], + [ + 'name' => 'Plan Your Visit', + 'url' => route('pages.slug', ['slug' => 'visit']) . '#plan-your-visit', + 'children' => [ + [ + 'name' => 'Museum Map', + 'url' => '/visit/explore-on-your-own/museum-floor-plan', + ], + [ + 'name' => 'Free Daily Tours', + 'url' => route('events', ['audience' => 3, 'type' => 6]), + ], + [ + 'name' => 'My Museum Tour', + 'url' => route('pages.slug', ['slug' => 'my-museum-tour']), + ], + [ + 'name' => 'What to See in an Hour', + 'url' => route('highlights.show', ['id' => 3, 'slug' => 'what-to-see-in-an-hour']), + ], + [ + 'name' => 'Shopping and Dining', + 'url' => '/visit/dining-and-shopping', + ], + [ + 'name' => 'Accessibility', + 'url' => '/visit/accessibility', + ], ], - [ - 'name' => 'Group Visits', - 'url' => '/visit/whos-visiting/adult-groups-2', + ], + [ + 'name' => 'Who's Visiting?', + 'url' => route('pages.slug', ['slug' => 'visit']) . '#whos-visiting', + 'children' => [ + [ + 'name' => 'First-Time Visitors', + 'url' => '/visit/whos-visiting/first-time-visitors', + ], + [ + 'name' => 'Families', + 'url' => '/visit/whos-visiting/families-2', + ], + [ + 'name' => 'Members', + 'url' => '/visit/whos-visiting/members', + ], + [ + 'name' => 'Teens', + 'url' => '/visit/whos-visiting/teens-2', + ], + [ + 'name' => 'Educators', + 'url' => '/visit/whos-visiting/educators-2', + ], + [ + 'name' => 'Group Visits', + 'url' => '/visit/whos-visiting/adult-groups-2', + ], ], ], - ], - [ - 'name' => 'Mobile App', - 'url' => '/visit/explore-on-your-own/mobile-app-audio-tours', - ], - [ - 'name' => 'Ryan Learning Center', - 'url' => '/ryan-learning-center', + [ + 'name' => 'Mobile App', + 'url' => '/visit/explore-on-your-own/mobile-app-audio-tours', + ], + [ + 'name' => 'Ryan Learning Center', + 'url' => '/ryan-learning-center', + ], ], ], - ], - [ - 'name' => 'Exhibitions', - 'url' => route('exhibitions'), - 'class' => 'exhibitions', - 'children' => [ - [ - 'name' => 'Current', - 'url' => route('exhibitions'), - ], - [ - 'name' => 'Upcoming', - 'url' => route('exhibitions.upcoming'), - ], - [ - 'name' => 'Archive', - 'url' => route('exhibitions.history'), + [ + 'name' => 'Exhibitions', + 'url' => route('exhibitions'), + 'class' => 'exhibitions', + 'children' => [ + [ + 'name' => 'Current', + 'url' => route('exhibitions'), + ], + [ + 'name' => 'Upcoming', + 'url' => route('exhibitions.upcoming'), + ], + [ + 'name' => 'Archive', + 'url' => route('exhibitions.history'), + ], ], ], - ], - [ - 'name' => 'Art & Artists', - 'description' => 'Explore the works in our collection and delve deeper into their stories.', - 'cta' => 'Start your discovery', - 'image' => 'https://artic-web.imgix.net/fd36787d-a4f7-480c-8e34-11115a9d240a/navigation-thumbnail-art-and-artists.jpg', - 'url' => route('collection'), - 'children' => [ - [ - 'name' => 'Artworks', - 'url' => route('collection'), - ], - [ - 'name' => 'Articles & Videos', - 'url' => route('pages.slug', ['slug' => 'articles-and-videos']), - ], - [ - 'name' => 'Research', - 'url' => route('collection.research_resources'), - 'children' => [ - [ - 'name' => 'Library', - 'url' => '/library', - ], - [ - 'name' => 'Archival Collections', - 'url' => '/archival-collections', - ], - [ - 'name' => 'Collection Information', - 'url' => '/collection-information', - ], - [ - 'name' => 'Conservation and Science', - 'url' => '/about-us/departments/conservation-and-science-2', - ], + [ + 'name' => 'Art & Artists', + 'description' => 'Explore the works in our collection and delve deeper into their stories.', + 'cta' => 'Start your discovery', + 'image' => 'https://artic-web.imgix.net/fd36787d-a4f7-480c-8e34-11115a9d240a/navigation-thumbnail-art-and-artists.jpg', + 'url' => route('collection'), + 'children' => [ + [ + 'name' => 'Artworks', + 'url' => route('collection'), ], - ], - [ - 'name' => 'Publications', - 'url' => route('articles_publications'), - 'children' => [ - [ - 'name' => 'Print Catalogues', - 'url' => route('collection.publications.printed-publications'), + [ + 'name' => 'Articles & Videos', + 'url' => route('pages.slug', ['slug' => 'articles-and-videos']), + ], + [ + 'name' => 'Research', + 'url' => route('collection.research_resources'), + 'children' => [ + [ + 'name' => 'Library', + 'url' => '/library', + ], + [ + 'name' => 'Archival Collections', + 'url' => '/archival-collections', + ], + [ + 'name' => 'Collection Information', + 'url' => '/collection-information', + ], + [ + 'name' => 'Conservation and Science', + 'url' => '/about-us/departments/conservation-and-science-2', + ], ], - [ - 'name' => 'Digital Publications', - 'url' => route('collection.publications.digital-publications'), + ], + [ + 'name' => 'Publications', + 'url' => route('articles_publications'), + 'children' => [ + [ + 'name' => 'Print Catalogues', + 'url' => route('collection.publications.printed-publications'), + ], + [ + 'name' => 'Digital Publications', + 'url' => route('collection.publications.digital-publications'), + ], ], ], ], ], - ], - [ - 'name' => 'Events', - 'description' => 'Join us for a wide range of programs—there's something for visitors of all ages.', - 'cta' => 'Check out the calendar', - 'image' => 'https://artic-web.imgix.net/d335c986-7075-4753-a84f-9cb11876ac77/navigation-thumbnail-events.jpg', - 'url' => route('events'), - 'children' => [ - [ - 'name' => 'Calendar', - 'url' => route('events'), - ], - [ - 'name' => 'Daily Tours', - 'url' => route('events', ['type' => 6]), - ], - [ - 'name' => 'Talks', - 'url' => route('events', ['type' => 5]), - ], - [ - 'name' => 'Art Making', - 'url' => route('events', ['type' => 1]), - ], - [ - 'name' => 'Member Programs', - 'url' => route('events', ['audience' => 2]), + [ + 'name' => 'Events', + 'description' => 'Join us for a wide range of programs—there's something for visitors of all ages.', + 'cta' => 'Check out the calendar', + 'image' => 'https://artic-web.imgix.net/d335c986-7075-4753-a84f-9cb11876ac77/navigation-thumbnail-events.jpg', + 'url' => route('events'), + 'children' => [ + [ + 'name' => 'Calendar', + 'url' => route('events'), + ], + [ + 'name' => 'Daily Tours', + 'url' => route('events', ['type' => 6]), + ], + [ + 'name' => 'Talks', + 'url' => route('events', ['type' => 5]), + ], + [ + 'name' => 'Art Making', + 'url' => route('events', ['type' => 1]), + ], + [ + 'name' => 'Member Programs', + 'url' => route('events', ['audience' => 2]), + ], ], ], + [ + 'name' => 'Become a Member', + 'class' => 'u-hide@small+', + 'url' => 'https://sales.artic.edu/memberships', + ], ], - [ - 'name' => 'Become a Member', - 'class' => 'u-hide@small+', - 'url' => 'https://sales.artic.edu/memberships', - ], - ], - 'secondaryNav' => [ - [ - 'name' => 'Buy Tickets', - 'url' => 'https://sales.artic.edu/admissions', - ], - [ - 'name' => 'Become a Member', - 'class' => 'u-show@small+', - 'url' => 'https://sales.artic.edu/memberships', - ], - [ - 'name' => 'Shop', - 'class' => 'u-show@small+', - 'url' => 'https://shop.artic.edu/', - ], - [ - 'name' => 'Visit', - 'class' => 'u-hide@small+', - 'url' => route('pages.slug', ['slug' => 'visit']), + 'secondaryNav' => [ + [ + 'name' => 'Buy Tickets', + 'url' => 'https://sales.artic.edu/admissions', + ], + [ + 'name' => 'Become a Member', + 'class' => 'u-show@small+', + 'url' => 'https://sales.artic.edu/memberships', + ], + [ + 'name' => 'Shop', + 'class' => 'u-show@small+', + 'url' => 'https://shop.artic.edu/', + ], + [ + 'name' => 'Visit', + 'class' => 'u-hide@small+', + 'url' => route('pages.slug', ['slug' => 'visit']), + ], ], - ], - ]); + ]; + })); }); } } diff --git a/app/Repositories/Behaviors/HandleFeaturedRelated.php b/app/Repositories/Behaviors/HandleFeaturedRelated.php index 79a7e22462..a788c64e49 100644 --- a/app/Repositories/Behaviors/HandleFeaturedRelated.php +++ b/app/Repositories/Behaviors/HandleFeaturedRelated.php @@ -15,6 +15,7 @@ public function afterSaveHandleFeaturedRelated($object, $fields) 'events' => false, 'experiences' => false, 'digitalPublications' => false, + 'digitalPublicationArticles' => false, 'videos' => false, 'exhibitions' => true, ]); @@ -34,6 +35,7 @@ public function getFormFieldsHandleFeaturedRelated($object, $fields) 'events' => false, 'experiences' => false, 'digitalPublications' => false, + 'digitalPublicationArticles' => false, 'videos' => false, 'exhibitions' => true, ]); diff --git a/app/Repositories/GenericPageRepository.php b/app/Repositories/GenericPageRepository.php index 4bb0053fb3..07c8526060 100644 --- a/app/Repositories/GenericPageRepository.php +++ b/app/Repositories/GenericPageRepository.php @@ -21,11 +21,24 @@ class GenericPageRepository extends ModuleRepository HandleApiBlocks::getBlockBrowsers insteadof HandleBlocks; } + protected $browsers = [ + 'sponsors' => [ + 'routePrefix' => 'exhibitions_events', + ], + ]; + public function __construct(GenericPage $model) { $this->model = $model; } + public function hydrate($object, $fields) + { + $this->hydrateBrowser($object, $fields, 'sponsors', 'position', 'Sponsor'); + + return parent::hydrate($object, $fields); + } + public function setNewOrder($ids) { if (is_array(Arr::first($ids))) { diff --git a/composer.json b/composer.json index 658e7127d4..f0fa46dffe 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,12 @@ { "name": "aic/website", "description": "Art Institute of Chicago website – artic.edu", - "keywords": ["website", "museum", "musetech", "art institute of chicago"], + "keywords": [ + "website", + "museum", + "musetech", + "art institute of chicago" + ], "license": "AGPL-3.0-or-later", "type": "project", "repositories": [ @@ -62,7 +67,7 @@ "rlanvin/php-rrule": "^2.0", "salesforce-mc/fuel-sdk-php": "dev-master", "sendgrid/sendgrid": "^8.1", - "sentry/sentry-laravel": "^3.0", + "sentry/sentry-laravel": "^4.9", "spatie/calendar-links": "^1.0", "spatie/laravel-feed": "^4.0", "spatie/laravel-sitemap": "^6.0", diff --git a/composer.lock b/composer.lock index 7cd915695a..632667e5b0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "88847d619228317bb5f01d90db331862", + "content-hash": "c13a1e04af07a01db29d089d834f4067", "packages": [ { "name": "aic/data-hub-foundation", @@ -694,72 +694,6 @@ ], "time": "2024-01-05T23:55:20+00:00" }, - { - "name": "clue/stream-filter", - "version": "v1.6.0", - "source": { - "type": "git", - "url": "https://github.com/clue/stream-filter.git", - "reference": "d6169430c7731d8509da7aecd0af756a5747b78e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/stream-filter/zipball/d6169430c7731d8509da7aecd0af756a5747b78e", - "reference": "d6169430c7731d8509da7aecd0af756a5747b78e", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "Clue\\StreamFilter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "A simple and modern approach to stream filtering in PHP", - "homepage": "https://github.com/clue/php-stream-filter", - "keywords": [ - "bucket brigade", - "callback", - "filter", - "php_user_filter", - "stream", - "stream_filter_append", - "stream_filter_register" - ], - "support": { - "issues": "https://github.com/clue/stream-filter/issues", - "source": "https://github.com/clue/stream-filter/tree/v1.6.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-02-21T13:15:14+00:00" - }, { "name": "composer/pcre", "version": "3.1.0", @@ -2753,64 +2687,6 @@ ], "time": "2021-10-07T12:57:01+00:00" }, - { - "name": "http-interop/http-factory-guzzle", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/http-interop/http-factory-guzzle.git", - "reference": "8f06e92b95405216b237521cc64c804dd44c4a81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/http-interop/http-factory-guzzle/zipball/8f06e92b95405216b237521cc64c804dd44c4a81", - "reference": "8f06e92b95405216b237521cc64c804dd44c4a81", - "shasum": "" - }, - "require": { - "guzzlehttp/psr7": "^1.7||^2.0", - "php": ">=7.3", - "psr/http-factory": "^1.0" - }, - "provide": { - "psr/http-factory-implementation": "^1.0" - }, - "require-dev": { - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "guzzlehttp/psr7": "Includes an HTTP factory starting in version 2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Http\\Factory\\Guzzle\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "An HTTP Factory using Guzzle PSR7", - "keywords": [ - "factory", - "http", - "psr-17", - "psr-7" - ], - "support": { - "issues": "https://github.com/http-interop/http-factory-guzzle/issues", - "source": "https://github.com/http-interop/http-factory-guzzle/tree/1.2.0" - }, - "time": "2021-07-21T13:50:14+00:00" - }, { "name": "imgix/imgix-php", "version": "3.3.1", @@ -5782,123 +5658,117 @@ "time": "2020-10-15T08:29:30+00:00" }, { - "name": "php-http/client-common", - "version": "2.7.0", + "name": "phpoption/phpoption", + "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/php-http/client-common.git", - "reference": "880509727a447474d2a71b7d7fa5d268ddd3db4b" + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/client-common/zipball/880509727a447474d2a71b7d7fa5d268ddd3db4b", - "reference": "880509727a447474d2a71b7d7fa5d268ddd3db4b", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", - "php-http/httplug": "^2.0", - "php-http/message": "^1.6", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0 || ^2.0", - "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0", - "symfony/polyfill-php80": "^1.17" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "doctrine/instantiator": "^1.1", - "guzzlehttp/psr7": "^1.4", - "nyholm/psr7": "^1.2", - "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", - "phpspec/prophecy": "^1.10.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.33 || ^9.6.7" - }, - "suggest": { - "ext-json": "To detect JSON responses with the ContentTypePlugin", - "ext-libxml": "To detect XML responses with the ContentTypePlugin", - "php-http/cache-plugin": "PSR-6 Cache plugin", - "php-http/logger-plugin": "PSR-3 Logger plugin", - "php-http/stopwatch-plugin": "Symfony Stopwatch plugin" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, "autoload": { "psr-4": { - "Http\\Client\\Common\\": "src/" + "PhpOption\\": "src/PhpOption/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" } ], - "description": "Common HTTP Client implementations and tools for HTTPlug", - "homepage": "http://httplug.io", + "description": "Option Type for PHP", "keywords": [ - "client", - "common", - "http", - "httplug" + "language", + "option", + "php", + "type" ], "support": { - "issues": "https://github.com/php-http/client-common/issues", - "source": "https://github.com/php-http/client-common/tree/2.7.0" + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" }, - "time": "2023-05-17T06:46:59+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2023-02-25T19:38:58+00:00" }, { - "name": "php-http/discovery", - "version": "1.19.1", + "name": "phpseclib/phpseclib", + "version": "3.0.21", "source": { "type": "git", - "url": "https://github.com/php-http/discovery.git", - "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e" + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/57f3de01d32085fea20865f9b16fb0e69347c39e", - "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0|^2.0", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "nyholm/psr7": "<1.0", - "zendframework/zend-diactoros": "*" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "*", - "psr/http-factory-implementation": "*", - "psr/http-message-implementation": "*" + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" }, "require-dev": { - "composer/composer": "^1.0.2|^2.0", - "graham-campbell/phpspec-skip-example-extension": "^5.0", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0", - "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", - "symfony/phpunit-bridge": "^6.2" + "phpunit/phpunit": "*" }, - "type": "composer-plugin", - "extra": { - "class": "Http\\Discovery\\Composer\\Plugin", - "plugin-optional": true + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." }, + "type": "library", "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], "psr-4": { - "Http\\Discovery\\": "src/" - }, - "exclude-from-classmap": [ - "src/Composer/Plugin.php" - ] + "phpseclib3\\": "phpseclib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5906,472 +5776,93 @@ ], "authors": [ { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" } ], - "description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations", - "homepage": "http://php-http.org", + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", "keywords": [ - "adapter", - "client", - "discovery", - "factory", - "http", - "message", - "psr17", - "psr7" + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" ], "support": { - "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.19.1" + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21" }, - "time": "2023-07-11T07:02:26+00:00" + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2023-07-09T15:24:48+00:00" }, { - "name": "php-http/httplug", - "version": "2.4.0", + "name": "pragmarx/google2fa", + "version": "v8.0.1", "source": { "type": "git", - "url": "https://github.com/php-http/httplug.git", - "reference": "625ad742c360c8ac580fcc647a1541d29e257f67" + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67", - "reference": "625ad742c360c8ac580fcc647a1541d29e257f67", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", - "php-http/promise": "^1.1", - "psr/http-client": "^1.0", - "psr/http-message": "^1.0 || ^2.0" + "paragonie/constant_time_encoding": "^1.0|^2.0", + "php": "^7.1|^8.0" }, "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", - "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eric GELOEN", - "email": "geloen.eric@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "HTTPlug, the HTTP client abstraction for PHP", - "homepage": "http://httplug.io", - "keywords": [ - "client", - "http" - ], - "support": { - "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/2.4.0" - }, - "time": "2023-04-14T15:10:03+00:00" - }, - { - "name": "php-http/message", - "version": "1.16.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/message.git", - "reference": "47a14338bf4ebd67d317bf1144253d7db4ab55fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/47a14338bf4ebd67d317bf1144253d7db4ab55fd", - "reference": "47a14338bf4ebd67d317bf1144253d7db4ab55fd", - "shasum": "" - }, - "require": { - "clue/stream-filter": "^1.5", - "php": "^7.2 || ^8.0", - "psr/http-message": "^1.1 || ^2.0" - }, - "provide": { - "php-http/message-factory-implementation": "1.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.6", - "ext-zlib": "*", - "guzzlehttp/psr7": "^1.0 || ^2.0", - "laminas/laminas-diactoros": "^2.0 || ^3.0", - "php-http/message-factory": "^1.0.2", - "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", - "slim/slim": "^3.0" - }, - "suggest": { - "ext-zlib": "Used with compressor/decompressor streams", - "guzzlehttp/psr7": "Used with Guzzle PSR-7 Factories", - "laminas/laminas-diactoros": "Used with Diactoros Factories", - "slim/slim": "Used with Slim Framework PSR-7 implementation" - }, - "type": "library", - "autoload": { - "files": [ - "src/filters.php" - ], - "psr-4": { - "Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "HTTP Message related tools", - "homepage": "http://php-http.org", - "keywords": [ - "http", - "message", - "psr-7" - ], - "support": { - "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/1.16.0" - }, - "time": "2023-05-17T06:43:38+00:00" - }, - { - "name": "php-http/message-factory", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/message-factory.git", - "reference": "4d8778e1c7d405cbb471574821c1ff5b68cc8f57" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/message-factory/zipball/4d8778e1c7d405cbb471574821c1ff5b68cc8f57", - "reference": "4d8778e1c7d405cbb471574821c1ff5b68cc8f57", - "shasum": "" - }, - "require": { - "php": ">=5.4", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Factory interfaces for PSR-7 HTTP Message", - "homepage": "http://php-http.org", - "keywords": [ - "factory", - "http", - "message", - "stream", - "uri" - ], - "support": { - "issues": "https://github.com/php-http/message-factory/issues", - "source": "https://github.com/php-http/message-factory/tree/1.1.0" - }, - "abandoned": "psr/http-factory", - "time": "2023-04-14T14:16:17+00:00" - }, - { - "name": "php-http/promise", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-http/promise.git", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "reference": "4c4c1f9b7289a2ec57cde7f1e9762a5789506f88", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.3.2", - "phpspec/phpspec": "^5.1.2 || ^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Http\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Joel Wurtz", - "email": "joel.wurtz@gmail.com" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com" - } - ], - "description": "Promise used for asynchronous HTTP requests", - "homepage": "http://httplug.io", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.1.0" - }, - "time": "2020-07-07T09:29:14+00:00" - }, - { - "name": "phpoption/phpoption", - "version": "1.9.1", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", - "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": true - }, - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpOption\\": "src/PhpOption/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh" - }, - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } - ], - "time": "2023-02-25T19:38:58+00:00" - }, - { - "name": "phpseclib/phpseclib", - "version": "3.0.21", - "source": { - "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1", - "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1", - "shasum": "" - }, - "require": { - "paragonie/constant_time_encoding": "^1|^2", - "paragonie/random_compat": "^1.4|^2.0|^9.99.99", - "php": ">=5.6.1" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "suggest": { - "ext-dom": "Install the DOM extension to load XML formatted public keys.", - "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", - "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", - "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", - "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." - }, - "type": "library", - "autoload": { - "files": [ - "phpseclib/bootstrap.php" - ], - "psr-4": { - "phpseclib3\\": "phpseclib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" - }, - { - "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" - }, - { - "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" - }, - { - "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" - } - ], - "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", - "homepage": "http://phpseclib.sourceforge.net", - "keywords": [ - "BigInteger", - "aes", - "asn.1", - "asn1", - "blowfish", - "crypto", - "cryptography", - "encryption", - "rsa", - "security", - "sftp", - "signature", - "signing", - "ssh", - "twofish", - "x.509", - "x509" - ], - "support": { - "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21" - }, - "funding": [ - { - "url": "https://github.com/terrafrost", - "type": "github" - }, - { - "url": "https://www.patreon.com/phpseclib", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", - "type": "tidelift" - } - ], - "time": "2023-07-09T15:24:48+00:00" - }, - { - "name": "pragmarx/google2fa", - "version": "v8.0.1", - "source": { - "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", - "shasum": "" - }, - "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^0.12.18", - "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + "phpstan/phpstan": "^0.12.18", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" }, "type": "library", "autoload": { @@ -7623,112 +7114,42 @@ }, "time": "2023-12-06T07:11:08+00:00" }, - { - "name": "sentry/sdk", - "version": "3.5.0", - "source": { - "type": "git", - "url": "https://github.com/getsentry/sentry-php-sdk.git", - "reference": "cd91b752f07c4bab9fb3b173f81af68a78a78d6d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/cd91b752f07c4bab9fb3b173f81af68a78a78d6d", - "reference": "cd91b752f07c4bab9fb3b173f81af68a78a78d6d", - "shasum": "" - }, - "require": { - "http-interop/http-factory-guzzle": "^1.0", - "sentry/sentry": "^3.19", - "symfony/http-client": "^4.3|^5.0|^6.0" - }, - "type": "metapackage", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Sentry", - "email": "accounts@sentry.io" - } - ], - "description": "This is a metapackage shipping sentry/sentry with a recommended HTTP client.", - "homepage": "http://sentry.io", - "keywords": [ - "crash-reporting", - "crash-reports", - "error-handler", - "error-monitoring", - "log", - "logging", - "sentry" - ], - "support": { - "issues": "https://github.com/getsentry/sentry-php-sdk/issues", - "source": "https://github.com/getsentry/sentry-php-sdk/tree/3.5.0" - }, - "funding": [ - { - "url": "https://sentry.io/", - "type": "custom" - }, - { - "url": "https://sentry.io/pricing/", - "type": "custom" - } - ], - "time": "2023-06-12T17:50:36+00:00" - }, { "name": "sentry/sentry", - "version": "3.21.0", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "624aafc22b84b089ffa43b71fb01e0096505ec4f" + "reference": "788ec170f51ebb22f2809a1e3f78b19ccd39b70d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/624aafc22b84b089ffa43b71fb01e0096505ec4f", - "reference": "624aafc22b84b089ffa43b71fb01e0096505ec4f", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/788ec170f51ebb22f2809a1e3f78b19ccd39b70d", + "reference": "788ec170f51ebb22f2809a1e3f78b19ccd39b70d", "shasum": "" }, "require": { + "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", - "guzzlehttp/promises": "^1.5.3|^2.0", + "guzzlehttp/psr7": "^1.8.4|^2.1.1", "jean85/pretty-package-versions": "^1.5|^2.0.4", "php": "^7.2|^8.0", - "php-http/async-client-implementation": "^1.0", - "php-http/client-common": "^1.5|^2.0", - "php-http/discovery": "^1.15", - "php-http/httplug": "^1.1|^2.0", - "php-http/message": "^1.5", - "php-http/message-factory": "^1.1", - "psr/http-factory": "^1.0", - "psr/http-factory-implementation": "^1.0", "psr/log": "^1.0|^2.0|^3.0", - "symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0", - "symfony/polyfill-php80": "^1.17" + "symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0" }, "conflict": { - "php-http/client-common": "1.8.0", "raven/raven": "*" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.19|3.4.*", + "friendsofphp/php-cs-fixer": "^3.4", + "guzzlehttp/promises": "^1.0|^2.0", "guzzlehttp/psr7": "^1.8.4|^2.1.1", - "http-interop/http-factory-guzzle": "^1.0", "monolog/monolog": "^1.6|^2.0|^3.0", - "nikic/php-parser": "^4.10.3", - "php-http/mock-client": "^1.3", "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.3", - "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^8.5.14|^9.4", - "symfony/phpunit-bridge": "^5.2|^6.0", + "symfony/phpunit-bridge": "^5.2|^6.0|^7.0", "vimeo/psalm": "^4.17" }, "suggest": { @@ -7753,7 +7174,7 @@ "email": "accounts@sentry.io" } ], - "description": "A PHP SDK for Sentry (http://sentry.io)", + "description": "PHP SDK for Sentry (http://sentry.io)", "homepage": "http://sentry.io", "keywords": [ "crash-reporting", @@ -7762,11 +7183,13 @@ "error-monitoring", "log", "logging", - "sentry" + "profiling", + "sentry", + "tracing" ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/3.21.0" + "source": "https://github.com/getsentry/sentry-php/tree/4.9.0" }, "funding": [ { @@ -7778,46 +7201,42 @@ "type": "custom" } ], - "time": "2023-07-31T15:31:24+00:00" + "time": "2024-08-08T14:40:50+00:00" }, { "name": "sentry/sentry-laravel", - "version": "3.7.3", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "2aee4ad217be8ef04ffcde6e9f7dd17af5a3b0bf" + "reference": "73078e1f26d57f7a10e3bee2a2f543a02f6493c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/2aee4ad217be8ef04ffcde6e9f7dd17af5a3b0bf", - "reference": "2aee4ad217be8ef04ffcde6e9f7dd17af5a3b0bf", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/73078e1f26d57f7a10e3bee2a2f543a02f6493c3", + "reference": "73078e1f26d57f7a10e3bee2a2f543a02f6493c3", "shasum": "" }, "require": { - "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", + "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0", "nyholm/psr7": "^1.0", "php": "^7.2 | ^8.0", - "sentry/sdk": "^3.4", - "sentry/sentry": "^3.20.1", - "symfony/psr-http-message-bridge": "^1.0 | ^2.0" + "sentry/sentry": "^4.9", + "symfony/psr-http-message-bridge": "^1.0 | ^2.0 | ^6.0 | ^7.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.11", - "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", + "guzzlehttp/guzzle": "^7.2", + "laravel/folio": "^1.1", + "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0", + "livewire/livewire": "^2.0 | ^3.0", "mockery/mockery": "^1.3", - "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0", + "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0 | ^9.0", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.4 | ^9.3" + "phpunit/phpunit": "^8.4 | ^9.3 | ^10.4" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev", - "dev-0.x": "0.x-dev" - }, "laravel": { "providers": [ "Sentry\\Laravel\\ServiceProvider", @@ -7853,11 +7272,13 @@ "laravel", "log", "logging", - "sentry" + "profiling", + "sentry", + "tracing" ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/3.7.3" + "source": "https://github.com/getsentry/sentry-laravel/tree/4.9.0" }, "funding": [ { @@ -7869,7 +7290,7 @@ "type": "custom" } ], - "time": "2023-08-03T10:10:23+00:00" + "time": "2024-09-19T12:58:53+00:00" }, { "name": "spatie/browsershot", @@ -9685,176 +9106,6 @@ ], "time": "2023-07-31T08:31:44+00:00" }, - { - "name": "symfony/http-client", - "version": "v6.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", - "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "keywords": [ - "http" - ], - "support": { - "source": "https://github.com/symfony/http-client/tree/v6.3.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-05T08:41:27+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "3b66325d0176b4ec826bffab57c9037d759c31fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/3b66325d0176b4ec826bffab57c9037d759c31fb", - "reference": "3b66325d0176b4ec826bffab57c9037d759c31fb", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-05-23T14:45:45+00:00" - }, { "name": "symfony/http-foundation", "version": "v6.3.2", @@ -15577,6 +14828,6 @@ "platform": { "php": "^8.1" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/config/aic.php b/config/aic.php index c0ad735e7b..c6de20228a 100644 --- a/config/aic.php +++ b/config/aic.php @@ -19,6 +19,10 @@ 'pdf_s3_endpoint' => env('PDF_S3_ENDPOINT'), 'pdf_debug' => (bool) env('PDF_DEBUG', false), + 'osci_s3_bucket' => env('OSCI_S3_BUCKET'), + 'osci_s3_endpoint' => env('OSCI_S3_ENDPOINT'), + 'osci_s3_regiont' => env('OSCI_S3_REGION'), + 'disable_extra_scripts' => (bool) env('DISABLE_EXTRA_SCRIPTS', false), 'hide_from_tours' => env('HIDE_FROM_TOURS'), @@ -35,4 +39,30 @@ 'show_hours_in_footer' => (bool) env('SHOW_HOURS_IN_FOOTER', false), 'disable_captcha' => (bool) env('DISABLE_CAPTCHA', false), 'show_default_related_items' => (bool) env('SHOW_DEFAULT_RELATED_ITEMS', true), + + // Branding + 'branding' => [ + 'digital_publications' => [ + 'colors' => [ + '#282829', + '#422E22', + '#284725', + '#1E3F49', + '#1C2454', + '#35295A', + '#711F2A', + '#983820', + '#E19E26', + '#9D9FA2', + '#A39282', + '#A1B981', + '#75B4BA', + '#77A8D5', + '#8C8FBE', + '#D58FA2', + '#EC9E6C', + '#F8EB83', + ], + ] + ], ]; diff --git a/config/database.php b/config/database.php index 4445f22dfe..cc2d948edf 100644 --- a/config/database.php +++ b/config/database.php @@ -115,6 +115,15 @@ 'sslmode' => 'prefer', ], + 'osci_migration' => [ + 'driver' => 'sqlite', + 'database' => storage_path('app/migration.sqlite3'), + 'prefix' => '', + 'foreign_key_constraints' => true, + ], + + + ], /* diff --git a/config/filesystems.php b/config/filesystems.php index 46dccf3fd4..fa2ad2573c 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -74,6 +74,16 @@ 'endpoint' => env('PDF_S3_ENDPOINT'), 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), ], + + 'osci_s3' => [ + 'driver' => 's3', + 'key' => env('S3_KEY'), + 'secret' => env('S3_SECRET'), + 'region' => env('OSCI_S3_REGION', env('S3_REGION')), + 'bucket' => env('OSCI_S3_BUCKET'), + 'endpoint' => env('OSCI_S3_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + ], ], /* diff --git a/config/sentry.php b/config/sentry.php index 6900bb1102..b377e840d7 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -11,6 +11,12 @@ // @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/ 'dsn' => env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')), + // @see https://spotlightjs.com/ + // 'spotlight' => env('SENTRY_SPOTLIGHT', false), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#logger + // 'logger' => Sentry\Logger\DebugFileLogger::class, // By default this will log to `storage_path('logs/sentry.log')` + // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) 'release' => trim(exec('git log --pretty="%h" -n1 HEAD')), @@ -19,20 +25,29 @@ 'environment' => env('SENTRY_ENVIRONMENT'), // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#sample-rate - 'sample_rate' => env('SENTRY_SAMPLE_RATE') === null ? 1.0 : (float)env('SENTRY_SAMPLE_RATE'), + 'sample_rate' => env('SENTRY_SAMPLE_RATE') === null ? 1.0 : (float) env('SENTRY_SAMPLE_RATE'), // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces-sample-rate - 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_TRACES_SAMPLE_RATE'), + 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_TRACES_SAMPLE_RATE'), // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#profiles-sample-rate - 'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_PROFILES_SAMPLE_RATE'), + 'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_PROFILES_SAMPLE_RATE'), // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send-default-pii 'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false), + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore-exceptions + // 'ignore_exceptions' => [], + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore-transactions + 'ignore_transactions' => [ + // Ignore Laravel's default health URL + '/up', + ], + // Breadcrumb specific configuration 'breadcrumbs' => [ - // Capture Laravel logs in breadcrumbs + // Capture Laravel logs as breadcrumbs 'logs' => env('SENTRY_BREADCRUMBS_LOGS_ENABLED', true), // Capture Laravel cache events (hits, writes etc.) as breadcrumbs @@ -55,12 +70,15 @@ // Capture HTTP client request information as breadcrumbs 'http_client_requests' => env('SENTRY_BREADCRUMBS_HTTP_CLIENT_REQUESTS_ENABLED', true), + + // Capture send notifications as breadcrumbs + 'notifications' => env('SENTRY_BREADCRUMBS_NOTIFICATIONS_ENABLED', true), ], // Performance monitoring specific configuration 'tracing' => [ // Trace queue jobs as their own transactions (this enables tracing for queue jobs) - 'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', false), + 'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', true), // Capture queue jobs as spans when executed on the sync driver 'queue_jobs' => env('SENTRY_TRACE_QUEUE_JOBS_ENABLED', true), @@ -68,9 +86,15 @@ // Capture SQL queries as spans 'sql_queries' => env('SENTRY_TRACE_SQL_QUERIES_ENABLED', true), + // Capture SQL query bindings (parameters) in SQL query spans + 'sql_bindings' => env('SENTRY_TRACE_SQL_BINDINGS_ENABLED', false), + // Capture where the SQL query originated from on the SQL query spans 'sql_origin' => env('SENTRY_TRACE_SQL_ORIGIN_ENABLED', true), + // Define a threshold in milliseconds for SQL queries to resolve their origin + 'sql_origin_threshold_ms' => env('SENTRY_TRACE_SQL_ORIGIN_THRESHOLD_MS', 100), + // Capture views rendered as spans 'views' => env('SENTRY_TRACE_VIEWS_ENABLED', true), @@ -80,12 +104,18 @@ // Capture HTTP client requests as spans 'http_client_requests' => env('SENTRY_TRACE_HTTP_CLIENT_REQUESTS_ENABLED', true), + // Capture Laravel cache events (hits, writes etc.) as spans + 'cache' => env('SENTRY_TRACE_CACHE_ENABLED', true), + // Capture Redis operations as spans (this enables Redis events in Laravel) 'redis_commands' => env('SENTRY_TRACE_REDIS_COMMANDS', false), // Capture where the Redis command originated from on the Redis command spans 'redis_origin' => env('SENTRY_TRACE_REDIS_ORIGIN_ENABLED', true), + // Capture send notifications as spans + 'notifications' => env('SENTRY_TRACE_NOTIFICATIONS_ENABLED', true), + // Enable tracing for requests without a matching route (404's) 'missing_routes' => env('SENTRY_TRACE_MISSING_ROUTES_ENABLED', false), diff --git a/database/factories/DigitalPublicationArticleFactory.php b/database/factories/DigitalPublicationArticleFactory.php index 82942d83ba..fe6dc71503 100644 --- a/database/factories/DigitalPublicationArticleFactory.php +++ b/database/factories/DigitalPublicationArticleFactory.php @@ -14,7 +14,7 @@ public function definition(): array { return [ 'title' => $this->faker->words(5, true), - 'type' => $this->faker->randomElement(array_map( + 'article_type' => $this->faker->randomElement(array_map( fn ($type) => $type->value, DigitalPublicationArticleType::cases() )), diff --git a/database/factories/ExperienceFactory.php b/database/factories/ExperienceFactory.php new file mode 100644 index 0000000000..a952b691f0 --- /dev/null +++ b/database/factories/ExperienceFactory.php @@ -0,0 +1,22 @@ + false, + 'interactive_feature_id' => InteractiveFeature::factory(), + 'published' => true, + 'title' => ucfirst(fake()->words(3, asText: true)), + ]; + } +} diff --git a/database/factories/InteractiveFeatureFactory.php b/database/factories/InteractiveFeatureFactory.php new file mode 100644 index 0000000000..da47ef11fd --- /dev/null +++ b/database/factories/InteractiveFeatureFactory.php @@ -0,0 +1,20 @@ + false, + 'published' => true, + 'title' => ucfirst(fake()->words(3, asText: true)), + ]; + } +} diff --git a/database/migrations/2024_08_21_162309_drop_sidebar_title_display_column.php b/database/migrations/2024_08_21_162309_drop_sidebar_title_display_column.php new file mode 100644 index 0000000000..b0f9a7adab --- /dev/null +++ b/database/migrations/2024_08_21_162309_drop_sidebar_title_display_column.php @@ -0,0 +1,22 @@ +dropColumn('sidebar_title_display'); + }); + } + + public function down(): void + { + Schema::table('digital_publications', function (Blueprint $table) { + $table->text('sidebar_title_display')->nullable(); + }); + } +}; diff --git a/database/migrations/2024_08_30_142048_add_article_type_column_to_digital_publication_articles_table.php b/database/migrations/2024_08_30_142048_add_article_type_column_to_digital_publication_articles_table.php new file mode 100644 index 0000000000..e9387453a4 --- /dev/null +++ b/database/migrations/2024_08_30_142048_add_article_type_column_to_digital_publication_articles_table.php @@ -0,0 +1,39 @@ + $enum->value, DigitalPublicationArticleType::cases()); + Schema::table('digital_publication_articles', function (Blueprint $table) use ($types) { + $table->enum('article_type', $types)->default('text'); + }); + foreach (DigitalPublicationArticle::withTrashed()->get() as $article) { + $article->article_type = $article->type; + $article->save(); + } + Schema::table('digital_publication_articles', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } + + public function down(): void + { + Schema::table('digital_publication_articles', function (Blueprint $table) { + $table->string('type')->nullable(false)->default('text'); + }); + foreach (DigitalPublicationArticle::withTrashed()->get() as $article) { + $article->type = $article->article_type; + $article->save(); + } + Schema::table('digital_publication_articles', function (Blueprint $table) { + $table->dropColumn('article_type'); + }); + } +}; diff --git a/database/migrations/2024_09_10_135205_drop_header_title_display_column_from_digital_publications.php b/database/migrations/2024_09_10_135205_drop_header_title_display_column_from_digital_publications.php new file mode 100644 index 0000000000..4ddd2f4cd3 --- /dev/null +++ b/database/migrations/2024_09_10_135205_drop_header_title_display_column_from_digital_publications.php @@ -0,0 +1,22 @@ +dropColumn('header_title_display'); + }); + } + + public function down(): void + { + Schema::table('digital_publications', function (Blueprint $table) { + $table->string('header_title_display')->nullable(); + }); + } +}; diff --git a/database/migrations/2024_10_08_114028_update_digital_publication_article_image_blocks.php b/database/migrations/2024_10_08_114028_update_digital_publication_article_image_blocks.php new file mode 100644 index 0000000000..98c8116be5 --- /dev/null +++ b/database/migrations/2024_10_08_114028_update_digital_publication_article_image_blocks.php @@ -0,0 +1,32 @@ +where('blockable_type', 'digitalPublicationArticles') + ->get(); + + // Update the content JSON column for image block on digital publication articles + foreach ($digiPubImageBlocks as $block) { + $content = $block->content; + $content['size'] = 'l'; + $content['use_alt_background'] = true; + $content['use_contain'] = true; + + // Update the block with the new JSON + $block->content = $content; + $block->save(); + } + } + + public function down(): void + { + // There's no going back... + } +}; diff --git a/database/migrations/2024_10_22_131536_add_type_override_column_to_exhibitions.php b/database/migrations/2024_10_22_131536_add_type_override_column_to_exhibitions.php new file mode 100644 index 0000000000..96e23eed16 --- /dev/null +++ b/database/migrations/2024_10_22_131536_add_type_override_column_to_exhibitions.php @@ -0,0 +1,22 @@ +string(column: 'type_override')->nullable(); + }); + } + + public function down(): void + { + Schema::table(table: 'exhibitions', callback: function (Blueprint $table): void { + $table->dropColumn(columns: 'type_override'); + }); + } +}; diff --git a/database/migrations/2024_10_25_100150_create_generic_page_sponsor_table.php b/database/migrations/2024_10_25_100150_create_generic_page_sponsor_table.php new file mode 100644 index 0000000000..314ebf4f2c --- /dev/null +++ b/database/migrations/2024_10_25_100150_create_generic_page_sponsor_table.php @@ -0,0 +1,33 @@ +increments('id'); + $table->timestamps(); + + createDefaultRelationshipTableFields($table, 'generic_page', 'sponsor'); + $table->integer('position')->unsigned()->index(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('generic_page_sponsor'); + } +}; diff --git a/database/migrations/2024_11_13_121456_update_sizes_of_digital_publication_article_media_blocks.php b/database/migrations/2024_11_13_121456_update_sizes_of_digital_publication_article_media_blocks.php new file mode 100644 index 0000000000..84cd04bae4 --- /dev/null +++ b/database/migrations/2024_11_13_121456_update_sizes_of_digital_publication_article_media_blocks.php @@ -0,0 +1,48 @@ + [], + 'artwork' => [], + 'image_slider' => [], + 'layered_image_viewer' => [], + 'media_embed' => [], + 'mirador_embed' => [], + 'table' => [], + 'video' => ['use_alt_background'], + 'vtour_embed' => [], + ]; + + foreach ($types as $type => $fieldsToSetToTrue) { + // Find all blocks of digitalPublicationArticles + $digiPubBlocks = Block::where('type', $type) + ->where('blockable_type', 'digitalPublicationArticles') + ->get(); + + // Update the content JSON column for image block on digital publication articles + foreach ($digiPubBlocks as $block) { + $content = $block->content; + $content['size'] = 'l'; + + foreach ($fieldsToSetToTrue as $field) { + $content[$field] = true; + } + + // Update the block with the new JSON + $block->content = $content; + $block->save(); + } + } + } + + public function down(): void + { + // There's no going back... + } +}; diff --git a/frontend/js/app.js b/frontend/js/app.js index ae992335aa..fe27bc1c49 100755 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -1,6 +1,6 @@ import { manageBehaviors, resized, getCurrentMediaQuery, forEach, lazyLoad } from '@area17/a17-helpers'; import * as Behaviors from './behaviors/core'; -import { lockBody, focusTrap, focusDisplayHandler, ajaxPageLoad, ajaxPageLoadMaskToggle, historyProxy, loadProgressBar, setScrollDirection, anchorLinksScroll, fontObservers, modals, collectionFilters, googleTagManager, accessibleContent, headerHeight, roadblock } from './functions/core'; +import { lockBody, focusTrap, focusDisplayHandler, ajaxPageLoad, ajaxPageLoadMaskToggle, historyProxy, loadProgressBar, setScrollDirection, anchorLinksScroll, headerAwareScroll, fontObservers, modals, collectionFilters, googleTagManager, accessibleContent, headerHeight, roadblock } from './functions/core'; /** * A17 * @see Doc: https://code.area17.com/a17/fe-boilerplate/wikis/js-app @@ -57,6 +57,8 @@ document.addEventListener('DOMContentLoaded', function(){ focusDisplayHandler(); // Scroll anchor links anchorLinksScroll(); + // Scroll to hash and adjust for header + headerAwareScroll(); // Listen for modal open/close requests modals(); // Listen for modal open/close requests diff --git a/frontend/js/behaviors/blocks360/viewer360.js b/frontend/js/behaviors/blocks360/viewer360.js index 3ff51ae6a7..ccc3c09b07 100644 --- a/frontend/js/behaviors/blocks360/viewer360.js +++ b/frontend/js/behaviors/blocks360/viewer360.js @@ -26,9 +26,9 @@ const viewer360 = function(container) { frames360 = frames360.map(frame => ({ frame: frame.frame, src: assetURL(frame.src, { - w: isLarge ? windowWidth - 120 : windowWidth, - h: isLarge ? windowHeight - 80 : windowHeight, - q: 75 + w: isLarge ? (windowWidth - 120) * (window.devicePixelRatio || 1) : windowWidth * (window.devicePixelRatio || 1), + h: isLarge ? (windowHeight - 80) * (window.devicePixelRatio || 1) : windowHeight * (window.devicePixelRatio || 1), + q: 75 }) })); diff --git a/frontend/js/behaviors/core/contrastText.js b/frontend/js/behaviors/core/contrastText.js new file mode 100644 index 0000000000..6f1afaa581 --- /dev/null +++ b/frontend/js/behaviors/core/contrastText.js @@ -0,0 +1,14 @@ +import { textContrast } from "text-contrast" + +const contrastText = function(container) { + function _init() { + let lightOrDark = textContrast.isLightOrDark(container.dataset.backgroundColor); + container.classList.add(`s-${lightOrDark}-background`); + } + + this.init = function() { + _init(); + } +} + +export default contrastText; diff --git a/frontend/js/behaviors/core/index.js b/frontend/js/behaviors/core/index.js index 4676a019cc..7e9237c628 100644 --- a/frontend/js/behaviors/core/index.js +++ b/frontend/js/behaviors/core/index.js @@ -64,3 +64,5 @@ export { default as primaryNavigation } from './primaryNavigation'; export { default as editorialHeader } from './editorialHeader'; export { default as loadRelatedSidebar } from './loadRelatedSidebar'; export { default as rangedAccordion } from './rangedAccordion'; +export { default as stickyDigitalPublicationHeader } from './stickyDigitalPublicationHeader'; +export { default as contrastText } from './contrastText'; diff --git a/frontend/js/behaviors/core/rangedAccordion.js b/frontend/js/behaviors/core/rangedAccordion.js index 0f6414eca3..5e27074289 100644 --- a/frontend/js/behaviors/core/rangedAccordion.js +++ b/frontend/js/behaviors/core/rangedAccordion.js @@ -72,4 +72,4 @@ const rangedAccordion = function(container) { }; } -export default rangedAccordion; \ No newline at end of file +export default rangedAccordion; diff --git a/frontend/js/behaviors/core/stickyDigitalPublicationHeader.js b/frontend/js/behaviors/core/stickyDigitalPublicationHeader.js new file mode 100644 index 0000000000..794371c0c4 --- /dev/null +++ b/frontend/js/behaviors/core/stickyDigitalPublicationHeader.js @@ -0,0 +1,90 @@ +const stickyDigitalPublicationHeader = function(container) { + const MIN_CONTAINER_HEIGHT = 180; // in px + const HEADER_IS_STICKY = 's-sticky-digital-publication-header'; + const HEADER_IS_SHRINKING = 's-shrinking-digital-publication-header'; + + const getOffsetTop = element => { + let offsetTop = 0; + while(element) { + offsetTop += element.offsetTop; + element = element.offsetParent; + } + return offsetTop; + }; + + const setState = targetState => { + let classList = document.documentElement.classList; + let possibleStates = [HEADER_IS_STICKY, HEADER_IS_SHRINKING]; + possibleStates.forEach(state => { + if (state !== targetState && classList.contains(state)) { + classList.remove(state); + } + }); + + if (targetState && !classList.contains(targetState)) { + classList.add(targetState); + } + + currentState = targetState; + }; + + const resetScroll = () => { + if (currentState == HEADER_IS_STICKY) { + container.scrollTo(0, 0); + } + }; + + let currentState; + let containerOffsetHeight; + let scrollTop; + let navContainerHeight; + + function handleScroll() { + window.requestAnimationFrame(update); + } + + function handleResize() { + containerOffsetHeight = getOffsetTop(container) + container.offsetHeight; + navContainerHeight = document.querySelector('.g-header').clientHeight; + handleScroll(); + } + + function update() { + scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + + if (scrollTop < (containerOffsetHeight - MIN_CONTAINER_HEIGHT)*.75) { + // If user has not scrolled past nav container to the digital publication + // header, unset state + setState(null); + } else if (scrollTop < (containerOffsetHeight - MIN_CONTAINER_HEIGHT)) { + // If user has not scrolled to the container's minimum height, header is + // "shrinking down to the minimum size" + setState(HEADER_IS_SHRINKING); + } else { + // As user continues to scroll down the page, keep the header stuck to the + // top of the page + setState(HEADER_IS_STICKY); + } + } + + function _init() { + window.addEventListener('resized', handleResize); + window.addEventListener('scroll', handleScroll); + handleResize(); + resetScroll(); + handleScroll(); + } + + this.destroy = function() { + window.removeEventListener('scroll', handleScroll); + + // Remove properties of this behavior + A17.Helpers.purgeProperties(this); + } + + this.init = function() { + _init(); + } +} + +export default stickyDigitalPublicationHeader; diff --git a/frontend/js/behaviors/core/stickySidebar.js b/frontend/js/behaviors/core/stickySidebar.js index 7ed69a5404..fb7d3618c2 100644 --- a/frontend/js/behaviors/core/stickySidebar.js +++ b/frontend/js/behaviors/core/stickySidebar.js @@ -2,6 +2,21 @@ import { triggerCustomEvent, setFocusOnTarget } from '@area17/a17-helpers'; import { mediaQuery } from '../../functions/core'; const stickySidebar = function(container){ + const isDigitalPublicationArticle = document.documentElement.classList.contains('p-digitalpublicationarticle-show'); + const isDigitalPublicationLanding = document.documentElement.classList.contains('p-digitalpublications-show'); + const isDigitalPublicationListing = document.documentElement.classList.contains('p-digitalpublications-showlisting'); + const isContribution = document.documentElement.classList.contains('p-t-contributions'); + const isMagazineIssue = document.documentElement.classList.contains('p-magazineissue-latest') || document.documentElement.classList.contains('p-magazineissue-show'); + const logoSelector = '.m-article-actions--publication__logo'; + const logo = document.querySelector(logoSelector); + const sidebarOverlayState = 'is-sidebar-overlay'; + const stickyHeaderContainer = document.querySelector('.m-article-header'); + + let containerTop; + let currentState; + let overlayActive = document.documentElement.classList.contains(sidebarOverlayState); + let savedFocus; + let savedScroll; const getOffsetTop = element => { let offsetTop = 0; @@ -12,6 +27,11 @@ const stickySidebar = function(container){ return offsetTop; } + function _getPaddingTop(node) { + let style = window.getComputedStyle(node); + return parseInt(style.getPropertyValue('padding-top')); +} + const setState = targetState => { let classList = document.documentElement.classList; @@ -38,43 +58,39 @@ const stickySidebar = function(container){ } } - let article = document.querySelector('.o-article'); - let logo = document.querySelector('.m-article-actions--publication__logo'); - - let scrollTop; - - let windowHeight; - let containerTop; - let containerHeight; - - let navContainer; - - const sidebarOverlayState = 'is-sidebar-overlay'; - let overlayActive = document.documentElement.classList.contains(sidebarOverlayState); - - let savedFocus; - let savedScroll; - - let currentState; - function update() { - scrollTop = document.documentElement.scrollTop || document.body.scrollTop; - - containerHeight = container.offsetHeight; - - navContainer = document.querySelector('.g-header'); - - if (scrollTop < containerTop) { + const article = document.querySelector('.o-article'); + const hasUnstickyHeader = document.documentElement.classList.contains('s-unsticky-header'); + const hasStickyNav = document.documentElement.classList.contains('s-scroll-direction-up'); + const hasDigitalPublicationStickyHeader = document.documentElement.classList.contains('s-sticky-digital-publication-header'); + const hasDigitalPublicationUnstickyHeader = document.documentElement.classList.contains('s-unsticky-digital-publication-header'); + const navContainer = document.querySelector('.g-header'); + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + + let digitalPublicaitonStickyHeaderHeight = hasDigitalPublicationStickyHeader ? stickyHeaderContainer.clientHeight : 0; + let unstickyNavHeight = hasUnstickyHeader ? navContainer.clientHeight : 0; + let marginToAdd = 0; + + // `containerTop` is caluclated in the `handleResize` method + if (scrollTop < containerTop - digitalPublicaitonStickyHeaderHeight) { top(); - container.style.marginTop = '0px'; + } else if (scrollTop + container.offsetHeight > article.offsetHeight + unstickyNavHeight) { + bottom(); } else { - if (scrollTop + containerHeight > article.offsetHeight) { - bottom(); - } else { - sticky(); - (document.documentElement.classList.contains('s-scroll-direction-up') && !document.documentElement.classList.contains('s-unsticky-header')) ? container.style.marginTop = navContainer.offsetHeight + 'px' : container.style.marginTop = '0px'; + sticky(); + // Only add margin if the screen width is 1200px or above + if (window.innerWidth >= 1200) { + if (isDigitalPublicationLanding || isDigitalPublicationListing) { + marginToAdd += !hasUnstickyHeader ? navContainer.clientHeight : 0; + marginToAdd += !hasDigitalPublicationUnstickyHeader ? stickyHeaderContainer.clientHeight : 0; + } + if (isMagazineIssue) { + let paddingTop = _getPaddingTop(container); + marginToAdd += hasStickyNav ? navContainer.clientHeight - paddingTop : 0; + } } } + container.style.marginTop = marginToAdd + 'px'; } function top() { @@ -96,8 +112,24 @@ const stickySidebar = function(container){ function handleResize() { top(); - windowHeight = window.innerHeight || document.documentElement.clientHeight; + let contributionHeaderHeight = 0; + let logoList = document.querySelectorAll(logoSelector); + let hasDigitalPublicationStickyHeader = document.documentElement.classList.contains('s-sticky-digital-publication-header'); + + for (let i = 0; i < logoList.length; i++) { + contributionHeaderHeight += logoList[i].clientHeight; + } + containerTop = getOffsetTop(container) + document.body.scrollTop; + if (hasDigitalPublicationStickyHeader) { + containerTop -= stickyHeaderContainer.offsetHeight; + } + if ((isDigitalPublicationArticle && isContribution) || isDigitalPublicationLanding) { + containerTop += contributionHeaderHeight; + } + if (isMagazineIssue) { + containerTop -= 30; + } logo.setAttribute('style', 'display: block'); logo.removeAttribute('style'); @@ -175,6 +207,12 @@ const stickySidebar = function(container){ } } + function _escape(event) { + if (overlayActive && event.keyCode === 27) { + triggerCustomEvent(document, 'stickySidebar:close'); + } + } + function _init() { window.addEventListener('scroll', handleScroll); window.addEventListener('resized', handleResize); @@ -189,6 +227,7 @@ const stickySidebar = function(container){ document.addEventListener('mediaQueryUpdated',_mediaQueryUpdated, false); window.addEventListener('hashchange', _hideSidebar, false); + window.addEventListener('keyup', _escape, false); handleResize(); handleScroll(); @@ -206,6 +245,7 @@ const stickySidebar = function(container){ document.removeEventListener('mediaQueryUpdated',_mediaQueryUpdated); window.removeEventListener('hashchange', _hideSidebar); + window.removeEventListener('keyup', _escape); // Remove properties of this behavior A17.Helpers.purgeProperties(this); diff --git a/frontend/js/functions/core/headerAwareScroll.js b/frontend/js/functions/core/headerAwareScroll.js new file mode 100644 index 0000000000..b948cb993a --- /dev/null +++ b/frontend/js/functions/core/headerAwareScroll.js @@ -0,0 +1,43 @@ +const headerAwareScroll = function(container) { + + // Function to get the scroll position of an element + function getOffsetTop(element) { + let offsetTop = 0; + while (element) { + offsetTop += element.offsetTop; + element = element.offsetParent; + } + return offsetTop; + } + + // Get the current URL hash + const hash = window.location.hash; + + if (hash) { + // Find the target element by ID + const targetElement = document.getElementById(hash.substring(1)); + + if (targetElement) { + // Get the scroll position of the target element + let scrollPosition = getOffsetTop(targetElement); + + const headerFeature = document.querySelectorAll('.m-article-header--digital-publication')[0]; + if (headerFeature) { + const headerHeight = headerFeature.getBoundingClientRect().height; + // Add the height to the scroll position + scrollPosition -= headerHeight + 60; + } + + + // Scroll to the target element + window.requestAnimationFrame(() => { + window.scrollTo({ + top: scrollPosition, + behavior: 'instant' + }); + }); + } + } +} + +export default headerAwareScroll; \ No newline at end of file diff --git a/frontend/js/functions/core/index.js b/frontend/js/functions/core/index.js index 06ae528a71..e51e81864a 100644 --- a/frontend/js/functions/core/index.js +++ b/frontend/js/functions/core/index.js @@ -11,6 +11,7 @@ export { default as ajaxableHref } from './ajaxableHref'; export { default as loadProgressBar } from './loadProgressBar'; export { default as setScrollDirection } from './setScrollDirection'; export { default as anchorLinksScroll } from './anchorLinksScroll'; +export { default as headerAwareScroll } from './headerAwareScroll'; export { default as fontObservers } from './fontObservers'; export { default as modals } from './modals'; export { default as mediaQuery } from './mediaQuery'; diff --git a/frontend/scss/_importsCore.scss b/frontend/scss/_importsCore.scss index 83cd8b82d5..4b22cc1fea 100755 --- a/frontend/scss/_importsCore.scss +++ b/frontend/scss/_importsCore.scss @@ -194,3 +194,4 @@ @import 'state/s-closer-look-scroll-blocked'; @import 'state/s-closer-look-footer-stuck'; @import 'state/s-layered-image-viewer-active'; +@import 'state/s-contrast-text'; diff --git a/frontend/scss/molecules/_m-article-header.scss b/frontend/scss/molecules/_m-article-header.scss index 7977343af2..60837089df 100644 --- a/frontend/scss/molecules/_m-article-header.scss +++ b/frontend/scss/molecules/_m-article-header.scss @@ -1076,7 +1076,11 @@ min-height: initial; z-index: auto; - white-space: nowrap; + white-space: normal; + + >a:hover { + text-decoration: underline !important; + } .f-headline-editorial { @include untuck(); @@ -1099,35 +1103,46 @@ } .title { - margin-top: 20px; + font-size: 38px; + line-height: 46px; + margin-right: 35px; + margin-top: 35px; } .subtitle { - text-align: right; color: $color__text--title-contrast; - - margin-top: 75px; - margin-bottom: 24px; + font-size: 18px; + line-height: 25px; + margin-bottom: 26px; + margin-right: 26px; + margin-top: 26px; + text-align: left; } - @include breakpoint('small') { + @include breakpoint('small-') { + .m-article-header__img { height: 300px; + + img { + z-index: map-get($zindexs, 'header'); + } } .title { - margin-top: 36px; + font-size: 22px; + line-height: 28px; + margin-bottom: 26px; } .subtitle { - margin-top: 100px; - margin-bottom: 36px; + display: none; } } @include breakpoint('medium') { .m-article-header__text { - padding-right: 0; + padding-right: 4.875vw !important; } .m-article-header__img { @@ -1135,20 +1150,30 @@ } .title { - margin-top: 40px; + font-size: 28px; + line-height: 34px; + margin-top: 35px; } .subtitle { - margin-top: 100px; - margin-bottom: 44px; + margin-bottom: 35px; + margin-top: 35px; } } - @include breakpoint('large+') { + @include breakpoint('medium+') { flex-flow: row-reverse nowrap; + .title { + opacity: 1; + } + + .subtitle { + opacity: 1; + } + .m-article-header__text { - flex: 0 0 38%; + flex: 0 0 colspan(19, 'large'); padding-right: initial; padding-left: initial; } @@ -1157,21 +1182,256 @@ left: auto; margin-left: auto; - flex: 0 0 62%; - height: 75vh; + flex: 0 0 colspan(50, 'large'); + height: 58vh; right: 50%; margin-right: -50vw; + + &::before { + z-index: -1; + } } + } - .title { - margin-top: 50px; + @include breakpoint('large') { + .m-article-header__text { + flex: 0 0 colspan(19, 'large'); } + } - .subtitle { - margin-top: 100px; - margin-bottom: 60px; + @include breakpoint('xlarge') { + .m-article-header__text::before { + left: 0; + margin-left: -100vw; + max-width: 100vw; + } + .m-article-header__text { + flex: 0 0 colspan(19, 'xlarge'); } } + + .p-digitalpublications-showlisting & { + @include breakpoint('medium-') { + display: none; + } + + @include breakpoint('large+') { + .m-article-header__img { + height: 180px; + + img { + display: none; + } + } + + .title { + margin-bottom: auto; + margin-top: auto; + } + + .subtitle { + display: none; + } + + } + } +} + +// TODO Isolate the following to a _s-sticky-digital-publication-header.scss file? +.s-shrinking-digital-publication-header.s-scroll-direction-down { + @include breakpoint('medium+') { + .m-article-header__img img { + opacity: 0; + } + + .m-article-header__text .title { + opacity: 0; + } + + .m-article-header__text .subtitle { + opacity: 0; + } + } +} + +.s-sticky-digital-publication-header:not(.s-allow-top-link), +.p-t-entry:not(.s-allow-top-link), +.p-t-contributions:not(.s-allow-top-link), +.p-t-about:not(.s-allow-top-link) { + .m-sidebar-toggle { + position: relative !important; + } +} + +.s-sticky-digital-publication-header.s-allow-top-link, +.p-t-entry.s-allow-top-link, +.p-t-contributions.s-allow-top-link, +.p-t-about.s-allow-top-link { + .m-sidebar-toggle { + position: fixed !important; + top: 0; + width: 100vw; + z-index: map-get($zindexs, 'header'); + + &::before { + left: 0; + width: 150vw; + } + } +} + +.s-sticky-digital-publication-header, .p-digitalpublications-showlisting { + .m-article-header--digital-publication { + .m-article-header__text { + justify-content: center; + + .title { + font-size: 26px !important; + line-height: 24px !important; + } + } + } +} + +.s-shrinking-digital-publication-header.is-sidebar-top { + @include breakpoint('medium+') { + .m-article-header__img img { + opacity: 1; + } + + .m-article-header__text .title { + opacity: 1; + } + + .m-article-header__text .subtitle { + opacity: 1; + } + } +} + +.s-sticky-digital-publication-header { + &.is-sidebar-fixed, &.is-sidebar-bottom { + @include breakpoint('medium+') { + .m-article-header__img img { + opacity: 0; + } + + .m-article-header__text .title { + opacity: 0; + } + + .m-article-header__text .subtitle { + opacity: 0; + } + } + + } + @include breakpoint('medium-') { + .m-sidebar-toggle { + top: 0; + width: 100vw; + z-index: map-get($zindexs, 'header'); + + &::before { + left: 0; + width: 150vw; + } + } + + .m-article-header__spacer { + min-height: 54px; + } + } + + @include breakpoint('large+') { + .m-article-header--digital-publication { + position: fixed; + top: 0; + width: 100%; + z-index: map-get($zindexs, 'header'); + + .m-article-header__text::before { + left: -40vw; + margin-left: 0; + max-width: 150vw; + } + + .m-article-header__text .title { + margin-bottom: auto; + margin-top: auto; + opacity: 1; + font-size: 26px !important; + line-height: 28px !important; + } + + .m-article-header__text .subtitle { + display: none; + } + + .m-article-header__img { + height: 180px; + flex: 0 0 colspan(56, 'large'); + + img { + opacity: 0; + } + } + } + + .m-article-header__spacer { + display: block; + width: 100vw; + } + + &.p-digitalpublications-show { + .m-article-header__spacer { + height: 58vh; + } + } + + &.p-digitalpublications-showlisting { + .m-article-header__spacer { + height: 180px; + } + } + } +} + +.m-article-header__spacer { + display: none; +} + +.m-article-header--digital-publication-article ~ .m-article-header__text { + height: 140px; + color: $color__white; + margin-top: -6px; + + @include breakpoint('large+') { + display: flex; + align-items: center; + } + + &::before { + content: ""; + position: absolute; + top: 40vh; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + height: 150px; + width: 200vw; + margin-left: -100vw; + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + z-index: 3; + } + } + } + + .title { + z-index: 4; + } } .m-article-header--generic { diff --git a/frontend/scss/molecules/_m-info-trigger.scss b/frontend/scss/molecules/_m-info-trigger.scss index 6ee1310318..76921898d7 100644 --- a/frontend/scss/molecules/_m-info-trigger.scss +++ b/frontend/scss/molecules/_m-info-trigger.scss @@ -64,6 +64,7 @@ padding: 12px 16px 16px; border: 0 none; border-radius: $border-radius; + color: $color__white; background-color: rgba($color__black, .7); transition: opacity .15s; overflow: hidden; diff --git a/frontend/scss/molecules/_m-listing.scss b/frontend/scss/molecules/_m-listing.scss index 615e0b1026..2abd85bb0e 100644 --- a/frontend/scss/molecules/_m-listing.scss +++ b/frontend/scss/molecules/_m-listing.scss @@ -2156,64 +2156,70 @@ a:focus:hover .btn--magazine { } .m-listing----cover { - @include breakpoint('xsmall') { - margin-bottom: 20px; - height: 80px; - width: colspan(design-cols-to-colspan(12), xsmall) !important; - } + &.m-listing { - @include breakpoint('small+') { - margin-bottom: 20px; - height: 80px; - width: colspan(design-cols-to-colspan(12), small) !important; - } + @include breakpoint('xsmall') { + margin-bottom: 20px; + height: 80px; + width: colspan(design-cols-to-colspan(12), xsmall) !important; + } - @include breakpoint('medium+') { - height: auto; - width: colspan(design-cols-to-colspan(4), medium) !important; - } + @include breakpoint('small+') { + margin-bottom: 20px; + height: 80px; + width: colspan(design-cols-to-colspan(12), small) !important; + } - @include breakpoint('large+') { - height: auto; - width: colspan(design-cols-to-colspan(3), large) !important; - } + @include breakpoint('medium+') { + height: auto; + width: colspan(design-cols-to-colspan(4), medium) !important; + } - @include breakpoint('xlarge') { - height: auto; - width: colspan(design-cols-to-colspan(3), xlarge) !important; - } + @include breakpoint('large+') { + height: auto; + width: colspan(11.25, large) !important; + margin-left: colspan(2, large) !important; + } - .m-listing__link:hover + .title { - color: rgba($color__white, .8) !important; - } + @include breakpoint('xlarge') { + height: auto; + width: colspan(11.33, xlarge) !important; + margin-left: colspan(2, xlarge) !important; + } - .m-listing__img { - aspect-ratio: 5/6; - height: 100%; - width: 100%; - position: relative; + a:hover .title { + color: rgba($color__white, .8) !important; + } - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; + .m-listing__img { + aspect-ratio: 5/6; height: 100%; - background: rgba($color__black, .4); - z-index: 1; + width: 100%; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba($color__black, .4); + z-index: 1; + } } - } - .m-listing----cover-title { - position: absolute; - display: flex; - justify-content: center; - align-items: center; - color: $color__white; - height: 100%; - width: 100%; - z-index: 2; + .m-listing----cover-title { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + color: $color__white; + height: 100%; + width: 100%; + z-index: 2; + } } } @@ -2221,19 +2227,23 @@ a:focus:hover .btn--magazine { &.m-listing { @include breakpoint('small+') { - width: colspan(design-cols-to-colspan(12), small) !important; + width: colspan(58, small) !important; + margin-left: colspan(2, small) !important; } - + @include breakpoint('medium+') { - width: colspan(design-cols-to-colspan(4), medium) !important; + width: colspan(18, medium) !important; + margin-left: colspan(2, medium) !important; } - + @include breakpoint('large+') { - width: colspan(design-cols-to-colspan(3), large) !important; + width: colspan(11.33, large) !important; + margin-left: colspan(2, large) !important; } - + @include breakpoint('xlarge') { - width: colspan(design-cols-to-colspan(3), xlarge) !important; + width: colspan(11.33, xlarge) !important; + margin-left: colspan(2, xlarge) !important; } .short-description { @@ -2251,6 +2261,7 @@ a:focus:hover .btn--magazine { display: none; } } + .link { display: none } @@ -2265,24 +2276,32 @@ a:focus:hover .btn--magazine { @include breakpoint('small+') { width: colspan(28, small) !important; + margin-left: colspan(2, small) !important; } @include breakpoint('medium+') { width: colspan(13, medium) !important; + margin-left: colspan(2, medium) !important; } @include breakpoint('large+') { - width: colspan(9, large) !important; + width: colspan(8, large) !important; + margin-left: colspan(2, large) !important; } @include breakpoint('xlarge') { - width: colspan(9, xlarge) !important; + width: colspan(8, xlarge) !important; + margin-left: colspan(2, xlarge) !important; } .m-listing__img { padding: 50% !important; } + .m-listing__meta { + min-height: unset; + } + .type { font-weight: 400; font-size: 18px !important; @@ -2290,15 +2309,35 @@ a:focus:hover .btn--magazine { .title { @include untuck; + padding-top: 10px; line-height: 1.25em; - font-size: 22px; + font-size: 19px; } } .m-listing--seventy-thirty { &.m-listing { - width: 100% !important; - margin: 24px 0 !important; + @include breakpoint('small') { + margin-left: 0 !important + } + + @include breakpoint('medium') { + margin-left: 0 !important + } + + @include breakpoint('large') { + margin-left: 0 !important; + } + + @include breakpoint('xlarge') { + margin-left: 0 !important; + } + + & { + width: 100% !important; + margin-top: 24px!important; + margin-bottom: 24px!important; + } >.link { display: none !important; @@ -2555,5 +2594,4 @@ a:focus:hover .btn--magazine { .o-grid-listing--feature-4x .m-listing__meta { min-height: 128px; -} - +} \ No newline at end of file diff --git a/frontend/scss/molecules/_m-showcase.scss b/frontend/scss/molecules/_m-showcase.scss index e7f964eef6..f0b932820e 100644 --- a/frontend/scss/molecules/_m-showcase.scss +++ b/frontend/scss/molecules/_m-showcase.scss @@ -39,6 +39,10 @@ } } + .showcase-link:hover { + opacity: 0.8; + } + .showcase-header { flex-basis: 100%; } @@ -111,14 +115,45 @@ background-color: unset !important; + + .m-showcase-wrapper { + @include breakpoint('small-') { + flex-direction: column; + gap: 16px; + } + } + + .showcase-tag { + @include breakpoint('small-') { + font-size: 12px; + } + } + .showcase-title { - margin-top: 12px !important; + @include breakpoint('small-') { + @include untuck; + & { + font-size: 19px !important; + line-height: 4px !important; + } + } + & { + font-size: 28px; + margin-top: 12px !important; + } + } + + .showcase-author { + font-size: 15px; } .showcase-description { * { color: $color__black !important; } + & { + margin: 0 !important; + } } } diff --git a/frontend/scss/molecules/_m-stories-block.scss b/frontend/scss/molecules/_m-stories-block.scss index cb11827992..041911d344 100644 --- a/frontend/scss/molecules/_m-stories-block.scss +++ b/frontend/scss/molecules/_m-stories-block.scss @@ -117,8 +117,10 @@ @include breakpoint('xsmall') { padding-bottom: 10px !important; + padding-left: 0; + padding-right: 0; + padding-top: 0; width: 100%; - padding: 0; } .m-story__link { @@ -181,7 +183,7 @@ display: none; } } - + .m-side-story:nth-of-type(1) { @include breakpoint('small-') { .m-story__link { @@ -190,7 +192,7 @@ } @include breakpoint('xsmall') { - + } } @@ -231,7 +233,7 @@ } .m-side-story:nth-of-type(3)::after { - + } .m-side-story:nth-of-type(4)::after { @@ -249,12 +251,12 @@ -o-transition-timing-function: ease-in-out; transition-timing-function: ease-in-out } - + a:focus .m-story-listing__img,a:hover .m-story-listing__img { opacity: .9 } - + a:active .m-story-listing__img { opacity: 1 } -} \ No newline at end of file +} diff --git a/frontend/scss/molecules/_m-title-bar.scss b/frontend/scss/molecules/_m-title-bar.scss index 9007f8667a..8bd2daa933 100644 --- a/frontend/scss/molecules/_m-title-bar.scss +++ b/frontend/scss/molecules/_m-title-bar.scss @@ -99,6 +99,21 @@ display: none !important; } } + + &.m-digipub-grouping-title-bar { + .title { + font-size: 26px; + letter-spacing: 3px !important; + } + } + + &.m-digipub-subgrouping-title-bar { + margin-top: 20px; + + .title { + font-size: 24px; + } + } } .m-title-bar::before { diff --git a/frontend/scss/organisms/_o-accordion.scss b/frontend/scss/organisms/_o-accordion.scss index 861a14d4bc..e32e6f68a5 100644 --- a/frontend/scss/organisms/_o-accordion.scss +++ b/frontend/scss/organisms/_o-accordion.scss @@ -3,7 +3,7 @@ h3 { padding: 25px 0; } - + .o-article + & { margin-top: 40px; } @@ -390,7 +390,8 @@ margin-bottom: 48px; } -.o-accordion.o-accordion--publication-sidebar { +.o-accordion.o-accordion--publication-sidebar, +.o-accordion.o-accordion--magazine-issue-archive { h3 { padding: 1px 0 0 0; @@ -414,7 +415,8 @@ } } - .o-accordion__panel > .o-accordion--publication-sidebar { + .o-accordion__panel > .o-accordion--publication-sidebar, + .o-accordion__panel > .o-accordion.o-accordion--magazine-issue-archive { padding-left: 20px; &:last-of-type { padding-bottom: 20px; @@ -442,6 +444,10 @@ &::before { padding-top: 25px !important; } + &.active { + font-weight: 400; + color: $color__black--90 !important; + } } &.o-blocks>:last-child { @@ -485,4 +491,4 @@ } } } -} \ No newline at end of file +} diff --git a/frontend/scss/organisms/_o-article__primary-actions.scss b/frontend/scss/organisms/_o-article__primary-actions.scss index c8f4e10785..d18e63ba60 100644 --- a/frontend/scss/organisms/_o-article__primary-actions.scss +++ b/frontend/scss/organisms/_o-article__primary-actions.scss @@ -46,7 +46,7 @@ left: 0; right: 0; top: 0; - height: 1px; + height: 0px; background-color: $color__rules--primary; } @@ -259,15 +259,89 @@ .o-article__primary-actions--digital-publication { @extend %o-article__primary-actions--toggleable; padding-top: 0px; + z-index: 4; - .o-sticky-sidebar__sticker { - padding-top: 40px; + .p-digitalpublications-show &, + .p-digitalpublications-showlisting & { + margin-top: 40px; + } + + .is-sidebar-overlay & .o-sticky-sidebar__sticker { + overflow-y: unset; padding-bottom: 60px; } .m-article-actions--publication__logo { margin-bottom: 40px; - white-space: nowrap; + color: $color__white; + display: flex; + align-items: center; + + .is-sidebar-fixed:not(.is-sidebar-overlay) & { + display: block; + align-items: normal; + } + + .p-digitalpublicationarticle-show.p-t-contributions &, + .p-digitalpublicationarticle-show.p-t-entry & { + background-color: unset; + } + + .p-digitalpublicationarticle-show.p-t-contributions:not(.is-sidebar-overlay) &, + .p-digitalpublicationarticle-show.p-t-entry:not(.is-sidebar-overlay) & { + margin-bottom: 40px; + } + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + height: 150px; + width: 600px; + margin-left: calc(-600px + 50%); + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + width: calc(#{colspan(14, '#{$name}')} * 2 + (#{colspan(1, '#{$name}')} * 2)); + padding-right: calc(#{colspan(1, '#{$name}')} * 2); + margin-left: calc((#{colspan(14, '#{$name}')} * -1) + (#{colspan(1, '#{$name}')} * -1)); + margin-right: colspan(2, #{$name}); + } + } + } + + a { + transition: color 1s; + + .is-sidebar-fixed & { + color: $color__black !important; + } + } + + a:hover, + a:focus { + color: $color__link--publications-hover; + } + + a:active { + color: $color__link--publications-active; + } + + @include breakpoint('medium-') { + margin-top: 40px; + padding-right: 20px; + width: 100%; + } + + @include breakpoint('large+') { + .p-digitalpublications-show &, + .p-digitalpublications-showlisting & { + display: none !important; + } + } @include font-styles-untuck(generate-font-obj( ( @@ -285,15 +359,54 @@ } } + .m-article-header__text::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + height: 150px; + width: 600px; + margin-left: calc(-600px + 50%); + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + width: calc(#{colspan(14, '#{$name}')} * 2 + (#{colspan(1, '#{$name}')} * 2)); + padding-right: calc(#{colspan(1, '#{$name}')} * 2); + margin-left: calc((#{colspan(14, '#{$name}')} * -1) + (#{colspan(1, '#{$name}')} * -1)); + margin-right: colspan(2, #{$name}); + } + } + } + .m-article-actions { margin-top: 50px; margin-bottom: 50px; padding-top: 8px; - border-top: 1px solid $color__rules--primary; } } // Styles specific to magazine issues: +.p-magazineissue-latest, +.p-magazineissue-show { + @include breakpoint('medium-') { + .o-article { + display: flex; + flex-direction: column; + + &>.m-article-header { + order: 1; + } + &>.o-article__primary-actions { + order: 3; + } + &>.o-article__body { + order: 2; + } + } + } +} .o-article__primary-actions--magazine-issue { .o-sticky-sidebar__sticker { padding-top: 100px; @@ -307,9 +420,20 @@ } } - #h-nav-magazine-social { + button#archive { + color: $color__magazine__byzantium !important; + + .o-accordion__trigger-icon { + color: $color__black__90; + } + } + + #h-nav-magazine-social, + hr+.o-accordion .o-accordion>.o-accordion__title>.o-accordion__trigger { + color: $color__black--90 !important; + font-family: $sans-serif-font--loaded; + font-size: 17px; font-weight: 400; - color: $color__black--90; } } diff --git a/frontend/scss/organisms/_o-blocks.scss b/frontend/scss/organisms/_o-blocks.scss index 365d89007e..652024b4b8 100644 --- a/frontend/scss/organisms/_o-blocks.scss +++ b/frontend/scss/organisms/_o-blocks.scss @@ -368,7 +368,7 @@ } @include breakpoint('xlarge') { - width: colspan(43, xlarge); + width: colspan(33, xlarge); margin-right: colspan(15, xlarge, 0, true); } } diff --git a/frontend/scss/organisms/_o-editors-note.scss b/frontend/scss/organisms/_o-editors-note.scss index 5b36e9b4a5..6f95e34c28 100644 --- a/frontend/scss/organisms/_o-editors-note.scss +++ b/frontend/scss/organisms/_o-editors-note.scss @@ -17,7 +17,9 @@ a:focus .f-deck, a:hover .f-body-editorial, a:focus .f-body-editorial { - color: $color__black--61; + &:not(.o-editors-note__title-lockup) { + color: $color__black--61; + } } a .icon--arrow { diff --git a/frontend/scss/pages/_p-issue-show.scss b/frontend/scss/pages/_p-issue-show.scss index c1bd14bca7..69c5ab31b9 100644 --- a/frontend/scss/pages/_p-issue-show.scss +++ b/frontend/scss/pages/_p-issue-show.scss @@ -5,14 +5,38 @@ .p-digitalpublications-showlisting { @extend %sticky-sidebar; - .o-article__body { + .o-article:not(.o-article--generic-page) .o-article__body { border-top: none; + float: none; + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + margin-left: colspan(20, '#{$name}'); + width: colspan(38, '#{$name}'); + } + } } .m-article-header~.o-article__primary-actions::before { content: none; } + .m-article-header~.o-article__primary-actions { + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + width: colspan(18, '#{$name}'); + margin-right: colspan(2, '#{$name}'); + } + } + } + + .m-article-header--digital-publication { + .subtitle { + font-size: 18px; + line-height: 25px; + text-align: left; + } + } + .o-article { padding-bottom: 0; @@ -63,9 +87,15 @@ } } -.p-digitalpublications-show, .p-digitalpublications-showlisting { .o-article__body { + margin-top: 40px !important; + } +} + +.p-digitalpublications-show, +.p-digitalpublications-showlisting { + .o-article__body:has(.o-issue__intro) { margin-top: 40px; } @@ -73,4 +103,22 @@ top: -40px; height: calc(100% + 40px); } + + .o-article__body > .m-title-bar { + margin-top: 55px; + } + + .o-article__body > .m-title-bar:first-of-type:not(:has(+ .m-showcase, + .o-issue__intro)) { + margin-top: 36px !important; + + &::before { + content: none; + } + } +} + +.p-digitalpublications-showlisting { + .m-info-trigger { + display: none; + } } diff --git a/frontend/scss/pages/_p-issuearticle-show.scss b/frontend/scss/pages/_p-issuearticle-show.scss index 47eee63ed0..9328543c84 100644 --- a/frontend/scss/pages/_p-issuearticle-show.scss +++ b/frontend/scss/pages/_p-issuearticle-show.scss @@ -16,6 +16,12 @@ } } + .m-article-header__text { + @include breakpoint('medium-') { + width: 100% !important; + } + } + .o-article__body::before { position: absolute; background-color: $color__rules--primary; @@ -35,6 +41,14 @@ } } + .o-blocks{ + &.o-blocks--with-sidebar { + >.o-gallery { + width: 100% !important; + } + } + } + .o-blocks:not(.o-blocks--bibliographic) > p { @include f-body; } @@ -101,6 +115,52 @@ } } + .m-article-actions--publication__logo { + margin-top: 15px; + color: $color__white; + @include breakpoint('medium-') { + width: 100%; + display: flex; + align-items: center; + padding-right: 20px; + } + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + width: 300vw; + margin-left: calc(-600px + 50%); + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + width: calc(#{colspan(14, '#{$name}')} * 2 + (#{colspan(1, '#{$name}')} * 2) + 30vw); + padding-right: calc(#{colspan(1, '#{$name}')} * 2); + margin-left: calc((#{colspan(14, '#{$name}')} * -1) + (#{colspan(1, '#{$name}')} * -1) - 30vw); + margin-right: colspan(2, #{$name}); + } + } + } + + @include font-styles-untuck(generate-font-obj( + ( + font-family: $serif-font, + font-family-loaded: $serif-font--loaded, + font-loaded-class: $serif-font-loaded-class, + settings: ( + 'xlarge': (font-size: 20, line-height: 26, push: 0), + 'large': (font-size: 20, line-height: 26, push: 0), + 'medium': (font-size: 28, line-height: 34, push: 0), + 'small': (font-size: 22, line-height: 28, push: 0), + 'xsmall': (font-size: 22, line-height: 28, push: 0), + ) + ) + )); + } + .o-article { padding-bottom: 0; @@ -137,7 +197,113 @@ .o-article__body { padding-top: 0; - margin-top: 25px; + margin-top: 60px; + + @include breakpoint('medium') { + width: colspan(58, 'medium'); + } + @include breakpoint('large') { + margin-left: colspan(20, 'large'); + width: colspan(38, 'large'); + } + @include breakpoint('xlarge') { + margin-left: colspan(20, 'xlarge'); + width: colspan(38, 'xlarge'); + } + + &>blockquote.quote, + &>div.author-links, + &>div.m-title-bar, + &>div.o-blocks--bibliographic, + &>.m-listing--sound, + &>h1, + &>h2, + &>h3, + &>h4, + &>p:not(.p--linked) { + @include breakpoint('xsmall') { + width: 100%; + } + @include breakpoint('small') { + width: colspan(52, 'small'); + } + @include breakpoint('medium') { + width: colspan(38, 'medium'); + } + @include breakpoint('large') { + width: colspan(28, 'large'); + } + @include breakpoint('xlarge') { + width: colspan(25, 'xlarge'); + } + } + } + + .o-article>.m-article-header__text { + .f-headline-editorial { + @include breakpoint('large+') { + font-size: 28px; + line-height: 32px; + } + } + } + + .o-article__body>.m-article-header__text { + .f-headline-editorial { + @include breakpoint('small-') { + font-size: 22px; + line-height: 28px; + } + @include breakpoint('medium') { + font-size: 28px; + line-height: 32px; + } + } + } + + .o-article__body .p--linked { + @include breakpoint('xsmall') { + padding-right: 0; + .p--linked__text { + width: 100%; + } + .p--linked__ref { + display: none; + } + } + @include breakpoint('small') { + padding-right: 0; + .p--linked__text { + width: colspan(52, 'small'); + } + .p--linked__ref { + display: none; + } + } + @include breakpoint('medium') { + .p--linked__text { + width: colspan(38, 'medium'); + } + } + @include breakpoint('large') { + .p--linked__text { + width: colspan(28, 'large'); + } + } + @include breakpoint('xlarge') { + .p--linked__text { + width: colspan(25, 'xlarge'); + } + } + } + + .o-article__primary-actions--digital-publication { + @include breakpoint('large') { + width: colspan(18, 'large'); + } + @include breakpoint('xlarge') { + width: colspan(18, 'xlarge'); + } } .o-article__secondary-actions { @@ -145,7 +311,7 @@ padding-top: 0; } - .m-article-header--journal-article { + .m-article-header--digital-publication-article { padding-top: 0; margin-bottom: 0 !important; // Bad @@ -170,7 +336,12 @@ .m-article-header__img { min-height: 240px; - height: 60vh; + height: 40vh; + right: 50%; + margin-right: -50vw; + left: auto; + margin-left: auto; + width: 100vw; img { width: 100%; @@ -179,6 +350,10 @@ } } + .m-article-header__img::before { + background-image: none; + } + .m-article-header__text { position: absolute; bottom: 0; @@ -217,13 +392,114 @@ margin-bottom: 50px; } - .m-media--s.o-blocks__block .m-media__img { - width: fit-content; + .o-layered-image-viewer { + .m-media.o-blocks__block { + .m-media__img { + max-height: unset !important; + width: 100% !important; + + img { + background-color: transparent; + background-image: none; + } + } + } + } + + .m-split-block { + .m-media--s.o-blocks__block, + .m-media--l.o-blocks__block { + width: unset !important; + + .m-media__img { + display: block; + width: unset !important; + height: auto; + margin: 0 auto; + max-width: 100% !important; + max-height: 80vh !important; + + img { + min-width: 100% + } + } + } + } + + .m-media--s.o-blocks__block, + .m-media--l.o-blocks__block { + &.m-media--layered-image-viewer-embed { + max-height: unset !important; + width: 100% !important; + .m-media__img { + width: 100% !important; + } + } + + .m-media__img.m-media--360-embed { + max-height: unset !important; + width: 100% !important; + } + + width: max-content !important; + max-width: 100%; + min-width: unset !important; + + .m-media__contain--spacer { + display: none !important; + } + + .m-media__img:not(.m-media__img--video) { + & { + width: max-content; + max-width: 100%; + max-height: 450px; + overflow: hidden !important; + text-align: left !important; + background-image: unset !important; + } + + img { + position: relative !important; + max-height: 450px !important; + width: 100% !important; + object-fit: contain !important; + display: block !important; + } + } + } + + .m-media--s.o-blocks__block { + .m-media__img { + img { + max-height: 225px !important; + } + } + } + + .m-media--s.o-blocks__block figcaption { + @include breakpoint('xsmall') { + padding-right: 0; + width: 100%; + } + @include breakpoint('small') { + padding-right: 0; + width: colspan(52, 'small'); + } + @include breakpoint('medium') { + width: colspan(38, 'medium'); + } + @include breakpoint('large') { + width: colspan(28, 'large'); + } + @include breakpoint('xlarge') { + width: colspan(25, 'xlarge'); + } } .m-media.o-blocks__block .m-media__img img { margin-left: 0; - max-height: 450px; + max-height: 100%; } .m-media--l, .m-media--m { @@ -251,6 +527,49 @@ margin-top: 8px; } } + + .o-accordion__trigger { + background-color: transparent; + } + + .m-split-block--quarter div { + &:first-child { + width: 25%; + flex-basis: 15%; + margin-top: 21px; + margin-bottom: 16px; + + .m-media .m-media__img img { + max-height: 180px !important; + } + } + + &:last-child { + & h2 { + margin-top: 0; + line-height: 21px; + } + } + + @include breakpoint('xsmall') { + &:first-child { + flex-basis: 25%; + } + + &:last-child { + width: 72%; + } + } + @include breakpoint('small+') { + &:first-child { + flex-basis: 25%; + } + + &:last-child { + width: 75%; + } + } + } } /*** @@ -262,8 +581,10 @@ Styling related specifically to digital publications ***/ .p-digitalpublicationarticle-show, .p-digitalpublicationarticle-preview { - .o-blocks { + .o-blocks { .author-links { + margin: 60px 0 28px; + a { color: $color__link--accent; } @@ -288,16 +609,37 @@ Styling related specifically to digital publications } } + .o-article__body.o-blocks>.o-gallery { + >.o-gallery__media-wrapper { + margin: 0 !important; + } + } + .o-article__body.o-blocks > ol, .o-article__body.o-blocks > ul { @extend .list; } .o-article__body.o-blocks { - & > h2, h3, h4 { + & > h2 { + font-family: $sans-serif-font--loaded; + font-size: 21px; + font-weight: 400; + letter-spacing: .08rem; + line-height: 28px; + text-transform: uppercase; + } + & > h3 { font-family: $serif-font--loaded; - font-size: 24px; - text-transform: none; + font-size: 22px; + font-weight: 400; + line-height: 28px; + } + & > h4 { + font-family: $sans-serif-font--loaded; + font-size: 18px; + font-weight: 500; + line-height: 28px; } } @@ -326,4 +668,59 @@ Styling related specifically to digital publications .o-blocks > h2:not([class*=f-]) { margin-top: 20px; } + + &:not(.is-sidebar-overlay) .o-article > .o-article__primary-actions--digital-publication { + @include breakpoint('xsmall') { + display: none; + } + } + + .o-pinboard--3-col\@xlarge::before { + display: none; + } + + @include breakpoint('large') { + .o-gallery--small-mosaic .o-pinboard--3-col\@large > * { + width: colspan(grid-cols-to-colspan(4, 52), 'large'); + } + } + + @include breakpoint('xlarge') { + .o-gallery--small-mosaic .o-pinboard--3-col\@xlarge > * { + width: colspan(grid-cols-to-colspan(4, 52), 'xlarge'); + } + } +} + +.p-digitalpublicationarticle-show:not(.p-t-contributions) { + .m-article-header { + display: none; + } + + .m-article-header .m-article-header__text { + display: none; + } + + .o-article__primary-actions--digital-publication { + .m-article-actions--publication__logo, + .m-article-actions--publication__logo::before { + background: transparent !important; + } + + .m-article-actions--publication__logo { + a { + color: $color__text--title; + } + } + + } +} + +.p-digitalpublicationarticle-show.p-t-contributions, +.p-digitalpublicationarticle-show.p-t-entry.is-sidebar-overlay { + .m-article-actions--publication__logo { + height: 150px; + margin-bottom: 0; + margin-top: 0; + } } diff --git a/frontend/scss/print.scss b/frontend/scss/print.scss index 8bfcb84dad..f926f98b76 100644 --- a/frontend/scss/print.scss +++ b/frontend/scss/print.scss @@ -533,7 +533,7 @@ html.s-print body { } .m-article-header--feature .m-article-header__text, - .m-article-header--journal-article .m-article-header__text { + .m-article-header--digital-publication-article .m-article-header__text { margin-top: 0.25in; margin-left: 0; min-height: auto; diff --git a/frontend/scss/setup/_colors.scss b/frontend/scss/setup/_colors.scss index 30ccd0f9e2..92759bd636 100755 --- a/frontend/scss/setup/_colors.scss +++ b/frontend/scss/setup/_colors.scss @@ -1,4 +1,5 @@ @import '../themes/rlc'; +@import '../themes/magazine'; // Main Color List - try not to use // Reds @@ -251,6 +252,10 @@ $color__link--limited: $color__orange; $color__link--limited-hover: $color__orange--hover; $color__link--limited-active: $color__orange--active; +$color__link--publications: $color__white; +$color__link--publications-hover: #dcddde; +$color__link--publications-active: #9d9fa2; + /*** diff --git a/frontend/scss/setup/mixins/_other.scss b/frontend/scss/setup/mixins/_other.scss index 110ba9107c..905982f3d5 100755 --- a/frontend/scss/setup/mixins/_other.scss +++ b/frontend/scss/setup/mixins/_other.scss @@ -120,18 +120,6 @@ Creates a load spinner. @return $string; } -// To fix collapsing margins -.clearfix { - &:before,&:after { - display:table; - content:""; - line-height:0; - } - &:after { - clear:both; - } -} - @mixin tucked-margin-top($font-obj, $distances, $single-bp: false) { $settings: false; $line-height: 0; diff --git a/frontend/scss/state/_s-contrast-text.scss b/frontend/scss/state/_s-contrast-text.scss new file mode 100644 index 0000000000..3569592509 --- /dev/null +++ b/frontend/scss/state/_s-contrast-text.scss @@ -0,0 +1,11 @@ +.contrast-text { + .s-light-background ~ * &, + .s-light-background & { + color: $color__black !important; + } + + .s-dark-background ~ * &, + .s-dark-background & { + color: $color__white !important; + } +} diff --git a/frontend/scss/state/_s-sticky-sidebar.scss b/frontend/scss/state/_s-sticky-sidebar.scss index 9eeb162751..b902585b7e 100644 --- a/frontend/scss/state/_s-sticky-sidebar.scss +++ b/frontend/scss/state/_s-sticky-sidebar.scss @@ -12,7 +12,15 @@ For _p-issuearticle-show.scss and _p-issue-show.scss display: none; } + .o-sticky-sidebar__sticker { + height: 100vh; + } + &.is-sidebar-overlay { + .m-article-actions--publication__logo a { + color: $color__text--title !important; + } + @include breakpoint('medium-') { .g-mask { right: 0; @@ -40,7 +48,6 @@ For _p-issuearticle-show.scss and _p-issue-show.scss left: 0; width: 300px; - height: 100vh; overflow-y: auto; // Gutters are always about the same as on xsmall @@ -82,6 +89,16 @@ For _p-issuearticle-show.scss and _p-issue-show.scss width: 100vw; } } + @include breakpoint('small') { + .o-sticky-sidebar__sticker { + width: calc(colspan(19, 'small') + map-get($outer-gutters, 'small')); + } + } + @include breakpoint('medium') { + .o-sticky-sidebar__sticker { + width: calc(colspan(19, 'medium') + map-get($outer-gutters, 'medium')); + } + } } @include breakpoint('large+') { @@ -96,20 +113,35 @@ For _p-issuearticle-show.scss and _p-issue-show.scss &.is-sidebar-grabbed, &.is-sidebar-fixed, &.is-sidebar-bottom { - .o-sticky-sidebar__sticker { - - &[data-sticky-animated-logo] { + .o-article__primary-actions { + .o-sticky-sidebar__sticker { .m-article-actions--publication__logo { display: block; + align-items: normal; + height: auto; + margin-top: 30px; + } + + @each $name in ('large', 'xlarge') { + @include breakpoint('#{$name}') { + width: #{colspan(19, '#{$name}')}; + padding-right: #{colspan(1, '#{$name}')}; + } + } + + .m-article-actions--publication__logo, + .m-article-actions--publication__logo::before { + background-color: transparent; } - } - @each $name in ('large', 'xlarge') { - @include breakpoint('#{$name}') { - width: #{colspan(14, '#{$name}')}; - padding-right: #{colspan(1, '#{$name}')}; + .m-article-actions--publication__logo a { + color: $color__text--title; } } + + .m-article-header--digital-publication-article ~ .m-article-header__text::before { + background-color: transparent; + } } } @@ -122,7 +154,6 @@ For _p-issuearticle-show.scss and _p-issue-show.scss } } - height: 100vh; overflow-y: auto; } } diff --git a/frontend/scss/themes/_magazine.scss b/frontend/scss/themes/_magazine.scss new file mode 100644 index 0000000000..5b9a4955fc --- /dev/null +++ b/frontend/scss/themes/_magazine.scss @@ -0,0 +1,4 @@ +// Magazine Issue color list +// Color names are taken from https://www.color-name.com/. + +$color__magazine__byzantium: #6F2562; diff --git a/package-lock.json b/package-lock.json index d998edcc75..c99e383a9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8575,8 +8575,8 @@ } }, "my-museum-tour-builder": { - "version": "git+https://github.com/art-institute-of-chicago/my-museum-tour.git#cb9560febc9e0eadea02decb690b5a5dd2c2cb0e", - "from": "git+https://github.com/art-institute-of-chicago/my-museum-tour.git#semver:^1.15", + "version": "git+https://github.com/art-institute-of-chicago/my-museum-tour.git#bef1cdbb2db2c65b912cfb6a93290935864c47bf", + "from": "git+https://github.com/art-institute-of-chicago/my-museum-tour.git#semver:^1.0.0", "requires": { "@area17/a17-helpers": "^0.6.7", "classnames": "^2.3.2", @@ -11962,6 +11962,11 @@ } } }, + "text-contrast": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/text-contrast/-/text-contrast-1.0.2.tgz", + "integrity": "sha512-tTWdDxZavzt7sPaKG7bR4RfXmyaWJodNhg1vzQVxzpXfmGSa4DIG9zYJ1PYuUObyae7ErSrPiUA8hRlwafmbow==" + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", diff --git a/package.json b/package.json index d2b70c3060..3850b129ce 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "lodash": "^4.17.15", "minimist": "^1.2.0", "mirador": "^3.0.0", - "my-museum-tour-builder": "git+https://github.com/art-institute-of-chicago/my-museum-tour.git#semver:^1.15", + "my-museum-tour-builder": "git+https://github.com/art-institute-of-chicago/my-museum-tour.git#semver:^1.0.0", "prettier": "^1.7.4", "react": "^16.9.0", "react-app-polyfill": "^0.2.0", @@ -48,6 +48,7 @@ "read-pkg-up": "^3.0.0", "readline-sync": "^1.4.10", "sass": "^1.42.1", + "text-contrast": "^1.0.2", "video.js": "^7.14.3", "videojs-event-tracking": "^1.0.2", "webpack": "^4.39.3", diff --git a/resources/assets/js/components/ColorSelect.vue b/resources/assets/js/components/ColorSelect.vue index 013bdd8691..b3df924a47 100644 --- a/resources/assets/js/components/ColorSelect.vue +++ b/resources/assets/js/components/ColorSelect.vue @@ -11,8 +11,10 @@ > @@ -297,10 +299,12 @@ margin-top: -9px; } } + /* New code added [PUB-192] */ .singleselector__color-label { border: 10px solid lightgrey; float: right; height: 52px; width: 52px; } + /* /New code added [PUB-192] */ diff --git a/resources/views/admin/blocks/360_embed.blade.php b/resources/views/admin/blocks/360_embed.blade.php index 89189c8ba6..d77a4f26a8 100644 --- a/resources/views/admin/blocks/360_embed.blade.php +++ b/resources/views/admin/blocks/360_embed.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('360 Embed') @twillBlockIcon('image') @@ -5,7 +10,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 's', + 'default' => ($type === 'digitalPublications' ? 'l' : 's'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', diff --git a/resources/views/admin/blocks/artwork.blade.php b/resources/views/admin/blocks/artwork.blade.php index 9af34f1d64..db0452c87c 100644 --- a/resources/views/admin/blocks/artwork.blade.php +++ b/resources/views/admin/blocks/artwork.blade.php @@ -18,7 +18,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 'm', + 'default' => ($type === 'digitalPublications' ? 'l' : 'm'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', diff --git a/resources/views/admin/blocks/image.blade.php b/resources/views/admin/blocks/image.blade.php index 08c2b496ad..d852986abc 100644 --- a/resources/views/admin/blocks/image.blade.php +++ b/resources/views/admin/blocks/image.blade.php @@ -1,6 +1,23 @@ @php $currentUrl = explode('/', request()->url()); $type = $currentUrl[5] ?? null; + $options = []; + $options[] = [ + 'value' => 's', + 'label' => 'Small' + ]; + + if ($type !== 'digitalPublications') { + $options[] = [ + 'value' => 'm', + 'label' => 'Medium' + ]; + } + + $options[] = [ + 'value' => 'l', + 'label' => 'Large' + ]; @endphp @twillBlockTitle('Image') @@ -18,31 +35,22 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 'm', - 'options' => [ - [ - 'value' => 's', - 'label' => 'Small' - ], - [ - 'value' => 'm', - 'label' => 'Medium' - ], - [ - 'value' => 'l', - 'label' => 'Large' - ] - ] + 'default' => ($type === 'digitalPublications' ? 'l' : 'm'), + 'options' => $options, ]) @formField('checkbox', [ 'name' => 'use_contain', 'label' => 'Always show the whole image instead of cropping to the container', + 'default' => ($type === 'digitalPublications' ? true : false), + 'disabled' => ($type === 'digitalPublications' ? true : false), ]) @formField('checkbox', [ 'name' => 'use_alt_background', 'label' => 'Use white instead of gray to pillarbox the image', + 'default' => ($type === 'digitalPublications' ? true : false), + 'disabled' => ($type === 'digitalPublications' ? true : false), ]) @formField('checkbox', [ diff --git a/resources/views/admin/blocks/image_slider.blade.php b/resources/views/admin/blocks/image_slider.blade.php index 01097a4300..2c5860be35 100644 --- a/resources/views/admin/blocks/image_slider.blade.php +++ b/resources/views/admin/blocks/image_slider.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Image Slider') @twillBlockIcon('image') @@ -10,7 +15,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 'm', + 'default' => ($type === 'digitalPublications' ? 'l' : 'm'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', diff --git a/resources/views/admin/blocks/layered_image_viewer.blade.php b/resources/views/admin/blocks/layered_image_viewer.blade.php index 90d5d381e4..2cc37ddf8a 100644 --- a/resources/views/admin/blocks/layered_image_viewer.blade.php +++ b/resources/views/admin/blocks/layered_image_viewer.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Layered Image Viewer') @twillBlockIcon('image') @@ -23,7 +28,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 'm', + 'default' => ($type === 'digitalPublications' ? 'l' : 'm'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', diff --git a/resources/views/admin/blocks/media_embed.blade.php b/resources/views/admin/blocks/media_embed.blade.php index 376a0096d5..c965e749ab 100644 --- a/resources/views/admin/blocks/media_embed.blade.php +++ b/resources/views/admin/blocks/media_embed.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Media embed') @twillBlockIcon('text') @@ -5,7 +10,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 's', + 'default' => ($type === 'digitalPublications' ? 'l' : 's'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', diff --git a/resources/views/admin/blocks/mirador_embed.blade.php b/resources/views/admin/blocks/mirador_embed.blade.php index ce54de0346..128c613566 100644 --- a/resources/views/admin/blocks/mirador_embed.blade.php +++ b/resources/views/admin/blocks/mirador_embed.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Mirador Embed') @twillBlockIcon('image') @@ -18,7 +23,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 'm', + 'default' => ($type === 'digitalPublications' ? 'l' : 'm'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', diff --git a/resources/views/admin/blocks/table.blade.php b/resources/views/admin/blocks/table.blade.php index ec28443dc7..ae1693994d 100644 --- a/resources/views/admin/blocks/table.blade.php +++ b/resources/views/admin/blocks/table.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Table') @twillBlockIcon('text') @@ -9,7 +14,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 's', + 'default' => ($type === 'digitalPublications' ? 'l' : 's'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', @@ -62,4 +68,3 @@ 'name' => 'hide_columns', 'label' => 'Hide vertical cell borders', ]) - diff --git a/resources/views/admin/blocks/tombstone.blade.php b/resources/views/admin/blocks/tombstone.blade.php index a2767755fa..0cf38edcdf 100644 --- a/resources/views/admin/blocks/tombstone.blade.php +++ b/resources/views/admin/blocks/tombstone.blade.php @@ -12,6 +12,6 @@ 'label' => 'Text', 'placeholder' => 'Text', 'toolbarOptions' => [ - 'italic', + 'bold', 'italic' ], ]) diff --git a/resources/views/admin/blocks/video.blade.php b/resources/views/admin/blocks/video.blade.php index e6d97df6af..841005654e 100644 --- a/resources/views/admin/blocks/video.blade.php +++ b/resources/views/admin/blocks/video.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Video') @twillBlockIcon('image') @@ -5,7 +10,8 @@ 'name' => 'size', 'label' => 'Size', 'placeholder' => 'Select size', - 'default' => 'm', + 'default' => ($type === 'digitalPublications' ? 'l' : 'm'), + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 's', @@ -82,7 +88,8 @@ @formField('input', [ 'name' => 'url', - 'label' => 'Video URL' + 'label' => 'Video URL', + 'type' => 'url' ]) @endcomponent diff --git a/resources/views/admin/blocks/vtour_embed.blade.php b/resources/views/admin/blocks/vtour_embed.blade.php index 3a4da70aa1..1ef3988d5b 100644 --- a/resources/views/admin/blocks/vtour_embed.blade.php +++ b/resources/views/admin/blocks/vtour_embed.blade.php @@ -1,3 +1,8 @@ +@php + $currentUrl = explode('/', request()->url()); + $type = $currentUrl[5] ?? null; +@endphp + @twillBlockTitle('Virtual Tour Embed') @twillBlockIcon('image') @@ -12,6 +17,7 @@ 'label' => 'Size', 'placeholder' => 'Select size', 'default' => 'l', + 'disabled' => ($type === 'digitalPublications' ? true : false), 'options' => [ [ 'value' => 'm', diff --git a/resources/views/admin/digitalPublications/articles/create.blade.php b/resources/views/admin/digitalPublications/articles/create.blade.php deleted file mode 100644 index 358520d1c9..0000000000 --- a/resources/views/admin/digitalPublications/articles/create.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -@include('twill::partials.create') - -@formField('select', [ - 'name' => 'type', - 'label' => 'Type', - 'placeholder' => 'Select a type', - 'options' => $types, -]) diff --git a/resources/views/admin/digitalPublications/articles/form.blade.php b/resources/views/admin/digitalPublications/articles/form.blade.php index 0ef0611066..98c96b7136 100644 --- a/resources/views/admin/digitalPublications/articles/form.blade.php +++ b/resources/views/admin/digitalPublications/articles/form.blade.php @@ -15,46 +15,13 @@ 'note' => 'Required', ]) - @formField('medias', [ - 'with_multiple' => false, - 'no_crop' => false, - 'label' => 'Hero image', - 'name' => 'hero', - 'note' => 'Minimum image width 3000px' - ]) - - @formField('medias', [ - 'with_multiple' => false, - 'no_crop' => false, - 'label' => 'Mobile hero image', - 'name' => 'mobile_hero', - 'note' => 'Minimum image width 2000px' - ]) - @formField('select', [ - 'name' => 'type', + 'name' => 'article_type', 'label' => 'Type', 'placeholder' => 'Select a type', 'default' => 'text', 'options' => $types, ]) - - @component('twill::partials.form.utils._columns') - @slot('left') - @formField('checkbox', [ - 'name' => 'hide_title', - 'label' => 'Hide title in listing view', - ]) - @endslot - - @slot('right') - @formField('checkbox', [ - 'name' => 'suppress_listing', - 'label' => 'Hide from listing view', - ]) - @endslot - @endcomponent - @formField('select', [ 'name' => 'listing_display', 'label' => 'Listing display', @@ -70,96 +37,32 @@ ], ]) - @formField('input', [ - 'name' => 'label', - 'label' => 'Article label', - 'note' => 'Used in the "eyebrow" of cards on the publication page', - ]) - - @formField('wysiwyg', [ - 'name' => 'list_description', - 'label' => 'List description', - 'maxlength' => 255, - 'note' => 'Max 255 characters. Will be used on the main landing, search, and social media.', - 'toolbarOptions' => [ - 'italic', - ], - ]) - - @formField('input', [ - 'name' => 'author_display', - 'label' => 'Author display', - ]) - - @formField('browser', [ - 'routePrefix' => 'collection', - 'moduleName' => 'authors', - 'name' => 'authors', - 'label' => 'Authors', - 'max' => 10 - ]) - - @formField('wysiwyg', [ - 'name' => 'cite_as', - 'label' => 'How to Cite', - 'toolbarOptions' => [ - 'italic', - ], - ]) - - @formField('wysiwyg', [ - 'name' => 'references', - 'label' => 'References', - 'toolbarOptions' => [ - 'italic', 'link', 'list-ordered', 'list-unordered', - ], - ]) + @component('twill::partials.form.utils._columns') + @slot('left') + @formField('checkbox', [ + 'name' => 'hide_title', + 'label' => 'Hide title in listing view', + ]) + @endslot - @formField('block_editor', [ - 'blocks' => BlockHelpers::getBlocksForEditor([ - '360_embed', - '360_modal', - '3d_embed', - '3d_model', - '3d_tour', - 'ranged_accordion', - 'artwork', - 'audio_player', - 'button', - 'citation', - 'digital_label', - 'gallery_new', - 'hr', - 'image', - 'image_slider', - 'layered_image_viewer', - 'links-bar', - 'list', - 'media_embed', - 'membership_banner', - 'mirador_embed', - 'mirador_modal', - 'mobile_app', - 'paragraph', - 'quote', - 'split_block', - 'table', - 'tombstone', - 'tour_stop', - 'video', - ]) - ]) + @slot('right') + @formField('checkbox', [ + 'name' => 'suppress_listing', + 'label' => 'Hide from listing view', + ]) + @endslot + @endcomponent @stop @section('fieldsets') - @formConnectedFields([ - 'fieldName' => 'type', - 'fieldValues' => 'grouping', - 'renderForBlocks' => false, + @formFieldset([ + 'id' => 'editorial-content', + 'title' => 'Editorial Content', ]) - @formFieldset([ - 'id' => 'fields-for-type-grouping', - 'title' => 'Grouping fields', + @formConnectedFields([ + 'fieldName' => 'article_type', + 'fieldValues' => 'grouping', + 'renderForBlocks' => false, ]) @formField('wysiwyg', [ 'name' => 'grouping_description', @@ -174,7 +77,7 @@ @formField('medias', [ 'with_multiple' => false, 'no_crop' => false, - 'label' => 'Hero image', + 'label' => 'Grouping image', 'name' => 'grouping_hero', 'note' => 'Minimum image width 3000px' ]) @@ -182,12 +85,158 @@ @formField('medias', [ 'with_multiple' => false, 'no_crop' => false, - 'label' => 'Mobile hero image', + 'label' => 'Mobile grouping image', 'name' => 'grouping_mobile_hero', 'note' => 'Minimum image width 2000px' ]) - @endformFieldset - @endformConnectedFields + @endformConnectedFields + + @formConnectedFields([ + 'fieldName' => 'article_type', + 'fieldValues' => 'entry', + 'renderForBlocks' => false, + ]) + @formField('medias', [ + 'with_multiple' => false, + 'no_crop' => false, + 'label' => 'Listing image', + 'name' => 'hero', + 'note' => 'Minimum image width 3000px' + ]) + + @formField('medias', [ + 'with_multiple' => false, + 'no_crop' => false, + 'label' => 'Mobile listing image', + 'name' => 'mobile_hero', + 'note' => 'Minimum image width 2000px' + ]) + + @formField('input', [ + 'name' => 'author_display', + 'label' => 'Author display', + 'note' => 'On Entry type articles, authorship is prepended with "Entry by"', + ]) + + @formField('browser', [ + 'routePrefix' => 'collection', + 'moduleName' => 'authors', + 'name' => 'authors', + 'label' => 'Authors', + 'max' => 10, + 'note' => 'On Entry type articles, authorship is prepended with "Entry by"', + ]) + @endformConnectedFields + + @formConnectedFields([ + 'fieldName' => 'article_type', + 'fieldValues' => ['about', 'text', 'work'], + 'renderForBlocks' => false, + ]) + @formField('medias', [ + 'with_multiple' => false, + 'no_crop' => false, + 'label' => 'Hero image', + 'name' => 'hero', + 'note' => 'Minimum image width 3000px' + ]) + + @formField('medias', [ + 'with_multiple' => false, + 'no_crop' => false, + 'label' => 'Mobile hero image', + 'name' => 'mobile_hero', + 'note' => 'Minimum image width 2000px' + ]) + + @formField('input', [ + 'name' => 'author_display', + 'label' => 'Author display', + ]) + + @formField('browser', [ + 'routePrefix' => 'collection', + 'moduleName' => 'authors', + 'name' => 'authors', + 'label' => 'Authors', + 'max' => 10, + ]) + @endformConnectedFields + + @formConnectedFields([ + 'fieldName' => 'article_type', + 'fieldValues' => 'grouping', + 'isEqual' => false, + 'renderForBlocks' => false, + ]) + @formField('input', [ + 'name' => 'label', + 'label' => 'Article label', + 'note' => 'Used in the "eyebrow" of cards on the publication page', + ]) + + @formField('wysiwyg', [ + 'name' => 'list_description', + 'label' => 'List description', + 'maxlength' => 255, + 'note' => 'Max 255 characters. Will be used on the main landing, search, and social media.', + 'toolbarOptions' => [ + 'italic', + ], + ]) + + @formField('wysiwyg', [ + 'name' => 'cite_as', + 'label' => 'How to Cite', + 'toolbarOptions' => [ + 'italic', + ], + ]) + + @formField('wysiwyg', [ + 'name' => 'references', + 'label' => 'References', + 'toolbarOptions' => [ + 'italic', 'link', 'list-ordered', 'list-unordered', + ], + ]) + + @formField('block_editor', [ + 'blocks' => BlockHelpers::getBlocksForEditor([ + '360_embed', + '360_modal', + '3d_embed', + '3d_model', + '3d_tour', + 'ranged_accordion', + 'artwork', + 'audio_player', + 'button', + 'citation', + 'digital_label', + 'gallery_new', + 'hr', + 'image', + 'image_slider', + 'layered_image_viewer', + 'links-bar', + 'list', + 'media_embed', + 'membership_banner', + 'mirador_embed', + 'mirador_modal', + 'mobile_app', + 'paragraph', + 'quote', + 'split_block', + 'table', + 'tombstone', + 'tour_stop', + 'video', + ]) + ]) + @endformConnectedFields + @endformFieldset @include('admin.partials.meta') @stop diff --git a/resources/views/admin/digitalPublications/form.blade.php b/resources/views/admin/digitalPublications/form.blade.php index cf04b60bc8..9d3301503a 100644 --- a/resources/views/admin/digitalPublications/form.blade.php +++ b/resources/views/admin/digitalPublications/form.blade.php @@ -16,18 +16,9 @@ - @formField('wysiwyg', [ - 'name' => 'header_title_display', - 'label' => 'Title lockup for header', - 'note' => 'Use Shift+Enter to add linebreak instead of starting a new paragraph', - 'toolbarOptions' => [ - 'italic', - ], - ]) - @formField('wysiwyg', [ 'name' => 'header_subtitle_display', - 'label' => 'Subtitle lockup for header', + 'label' => 'Subtitle for header', 'toolbarOptions' => [ 'italic', ], @@ -51,6 +42,7 @@ 'name' => 'bgcolor', 'label' => 'Hero background color', 'options' => $heroBackgroundColors, + 'columns' => 3, ]) @formField('wysiwyg', [ diff --git a/resources/views/admin/exhibitions/form.blade.php b/resources/views/admin/exhibitions/form.blade.php index 3e13be24db..35d38848e9 100644 --- a/resources/views/admin/exhibitions/form.blade.php +++ b/resources/views/admin/exhibitions/form.blade.php @@ -104,13 +104,20 @@ 'note' => 'Override CITI gallery location' ]) - @formField('select', [ + @formField('select', [ 'name' => 'status_override', 'label' => 'Exhibition status', 'note' => 'Override exhibition status flag', 'options' => $exhibitionStatusesList, ]) + @formField('input', [ + 'name' => 'type_override', + 'label' => 'Exhibition eyebrow', + 'note' => 'Override exhibition eyebrow', + 'type' => 'text', + ]) + @formField('block_editor', [ 'blocks' => BlockHelpers::getBlocksForEditor([ 'paragraph', 'image', 'hr', 'artwork', 'split_block', 'gallery_new', 'link', 'video', 'quote', 'tour_stop', 'accordion', 'media_embed', 'list', 'timeline', 'button', 'newsletter_signup_inline', 'audio_player', '360_embed', 'vtour_embed', 'mirador_embed', 'event', 'feature_2x', 'layered_image_viewer', '3d_model', 'feature_4x', 'mobile_app', 'mirador_modal', '360_modal' diff --git a/resources/views/admin/experiences/slides/_interstitial.blade.php b/resources/views/admin/experiences/slides/_interstitial.blade.php index a51a507b02..49bce8ca8d 100644 --- a/resources/views/admin/experiences/slides/_interstitial.blade.php +++ b/resources/views/admin/experiences/slides/_interstitial.blade.php @@ -4,8 +4,8 @@ 'keepAlive' => true, ]) @formField('wysiwyg', [ - 'name' => 'section_title', - 'label' => 'Section Title', + 'name' => 'article_title', + 'label' => 'Article Title', 'maxlength' => 150, ]) diff --git a/resources/views/admin/genericPages/form.blade.php b/resources/views/admin/genericPages/form.blade.php index eacae31e00..f81e7f035a 100644 --- a/resources/views/admin/genericPages/form.blade.php +++ b/resources/views/admin/genericPages/form.blade.php @@ -91,6 +91,17 @@ @slot('moduleName', 'genericPages') @endcomponent + + @formField('browser', [ + 'routePrefix' => 'exhibitions_events', + 'moduleName' => 'sponsors', + 'name' => 'sponsors', + 'label' => 'Sponsors', + 'note' => 'Display content blocks from this sponsor', + 'max' => 1 + ]) + + {{-- WEB-2236: Use 'admin.partials.meta' as a component --}} @formField('input', [ diff --git a/resources/views/admin/partials/featured-related.blade.php b/resources/views/admin/partials/featured-related.blade.php index 6e766f0170..2369e005c6 100644 --- a/resources/views/admin/partials/featured-related.blade.php +++ b/resources/views/admin/partials/featured-related.blade.php @@ -30,6 +30,10 @@ 'label' => 'Digital Publication', 'value' => moduleRoute('digitalPublications', 'collection.articles_publications', 'browser'), ], + [ + 'label' => 'Digital Publication Article', + 'value' => moduleRoute('digitalPublications.articles', 'collection.articles_publications', 'browser'), + ], [ 'label' => 'Video', 'value' => moduleRoute('videos', 'collection.articles_publications', 'browser'), @@ -57,7 +61,7 @@
    @foreach($autoRelated as $related)
  1. - {!! Str::title($related->type) . (Str::title($related->type) ? ":" : "") !!} {{ $related->title }} + {!! Str::title($related->present()->articleType) . (Str::title($related->present()->articleType) ? ":" : "") !!} {{ $related->title }}
  2. @endforeach
diff --git a/resources/views/components/molecules/_m-article-actions----digital-publication.blade.php b/resources/views/components/molecules/_m-article-actions----digital-publication.blade.php index e72f206cf7..ecd8bd47cb 100644 --- a/resources/views/components/molecules/_m-article-actions----digital-publication.blade.php +++ b/resources/views/components/molecules/_m-article-actions----digital-publication.blade.php @@ -4,9 +4,9 @@ -
diff --git a/resources/views/components/molecules/_m-article-actions----magazine-issue.blade.php b/resources/views/components/molecules/_m-article-actions----magazine-issue.blade.php index 3e0f77a856..26de10a32e 100644 --- a/resources/views/components/molecules/_m-article-actions----magazine-issue.blade.php +++ b/resources/views/components/molecules/_m-article-actions----magazine-issue.blade.php @@ -26,39 +26,16 @@ - @if (isset($issues) && $issues->count() > 1) + @if (isset($issues))
- -
    - @foreach($issues as $issue) -
  • - @component('components.atoms._tag') - @slot('href', route('magazine-issues.show', [ - 'id' => $issue->id, - 'slug' => $issue->getSlug(), - ])) - @slot('variation', 'tag--magazine tag--senary tag--w-image') - @slot('gtmAttributes', 'data-gtm-event="' . StringHelpers::getUtf8Slug( $issue->title ) . '" data-gtm-event-category="magazine-sidebar-issue"') - @if (!empty($issue->imageFront('hero', 'default'))) - @component('components.atoms._img') - @slot('image', $issue->imageFront('hero', 'default')) - @slot('settings', array( - 'fit' => 'crop', - 'ratio' => '1:1', - 'srcset' => array(30,60), - 'sizes' => '60px', - )) - @endcomponent - @endif - {!! $issue->present()->title !!} - @endcomponent -
  • - @endforeach -
+ @component('components.organisms._o-accordion-tree') + @slot('variation', 'o-accordion--magazine-issue-archive') + @slot('titleFont', 'f-tag-2') + @slot('items', $issues) + @slot('active', true) + @endcomponent @endif -
-
diff --git a/resources/views/components/molecules/_m-article-header----journal-article.blade.php b/resources/views/components/molecules/_m-article-header----digital-publication-article.blade.php similarity index 53% rename from resources/views/components/molecules/_m-article-header----journal-article.blade.php rename to resources/views/components/molecules/_m-article-header----digital-publication-article.blade.php index eb62c491bc..e1eba451f7 100644 --- a/resources/views/components/molecules/_m-article-header----journal-article.blade.php +++ b/resources/views/components/molecules/_m-article-header----digital-publication-article.blade.php @@ -1,4 +1,17 @@ -
+@if ($bgcolor ?? false) + +@endif + +
@if ($img) @component('components.atoms._img') @@ -19,21 +32,7 @@ @endcomponent @endif
-
- @if (isset($title)) - @component('components.atoms._title') - @slot('tag', 'h1') - @slot('font', 'f-headline-editorial') - @slot('itemprop', 'name') - @slot('title', $title) - @slot('title_display', $title_display ?? null) - @endcomponent - @endif - @if (!empty($credit)) - @component('components.molecules._m-info-trigger') - @slot('isInverted', true) - @slot('creditText', $credit) - @endcomponent - @endif +
diff --git a/resources/views/components/molecules/_m-article-header----feature.blade.php b/resources/views/components/molecules/_m-article-header----feature.blade.php index f4d38c245f..bd3b1e5cfe 100644 --- a/resources/views/components/molecules/_m-article-header----feature.blade.php +++ b/resources/views/components/molecules/_m-article-header----feature.blade.php @@ -1,11 +1,17 @@ @if ($bgcolor ?? false) @endif -<{{ $tag ?? 'header' }} class="m-article-header m-article-header--feature{{ (isset($variation)) ? ' '.$variation : '' }}" data-behavior="blurMyBackground"> +<{{ $tag ?? 'header' }} + class="m-article-header m-article-header--feature{{ (isset($variation)) ? ' '.$variation : '' }}" + data-behavior="blurMyBackground stickyDigitalPublicationHeader contrastText" + data-background-color="{{ isset($bgcolor) ? $bgcolor : null }}" +>
@if ($img) @component('components.atoms._img') @@ -28,15 +34,22 @@
@if (isset($title)) - @component('components.atoms._title') - @slot('tag','h1') - @slot('font', (isset($editorial) && $editorial) ? 'f-headline-editorial' : 'f-headline') - @slot('itemprop','name') - @slot('title', $title) - @slot('title_display', $title_display ?? null) - @endcomponent + @if (isset($title_href)) + + @endif + @component('components.atoms._title') + @slot('tag', 'h1') + @slot('font', (isset($editorial) && $editorial) ? 'f-headline-editorial' : 'f-headline') + @slot('itemprop','name') + @slot('title', $title) + @slot('title_display', $title_display ?? null) + @slot('variation', 'contrast-text') + @endcomponent + @if (isset($title_href)) + + @endif @if (isset($subtitle_display)) -

+

{!! $subtitle_display !!}

@endif @@ -57,10 +70,10 @@ @slot('date', $date ?? null) @endcomponent - @if (isset($type)) + @if (isset($type) || isset($type_override)) @component('components.atoms._type') @slot('tag','p') - {{ $type }} + {{ $type_override ?? $type }} @endcomponent @endif @@ -83,3 +96,7 @@ @endif @endif + +@if (($variation ?? '') == 'm-article-header--digital-publication') +
+@endif diff --git a/resources/views/components/molecules/_m-digipub-title-bar.blade.php b/resources/views/components/molecules/_m-digipub-title-bar.blade.php index c0149d8099..08ee2feb9e 100644 --- a/resources/views/components/molecules/_m-digipub-title-bar.blade.php +++ b/resources/views/components/molecules/_m-digipub-title-bar.blade.php @@ -6,7 +6,7 @@ @if ($item->children && count($item->children) >= 1)

{!! $item->present()->title !!}

- {{ 'View all ' . $item->present()->type }} + {{ 'View all ' . $item->present()->articleType }}
diff --git a/resources/views/components/molecules/_m-listing----auto-related.blade.php b/resources/views/components/molecules/_m-listing----auto-related.blade.php index 7f54919376..368037e82d 100644 --- a/resources/views/components/molecules/_m-listing----auto-related.blade.php +++ b/resources/views/components/molecules/_m-listing----auto-related.blade.php @@ -59,7 +59,7 @@ @endif
-1) ? ' data-blur-clip-to' : '' }}> - {!! $item->subtype ? $item->present()->subtype : $item->type !!} + {!! $item->present()->subtype ?? $item->present()->type !!} @if ($item->exclusive) @component('components.atoms._type') @slot('variation', 'type--membership') diff --git a/resources/views/components/molecules/_m-listing----digital-publication-article-entry.blade.php b/resources/views/components/molecules/_m-listing----digital-publication-article-entry.blade.php index dc50d500aa..e7ac155f44 100644 --- a/resources/views/components/molecules/_m-listing----digital-publication-article-entry.blade.php +++ b/resources/views/components/molecules/_m-listing----digital-publication-article-entry.blade.php @@ -1,5 +1,5 @@ <{{ $tag ?? 'li' }} class="m-listing m-listing--digital-publication-article-entry m-listing--w-meta-bottom{{ (isset($variation)) ? ' '.$variation : '' }}"> - + @if ($image) @component('components.atoms._img') diff --git a/resources/views/components/molecules/_m-listing----digital-publication-article.blade.php b/resources/views/components/molecules/_m-listing----digital-publication-article.blade.php index 5f5b85331a..f902c92e67 100644 --- a/resources/views/components/molecules/_m-listing----digital-publication-article.blade.php +++ b/resources/views/components/molecules/_m-listing----digital-publication-article.blade.php @@ -44,7 +44,7 @@ @component('components.atoms._link') @slot('font', 'f-secondary') @slot('href', $href) - Read full {{isset($type) ? Str::singular(Str::lower($type)) : 'article'}} + Read more @endcomponent diff --git a/resources/views/components/molecules/_m-showcase.blade.php b/resources/views/components/molecules/_m-showcase.blade.php index aca962ca88..ec70fc0bd9 100644 --- a/resources/views/components/molecules/_m-showcase.blade.php +++ b/resources/views/components/molecules/_m-showcase.blade.php @@ -1,11 +1,13 @@
@if ($tag) @@ -17,23 +19,27 @@ @endcomponent @endif @if ($title) + @component('components.atoms._title') @slot('tag', 'div') @slot('font', 'f-headline-editorial') @slot('variation', 'showcase-title') @slot('title', $title) @endcomponent + @endif @if ($author_display) - by {{ $author_display }} + by {{ $author_display }} @endif @if ($description) - @component('components.blocks._text') - @slot('tag', 'div') - @slot('font', 'f-secondary') - @slot('variation', 'showcase-description') - {!! SmartyPants::defaultTransform($description) !!} - @endcomponent + + @component('components.blocks._text') + @slot('tag', 'div') + @slot('font', 'f-secondary') + @slot('variation', 'showcase-description') + {!! SmartyPants::defaultTransform($description) !!} + @endcomponent + @endif @if ($linkLabel || $linkUrl) @component('components.atoms._link') diff --git a/resources/views/components/organisms/_o-accordion-tree.blade.php b/resources/views/components/organisms/_o-accordion-tree.blade.php new file mode 100644 index 0000000000..435058466b --- /dev/null +++ b/resources/views/components/organisms/_o-accordion-tree.blade.php @@ -0,0 +1,65 @@ +{{-- + Expects $items to be in the format: + [ + [ + 'title' => 'Level 1 title', + 'items' => [ + [ + 'title' => 'Level 2 title', + 'items' => [ + [ + 'title' => 'Level 3 item', + 'url' => 'https://some.link', + ], + [ + 'title' => 'Another level 3 item', + 'url' => 'https://some-other.link', + ] + ] + ], + ], + ] + ] +--}} +
+ @foreach ($items as $item) + @if (isset($item['items']) && count($item['items']) > 0) +

+ +

+
+ @component('components.organisms._o-accordion-tree') + @slot('variation', $variation) + @slot('titleFont', $titleFont) + @slot('title', $item['title']) + @slot('items', $item['items']) + @endcomponent +
+ @else + + + {!! $item['title'] !!} + + + @endif + @endforeach +
diff --git a/resources/views/components/organisms/_o-editors-note----publication.blade.php b/resources/views/components/organisms/_o-editors-note----publication.blade.php index d1a9aa02af..cc523ff118 100644 --- a/resources/views/components/organisms/_o-editors-note----publication.blade.php +++ b/resources/views/components/organisms/_o-editors-note----publication.blade.php @@ -1,11 +1,10 @@
@component('components.atoms._link') @slot('font', '') - @slot('href', $articleLink)
- {!! $description !!} + {!! $description !!}
@endcomponent diff --git a/resources/views/components/organisms/_o-gallery----slider.blade.php b/resources/views/components/organisms/_o-gallery----slider.blade.php index 296bbda947..0075198597 100644 --- a/resources/views/components/organisms/_o-gallery----slider.blade.php +++ b/resources/views/components/organisms/_o-gallery----slider.blade.php @@ -1,5 +1,4 @@