diff --git a/composer.lock b/composer.lock index 50872287..8bdd86ba 100644 --- a/composer.lock +++ b/composer.lock @@ -2296,17 +2296,17 @@ }, { "name": "drupal/admin_toolbar", - "version": "3.5.0", + "version": "3.5.1", "source": { "type": "git", "url": "https://git.drupalcode.org/project/admin_toolbar.git", - "reference": "3.5.0" + "reference": "3.5.1" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/admin_toolbar-3.5.0.zip", - "reference": "3.5.0", - "shasum": "099e8d4dc98e1d551b4f9cffdc39599eb8ad04e8" + "url": "https://ftp.drupal.org/files/projects/admin_toolbar-3.5.1.zip", + "reference": "3.5.1", + "shasum": "b5215109836f7fade374fef531231e36c1c9b945" }, "require": { "drupal/core": "^9.5 || ^10 || ^11" @@ -2317,8 +2317,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "3.5.0", - "datestamp": "1722639094", + "version": "3.5.1", + "datestamp": "1730409973", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -12910,20 +12910,20 @@ }, { "name": "ezyang/htmlpurifier", - "version": "v4.17.0", + "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c" + "reference": "cb56001e54359df7ae76dc522d08845dc741621b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { "cerdic/css-tidy": "^1.7 || ^2.0", @@ -12965,9 +12965,9 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" }, - "time": "2023-11-17T15:01:25+00:00" + "time": "2024-11-01T03:51:45+00:00" }, { "name": "fileeye/mimemap", @@ -17385,12 +17385,12 @@ "source": { "type": "git", "url": "https://github.com/SU-SWS/stanford_profile.git", - "reference": "9c3b93306b86ffa48b2f1eec67c22a2242e792d1" + "reference": "9d7fa73943641937bbfebf92bea14b411016a0cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SU-SWS/stanford_profile/zipball/9c3b93306b86ffa48b2f1eec67c22a2242e792d1", - "reference": "9c3b93306b86ffa48b2f1eec67c22a2242e792d1", + "url": "https://api.github.com/repos/SU-SWS/stanford_profile/zipball/9d7fa73943641937bbfebf92bea14b411016a0cc", + "reference": "9d7fa73943641937bbfebf92bea14b411016a0cc", "shasum": "" }, "require": { @@ -17556,7 +17556,7 @@ "issues": "https://github.com/SU-SWS/stanford_profile/issues", "source": "https://github.com/SU-SWS/stanford_profile/tree/11.x" }, - "time": "2024-10-29T16:54:26+00:00" + "time": "2024-11-04T19:43:32+00:00" }, { "name": "su-sws/stanford_profile_helper", @@ -22165,16 +22165,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "48a792895a2b7a6ee65dd5442c299d7b835b6137" + "reference": "3b1fc3f0be055baa7c6258b1467849c3e8204eb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/48a792895a2b7a6ee65dd5442c299d7b835b6137", - "reference": "48a792895a2b7a6ee65dd5442c299d7b835b6137", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/3b1fc3f0be055baa7c6258b1467849c3e8204eb2", + "reference": "3b1fc3f0be055baa7c6258b1467849c3e8204eb2", "shasum": "" }, "require": { @@ -22221,7 +22221,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.2" + "source": "https://github.com/composer/ca-bundle/tree/1.5.3" }, "funding": [ { @@ -22237,7 +22237,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T07:49:53+00:00" + "time": "2024-11-04T10:15:26+00:00" }, { "name": "composer/class-map-generator", @@ -25076,16 +25076,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.5.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "54e10d44fc1a84e2598d26f70d4f6f1f233e228a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/54e10d44fc1a84e2598d26f70d4f6f1f233e228a", + "reference": "54e10d44fc1a84e2598d26f70d4f6f1f233e228a", "shasum": "" }, "require": { @@ -25098,13 +25098,13 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.5", + "mockery/mockery": "~1.3.5 || ~1.6.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -25134,29 +25134,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.5.0" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-11-04T21:26:31+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1fb5ba8d045f5dd984ebded5b1cc66f29459422d", + "reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18" }, "require-dev": { "ext-tokenizer": "*", @@ -25192,9 +25192,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.9.0" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-11-03T20:11:34+00:00" }, { "name": "phpspec/prophecy", diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.core.entity_view_display.paragraph.stanford_page_title_banner.variant_1.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.core.entity_view_display.paragraph.stanford_page_title_banner.variant_1.yml new file mode 100644 index 00000000..dfbbcd29 --- /dev/null +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.core.entity_view_display.paragraph.stanford_page_title_banner.variant_1.yml @@ -0,0 +1,4 @@ +adding: + hidden: + sup_title_banner_color: true +removing: { } diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.graphql_compose.settings.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.graphql_compose.settings.yml index d20f2805..b717b1ad 100644 --- a/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.graphql_compose.settings.yml +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.graphql_compose.settings.yml @@ -1,43 +1,9 @@ adding: entity_config: - config_pages: - policy_settings: - enabled: false - stanford_courses_importer: - enabled: false - stanford_events_importer: - enabled: false - stanford_person_importer: - enabled: false - stanford_saml: - enabled: false media: sup_protected_file: enabled: true - menu: - account: - enabled: false - admin: - enabled: false - courses-menu: - enabled: false - devel: - enabled: false - footer: - enabled: false - news-topics: - enabled: false - stanford-event-types: - enabled: false - stanford-person-type: - enabled: false - stanford-publication-topics: - enabled: false - tools: - enabled: false node: - stanford_opportunity: - enabled: false sup_book: enabled: true query_load_enabled: true @@ -51,49 +17,36 @@ adding: paragraph: stanford_page_title_banner: query_load_enabled: true - sup_author_list: - enabled: true - sup_blog: - enabled: true - sup_blog_teaser: - enabled: true sup_carousel: enabled: true sup_carousel_slide: enabled: true sup_file_list: enabled: true - sup_search_form: + sup_author_list: enabled: true - sup_award: - sup_award: + sup_blog: + enabled: true + sup_blog_teaser: + enabled: true + sup_search_form: enabled: true taxonomy_term: - cap_org_codes: - enabled: false - media_tags: - enabled: false - opportunity_sponsor: - enabled: false - opportunity_tag_filters: - enabled: false - opportunity_type: - enabled: false - site_owner_orgs: - enabled: false sup_book_subjects: enabled: true query_load_enabled: true - sup_book_tags: - enabled: false - sup_file_groups: - enabled: false sup_imprints: enabled: true query_load_enabled: true sup_series: enabled: true - query_load_enabled: true + sup_book_tags: + enabled: false + sup_file_groups: + enabled: false + sup_award: + sup_award: + enabled: true field_config: media: sup_protected_file: @@ -101,6 +54,8 @@ adding: enabled: true node: sup_book: + sup_book_awards: + enabled: true sup_book_author_info: enabled: true sup_book_authors: @@ -109,8 +64,6 @@ adding: enabled: true sup_book_avail_desc: enabled: true - sup_book_awards: - enabled: true sup_book_catalog_season_yyyy: enabled: true sup_book_cloth_sale_percent: @@ -123,16 +76,10 @@ adding: enabled: true sup_book_digital_comp_link: enabled: true - sup_book_e_retailers: - enabled: true - sup_book_excerpts: - enabled: true sup_book_image: enabled: true sup_book_imprint: enabled: true - sup_book_intl_cart: - enabled: true sup_book_isbn_13_alt: enabled: true sup_book_isbn_13_cloth: @@ -143,16 +90,12 @@ adding: enabled: true sup_book_isbn_13_paper: enabled: true - sup_book_no_cart: - enabled: true sup_book_pages: enabled: true sup_book_paper_sale_percent: enabled: true sup_book_paper_sale_price: enabled: true - sup_book_preorder: - enabled: true sup_book_price_cloth: enabled: true sup_book_price_paper: @@ -165,8 +108,6 @@ adding: enabled: true sup_book_pub_year_first: enabled: true - sup_book_publisher: - enabled: true sup_book_related_titles: enabled: true sup_book_reviews: @@ -179,50 +120,51 @@ adding: enabled: true sup_book_subtitle: enabled: true - sup_book_type: - enabled: true sup_book_url_isw: enabled: true sup_book_work_id_number: enabled: true - sup_book_ancillary: - body: + sup_book_type: enabled: true - sup_ancillary_book: + sup_book_intl_cart: enabled: true - sup_ancillary_paragraphs: + sup_book_excerpts: enabled: true - paragraph: - stanford_lists: - sup_list_eyebrow: + sup_book_e_retailers: enabled: true - stanford_page_title_banner: - sup_title_banner_color: + sup_book_no_cart: enabled: true - sup_blog: - sup_blog_body: + sup_book_preorder: enabled: true - sup_blog_image: + sup_book_publisher: enabled: true - sup_blog_title: + sup_book_ancillary: + sup_ancillary_book: enabled: true - sup_blog_url: + sup_ancillary_paragraphs: enabled: true - sup_blog_teaser: - sup_blog_teaser_header: + body: enabled: true - sup_blog_teaser_items: + paragraph: + stanford_lists: + sup_list_eyebrow: enabled: true - sup_blog_teaser_link: + stanford_page_title_banner: + sup_title_banner_color: enabled: true sup_carousel: sup_carousel_slides: enabled: true sup_carousel_top_hero: enabled: true - sup_carousel_slide: - sup_slide_bg_image: + sup_file_list: + sup_file_list_files: + enabled: true + sup_file_list_label: enabled: true + sup_file_list_display: + enabled: true + sup_carousel_slide: sup_slide_body: enabled: true sup_slide_button: @@ -233,8 +175,6 @@ adding: enabled: true sup_slide_hide: enabled: true - sup_slide_image: - enabled: true sup_slide_orientation: enabled: true sup_slide_subtitle: @@ -243,12 +183,25 @@ adding: enabled: true sup_slide_title_size: enabled: true - sup_file_list: - sup_file_list_display: + sup_slide_bg_image: enabled: true - sup_file_list_files: + sup_slide_image: enabled: true - sup_file_list_label: + sup_blog: + sup_blog_body: + enabled: true + sup_blog_image: + enabled: true + sup_blog_title: + enabled: true + sup_blog_url: + enabled: true + sup_blog_teaser: + sup_blog_teaser_header: + enabled: true + sup_blog_teaser_items: + enabled: true + sup_blog_teaser_link: enabled: true sup_search_form: sup_search_books_only: @@ -269,4 +222,8 @@ adding: enabled: true sup_year: enabled: true + taxonomy_term: + sup_series: + sup_series_page: + enabled: true removing: { } diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.search_api.index.algolia_search.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.search_api.index.algolia_search.yml index e7e1a4cd..f148ff07 100644 --- a/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.search_api.index.algolia_search.yml +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/config_split.patch.search_api.index.algolia_search.yml @@ -1,16 +1,16 @@ adding: dependencies: config: - - field.storage.node.sup_book_authors - - field.storage.node.sup_book_authors_full - - field.storage.node.sup_book_awards - - field.storage.node.sup_book_imprint - field.storage.node.sup_book_isbn_13_alt - field.storage.node.sup_book_isbn_13_cloth - field.storage.node.sup_book_isbn_13_digital - field.storage.node.sup_book_isbn_13_isw - field.storage.node.sup_book_isbn_13_paper - field.storage.node.sup_book_pub_date_first + - field.storage.node.sup_book_authors_full + - field.storage.node.sup_book_authors + - field.storage.node.sup_book_awards + - field.storage.node.sup_book_imprint - field.storage.node.sup_book_series - field.storage.node.sup_book_subjects - field.storage.node.sup_book_subtitle @@ -56,7 +56,7 @@ adding: label: 'Publication Date First' datasource_id: 'entity:node' property_path: sup_book_pub_date_first - type: integer + type: date dependencies: config: - field.storage.node.sup_book_pub_date_first diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_form_display.taxonomy_term.sup_series.default.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_form_display.taxonomy_term.sup_series.default.yml index 37d906e5..87e45db6 100644 --- a/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_form_display.taxonomy_term.sup_series.default.yml +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_form_display.taxonomy_term.sup_series.default.yml @@ -3,44 +3,40 @@ langcode: en status: true dependencies: config: + - field.field.taxonomy_term.sup_series.sup_series_page - taxonomy.vocabulary.sup_series module: - - path - - text + - link id: taxonomy_term.sup_series.default targetEntityType: taxonomy_term bundle: sup_series mode: default content: - description: - type: text_textfield - weight: 0 - region: content - settings: - size: 60 - placeholder: '' - third_party_settings: { } name: type: string_textfield - weight: -5 + weight: 0 region: content settings: size: 60 placeholder: '' third_party_settings: { } - path: - type: path - weight: 30 - region: content - settings: { } - third_party_settings: { } status: type: boolean_checkbox - weight: 100 + weight: 2 region: content settings: display_label: true third_party_settings: { } + sup_series_page: + type: link_default + weight: 1 + region: content + settings: + placeholder_url: '' + placeholder_title: '' + third_party_settings: { } hidden: + description: true + path: true publish_on: true unpublish_on: true diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_view_display.taxonomy_term.sup_series.default.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_view_display.taxonomy_term.sup_series.default.yml new file mode 100644 index 00000000..313a3764 --- /dev/null +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/core.entity_view_display.taxonomy_term.sup_series.default.yml @@ -0,0 +1,36 @@ +uuid: a7d1a135-6544-48f3-b8ac-4afcb9ac3801 +langcode: en +status: true +dependencies: + config: + - field.field.taxonomy_term.sup_series.sup_series_page + - taxonomy.vocabulary.sup_series + module: + - link + - text +id: taxonomy_term.sup_series.default +targetEntityType: taxonomy_term +bundle: sup_series +mode: default +content: + description: + type: text_default + label: hidden + settings: { } + third_party_settings: { } + weight: 0 + region: content + sup_series_page: + type: link + label: above + settings: + trim_length: 80 + url_only: false + url_plain: false + rel: '' + target: '' + third_party_settings: { } + weight: 1 + region: content +hidden: + search_api_excerpt: true diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/field.field.taxonomy_term.sup_series.sup_series_page.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/field.field.taxonomy_term.sup_series.sup_series_page.yml new file mode 100644 index 00000000..8a8f508a --- /dev/null +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/field.field.taxonomy_term.sup_series.sup_series_page.yml @@ -0,0 +1,27 @@ +uuid: 6f17ab74-51b9-4c38-ab60-aab974214b81 +langcode: en +status: true +dependencies: + config: + - field.storage.taxonomy_term.sup_series_page + - taxonomy.vocabulary.sup_series + module: + - link + - stanford_fields +third_party_settings: + stanford_fields: + force_relative: true +id: taxonomy_term.sup_series.sup_series_page +field_name: sup_series_page +entity_type: taxonomy_term +bundle: sup_series +label: 'Series Page' +description: '' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + title: 0 + link_type: 1 +field_type: link diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/field.storage.taxonomy_term.sup_series_page.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/field.storage.taxonomy_term.sup_series_page.yml new file mode 100644 index 00000000..a7085dfc --- /dev/null +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/field.storage.taxonomy_term.sup_series_page.yml @@ -0,0 +1,19 @@ +uuid: 6664056b-57be-44df-bcf7-4f522447bb4f +langcode: en +status: true +dependencies: + module: + - link + - taxonomy +id: taxonomy_term.sup_series_page +field_name: sup_series_page +entity_type: taxonomy_term +type: link +settings: { } +module: link +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_awards.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_awards.yml index db2130e6..b622b9ee 100644 --- a/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_awards.yml +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_awards.yml @@ -63,8 +63,17 @@ process: plugin: skip_on_empty source: year method: row - sup_description/value: description - sup_description/format: constant/minimal_html + sup_description/value: + - + plugin: skip_on_empty + method: process + source: description + - + plugin: str_replace + search: /<(\/?)i>/ + replace: '<$1em>' + regex: true + sup_description/format: constants/minimal_html destination: plugin: 'entity:sup_award' migration_dependencies: { } diff --git a/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_import_books.yml b/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_import_books.yml index c9097912..34da1585 100644 --- a/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_import_books.yml +++ b/docroot/profiles/lagunita/supress/config/sync/split/supress/migrate_plus.migration.sup_import_books.yml @@ -330,6 +330,10 @@ source: name: cart_button label: cart_button selector: fieldData/cart_button + - + name: series + label: series + selector: fieldData/series ids: work_id_number: type: integer @@ -548,6 +552,26 @@ process: preorder: 0 add: 0 none: 1 + sup_book_series: + - + plugin: skip_on_empty + method: process + source: series + - + plugin: str_replace + search: /^\s+|\s+$/ + replace: '' + regex: true + - + plugin: skip_on_empty + method: process + - + plugin: entity_generate + entity_type: taxonomy_term + bundle_key: vid + bundle: sup_series + value_key: name + ignore_case: true destination: plugin: 'entity:node' default_bundle: sup_book