├── .distignore ├── .editorconfig ├── .gitignore ├── .nvmrc ├── .release-please-manifest.json ├── .tx ├── config └── transifex.yml ├── 404.php ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── archive.php ├── assets ├── fonts │ ├── .gitkeep │ └── pressbooks-theme.woff ├── images │ ├── .gitkeep │ ├── catalog-header.jpg │ ├── header.jpg │ ├── logo.svg │ └── pb.svg ├── scripts │ ├── aldine.js │ ├── call-to-action.js │ ├── catalog-admin.js │ ├── customizer-toggle.js │ ├── customizer.js │ ├── featured-books.js │ ├── page-section.js │ ├── routes │ │ ├── catalog.js │ │ ├── common.js │ │ └── home.js │ ├── search-featured-books.js │ └── util │ │ ├── Router.js │ │ └── camelCase.js └── styles │ ├── aldine.scss │ ├── common │ ├── _global.scss │ └── _variables.scss │ ├── components │ ├── _book.scss │ ├── _featured_book.scss │ └── _forms.scss │ ├── editor.scss │ ├── layouts │ ├── _footer.scss │ ├── _header.scss │ ├── _page-catalog.scss │ ├── _page-home.scss │ ├── _page.scss │ └── _signup.scss │ └── search-featured-books.scss ├── bin ├── i18n.sh └── install-wp-tests.sh ├── codecov.yml ├── comments.php ├── composer.json ├── composer.lock ├── dist ├── fonts │ ├── .gitkeep │ └── pressbooks-theme.woff ├── images │ ├── .gitkeep │ ├── catalog-header.jpg │ ├── header.jpg │ ├── logo.svg │ └── pb.svg ├── mix-manifest.json ├── scripts │ ├── aldine.js │ ├── aldine.js.LICENSE.txt │ ├── call-to-action.js │ ├── catalog-admin.js │ ├── catalog-admin.js.LICENSE.txt │ ├── customizer-toggle.js │ ├── customizer.js │ ├── page-section.js │ ├── search-featured-books.js │ ├── search-featured-books.js.LICENSE.txt │ └── shortcodes.js └── styles │ ├── aldine.css │ ├── editor.css │ └── search-featured-books.css ├── footer.php ├── functions.php ├── header.php ├── inc ├── actions │ └── namespace.php ├── activation │ └── namespace.php ├── admin │ └── namespace.php ├── customizer │ ├── class-searchfeaturedbooks.php │ └── namespace.php ├── filters │ └── namespace.php ├── helpers │ └── namespace.php ├── shortcodes │ └── namespace.php └── tags │ └── namespace.php ├── index.php ├── languages ├── bg_BG.mo ├── bg_BG.po ├── de_CH.mo ├── de_CH.po ├── de_DE.mo ├── de_DE.po ├── en_CA.mo ├── en_CA.po ├── en_GB.mo ├── en_GB.po ├── es_ES.mo ├── es_ES.po ├── fr_FR.mo ├── fr_FR.po ├── he.mo ├── he.po ├── hr.mo ├── hr.po ├── hu_HU.mo ├── hu_HU.po ├── it_IT.mo ├── it_IT.po ├── ja.mo ├── ja.po ├── kn.mo ├── kn.po ├── lt_LT.mo ├── lt_LT.po ├── lv.mo ├── lv.po ├── nl_NL.mo ├── nl_NL.po ├── pressbooks-aldine.pot ├── ru_RU.mo ├── ru_RU.po ├── tr_TR.mo ├── tr_TR.po ├── zh_CN.mo ├── zh_CN.po ├── zh_TW.mo └── zh_TW.po ├── lib └── customizer-validate-wcag-color-contrast │ ├── README.md │ └── customizer-validate-wcag-color-contrast.js ├── package-lock.json ├── package.json ├── page-catalog.php ├── page-featured-books.php ├── page.php ├── partials ├── book.php ├── contact-form.php ├── content-front-page.php ├── content-none.php ├── content-page-catalog.php ├── content-page-featured-books.php ├── content-page.php ├── content-search.php ├── content-single.php ├── content.php ├── featured-book.php ├── page-block.php ├── page-header.php ├── paged-navigation.php └── search-featured-books.php ├── phpcs.ruleset.xml ├── phpunit.xml ├── release-please-config.json ├── screenshot.png ├── search.php ├── searchform.php ├── sidebar.php ├── single.php ├── style.css ├── tests ├── bootstrap.php └── test-filters.php └── webpack.mix.js /.distignore: -------------------------------------------------------------------------------- 1 | .git 2 | assets/src 3 | bin 4 | codecov.yml 5 | node_modules 6 | tests 7 | .editorconfig 8 | .gitattributes 9 | .github 10 | .gitignore 11 | .travis.yml 12 | .tx 13 | phpcs.ruleset.xml 14 | phpunit.xml 15 | webpack.mix.js 16 | yarn.lock 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | # WordPress Coding Standards 5 | # https://make.wordpress.org/core/handbook/coding-standards/ 6 | 7 | root = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | indent_style = tab 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | [{.jshintrc,*.json,*.yml,*.scss}] 20 | indent_style = space 21 | indent_size = 2 22 | 23 | [{*.txt,wp-config-sample.php}] 24 | end_of_line = crlf 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | .cache-loader 4 | .sass-cache 5 | .vscode 6 | node_modules 7 | npm-debug.log 8 | yarn-error.log 9 | vendor 10 | resources/assets/config-local.json 11 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18 2 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "1.21.0" 3 | } 4 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [o:pressbooks:p:aldine:r:pressbooks-aldine] 5 | file_filter = languages/.po 6 | lang_map = de:de_DE, es:es_ES, fr:fr_FR, gl:gl_ES, nb:nb_NO, it:it_IT, ka:ka_GE, ru:ru_RU, ta:ta_LK, hr_HR:hr 7 | minimum_perc = 0 8 | source_file = languages/pressbooks-aldine.pot 9 | source_lang = en 10 | type = PO 11 | -------------------------------------------------------------------------------- /.tx/transifex.yml: -------------------------------------------------------------------------------- 1 | git: 2 | filters: 3 | - filter_type: file 4 | file_format: PO 5 | source_file: languages/pressbooks-aldine.pot 6 | source_language: en 7 | translation_files_expression: languages/.po 8 | settings: 9 | pr_branch_name: chore/update-translations- 10 | language_mapping: 11 | de: de_DE 12 | es: es_ES 13 | fr: fr_FR 14 | gl: gl_ES 15 | nb: nb_NO 16 | it: it_IT 17 | ka: ka_GE 18 | ru: ru_RU 19 | ta: ta_LK 20 | hr_HR: hr 21 | -------------------------------------------------------------------------------- /404.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 | 15 |
16 | 19 | 20 |
21 |

22 |
23 |
24 | 25 |
26 |
27 | 28 | 16 | Stable tag: 1.19.1 17 | 18 | Requires PHP: 8.1 19 | License: GNU General Public License v3 or later 20 | License URI: https://www.gnu.org/licenses/gpl-3.0.html 21 | 22 | Aldine is the default theme for the home page of Pressbooks networks. It is named for the Aldine Press, founded by Aldus Manutius in 1494, who is regarded by many as the world’s first publisher. 23 | 24 | ## Description 25 | 26 | Aldine is the default theme for the home page of [Pressbooks](https://pressbooks.org) networks. It is named for the Aldine Press, founded by Aldus Manutius in 1494, who is regarded by many as the world’s first publisher. Aldine is based on [Underscores](https://underscores.me/). 27 | 28 | ## Installation 29 | 30 | 1. In your admin panel, go to Appearance > Themes and click the Add New button. 31 | 2. Click Upload Theme and Choose File, then select the theme's [.zip file](https://github.com/pressbooks/pressbooks-aldine/releases/latest/). Click Install Now. 32 | 3. Click Activate to use your new theme right away. 33 | 34 | ### Changelog 35 | Please see the [CHANGELOG](CHANGELOG.md) file for more information. 36 | 37 | ## Credits 38 | - Based on [Underscores](https://underscores.me/), (C) 2012-2017 Automattic, Inc., [GPLv2 or later](https://www.gnu.org/licenses/gpl-2.0.html) 39 | -------------------------------------------------------------------------------- /archive.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 | 15 | 18 | 19 | 25 | 26 | 48 | 49 |
50 |
51 | 52 | Pressbooks 2 | -------------------------------------------------------------------------------- /assets/images/pb.svg: -------------------------------------------------------------------------------- 1 | PB 2 | -------------------------------------------------------------------------------- /assets/scripts/aldine.js: -------------------------------------------------------------------------------- 1 | // import local dependencies 2 | import catalog from './routes/catalog'; 3 | import common from './routes/common'; 4 | import home from './routes/home'; 5 | import Router from './util/Router'; 6 | 7 | /** Populate Router instance with DOM routes */ 8 | const routes = new Router( { 9 | // All pages 10 | common, 11 | // Home page 12 | home, 13 | // Catalog page 14 | pageTemplatePageCatalog: catalog, 15 | } ); 16 | 17 | // Load Events 18 | jQuery( document ).ready( () => routes.loadEvents() ); 19 | -------------------------------------------------------------------------------- /assets/scripts/call-to-action.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | ( function () { 3 | tinymce.create( 'tinymce.plugins.aldine_call_to_action', { 4 | /** 5 | * @param editor 6 | * @param url 7 | */ 8 | init: function ( editor, url ) { 9 | editor.addButton( 'aldine_call_to_action', { 10 | title: aldine.call_to_action.title, 11 | icon: 'icon dashicons-flag', 12 | /** 13 | * 14 | */ 15 | onclick: function () { 16 | editor.windowManager.open( { 17 | title: aldine.call_to_action.title, 18 | body: [ 19 | { 20 | type: 'textbox', 21 | name: 'text', 22 | label: aldine.call_to_action.text, 23 | value: aldine.call_to_action.title, 24 | }, 25 | { 26 | type: 'textbox', 27 | name: 'link', 28 | label: aldine.call_to_action.link, 29 | value: '#', 30 | }, 31 | ], 32 | /** 33 | * @param e 34 | */ 35 | onsubmit: function ( e ) { 36 | editor.insertContent( 37 | '[aldine_call_to_action text="' + 38 | e.data.text + 39 | '" link="' + 40 | e.data.link + 41 | '"]' 42 | ); 43 | }, 44 | } ); 45 | }, 46 | } ); 47 | }, 48 | /** 49 | * @param n 50 | * @param cm 51 | */ 52 | createControl: function ( n, cm ) { 53 | return null; 54 | }, 55 | } ); 56 | tinymce.PluginManager.add( 57 | 'aldine_call_to_action', 58 | tinymce.plugins.aldine_call_to_action 59 | ); 60 | } )(); 61 | -------------------------------------------------------------------------------- /assets/scripts/catalog-admin.js: -------------------------------------------------------------------------------- 1 | /* global ajaxurl, PB_Aldine_Admin */ 2 | ( function ( $ ) { 3 | $( document ).ready( function () { 4 | $( '.wrap' ).on( 'click', '.notice-dismiss', function () { 5 | $( this ) 6 | .parent( '#message' ) 7 | .fadeOut( 500, function () { 8 | $( this ).remove(); 9 | } ); 10 | } ); 11 | $( 'input.in-catalog' ).on( 'change', function () { 12 | let book_id = $( this ) 13 | .parent( 'td' ) 14 | .siblings( 'th' ) 15 | .children( 'input' ) 16 | .val(); 17 | let in_catalog = $( this ).prop( 'checked' ); 18 | $.ajax( { 19 | url: ajaxurl, 20 | type: 'POST', 21 | data: { 22 | action: 'pressbooks_aldine_update_catalog', 23 | book_id: book_id, 24 | in_catalog: in_catalog, 25 | _ajax_nonce: PB_Aldine_Admin.aldineAdminNonce, 26 | }, 27 | success: function () { 28 | if ( $( '#message' ).length < 1 ) { 29 | $( '
' ) 30 | .html( 31 | '

' + 32 | PB_Aldine_Admin.catalog_updated + 33 | '

' 36 | ) 37 | .hide() 38 | .insertAfter( '.wrap h1' ) 39 | .fadeIn( 500 ); 40 | } else { 41 | $( '#message' ).fadeOut( 500, function () { 42 | $( this ).remove(); 43 | $( '
' ) 44 | .html( 45 | '

' + 46 | PB_Aldine_Admin.catalog_updated + 47 | '

' 50 | ) 51 | .hide() 52 | .insertAfter( '.wrap h1' ) 53 | .fadeIn( 500 ); 54 | } ); 55 | } 56 | }, 57 | error: function ( jqXHR, textStatus, errorThrown ) { 58 | if ( $( '#message' ).length < 1 ) { 59 | $( '
' ) 60 | .html( 61 | '

' + 62 | PB_Aldine_Admin.catalog_not_updated + 63 | '

' 66 | ) 67 | .hide() 68 | .insertAfter( '.wrap h1' ) 69 | .fadeIn( 500 ); 70 | } else { 71 | $( '#message' ).fadeOut( 500, function () { 72 | $( this ).remove(); 73 | $( '
' ) 74 | .html( 75 | '

' + 76 | PB_Aldine_Admin.catalog_not_updated + 77 | '

' 80 | ) 81 | .hide() 82 | .insertAfter( '.wrap h1' ) 83 | .fadeIn( 500 ); 84 | } ); 85 | } 86 | }, 87 | } ); 88 | } ); 89 | } ); 90 | } )( jQuery ); 91 | -------------------------------------------------------------------------------- /assets/scripts/customizer-toggle.js: -------------------------------------------------------------------------------- 1 | document.addEventListener( 'DOMContentLoaded', function () { 2 | let checkbox = document.getElementById( '_customize-input-pb_network_contact_form' ); 3 | let email = document.getElementById( 'customize-control-pb_network_contact_email' ); 4 | let link = document.getElementById( 'customize-control-pb_network_contact_link' ); 5 | let title = document.getElementById( 'customize-control-pb_network_contact_form_title' ); 6 | 7 | checkbox.addEventListener( 'click', toggleReadOnly ); 8 | 9 | /** 10 | * 11 | */ 12 | function toggleReadOnly() { 13 | if ( checkbox.checked === false ) { 14 | email.classList.add( 'hidden' ); 15 | email.style.cssText = null; 16 | 17 | title.classList.add( 'hidden' ); 18 | title.style.cssText = null; 19 | 20 | link.classList.remove( 'hidden' ); 21 | link.style.cssText = 'display: list-item;'; 22 | } else { 23 | email.classList.remove( 'hidden' ); 24 | email.style.cssText = 'display: list-item;'; 25 | 26 | title.classList.remove( 'hidden' ); 27 | title.style.cssText = 'display: list-item;'; 28 | 29 | link.classList.add( 'hidden' ); 30 | link.style.cssText = null; 31 | } 32 | } 33 | 34 | toggleReadOnly(); 35 | 36 | } ); 37 | -------------------------------------------------------------------------------- /assets/scripts/customizer.js: -------------------------------------------------------------------------------- 1 | wp.customize( 'blogname', value => { 2 | value.bind( to => document.querySelector( '.home .entry-title' ).textContent = to ); 3 | } ); 4 | 5 | wp.customize( 'blogdescription', value => { 6 | value.bind( to => document.querySelector( '.home .entry-description' ).textContent = to ); 7 | } ); 8 | -------------------------------------------------------------------------------- /assets/scripts/featured-books.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Disallow duplicate books in the featured books list. 3 | */ 4 | window.addEventListener( 'load', function () { 5 | const selects = document.querySelectorAll( '#sub-accordion-section-pb_front_page_catalog select' ); 6 | /** 7 | * 8 | * @param current 9 | * @param value 10 | */ 11 | let checkOtherValues = function ( current, value ) { 12 | selects.forEach( function ( select ) { 13 | if ( current.id !== select.id && select.value === value ) { 14 | select.selectedIndex = -1; 15 | } 16 | } ); 17 | }; 18 | selects.forEach( function ( select ) { 19 | select.addEventListener( 'change', function ( event ) { 20 | checkOtherValues( event.target, event.target.value ); 21 | } ); 22 | } ); 23 | } ); 24 | -------------------------------------------------------------------------------- /assets/scripts/page-section.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | ( function () { 3 | tinymce.create( 'tinymce.plugins.aldine_page_section', { 4 | /** 5 | * @param editor 6 | * @param url 7 | */ 8 | init: function ( editor, url ) { 9 | editor.addButton( 'aldine_page_section', { 10 | title: aldine.page_section.title, 11 | icon: 'icon dashicons-layout', 12 | /** 13 | * 14 | */ 15 | onclick: function () { 16 | editor.windowManager.open( { 17 | title: aldine.page_section.title, 18 | body: [ 19 | { 20 | type: 'textbox', 21 | name: 'title', 22 | label: aldine.page_section.title_label, 23 | value: aldine.page_section.title, 24 | }, 25 | { 26 | type: 'listbox', 27 | name: 'variant', 28 | label: 'Variant', 29 | values: [ 30 | { 31 | text: aldine.page_section.standard, 32 | value: '', 33 | }, 34 | { 35 | text: aldine.page_section.accent, 36 | value: 'accent', 37 | }, 38 | { 39 | text: aldine.page_section.bordered, 40 | value: 'bordered', 41 | }, 42 | { 43 | text: aldine.page_section.borderless, 44 | value: 'borderless', 45 | }, 46 | ], 47 | value: '', // Sets the default 48 | }, 49 | ], 50 | /** 51 | * @param e 52 | */ 53 | onsubmit: function ( e ) { 54 | editor.insertContent( 55 | '[aldine_page_section title="' + 56 | e.data.title + 57 | '" variant="' + 58 | e.data.variant + 59 | '"]

Insert your page section content here.

[/aldine_page_section]' 60 | ); 61 | }, 62 | } ); 63 | }, 64 | } ); 65 | }, 66 | /** 67 | * @param n 68 | * @param cm 69 | */ 70 | createControl: function ( n, cm ) { 71 | return null; 72 | }, 73 | } ); 74 | tinymce.PluginManager.add( 75 | 'aldine_page_section', 76 | tinymce.plugins.aldine_page_section 77 | ); 78 | } )(); 79 | -------------------------------------------------------------------------------- /assets/scripts/routes/common.js: -------------------------------------------------------------------------------- 1 | export default { 2 | init() { 3 | // JavaScript to be fired on all pages 4 | document.body.classList.replace( 'no-js', 'js' ); 5 | 6 | jQuery( $ => { 7 | $( document ).ready( function () { 8 | // Sets a -1 tabindex to ALL sections for .focus()-ing 9 | let sections = document.getElementsByTagName( 'section' ); 10 | for ( let i = 0, max = sections.length; i < max; i++ ) { 11 | sections[i].setAttribute( 'tabindex', -1 ); 12 | sections[i].className += ' focusable'; 13 | } 14 | 15 | // Scroll to anchor if there's a hash in the URL & anchor exists on the page 16 | if (document.location.hash && document.location.hash !== '#') { 17 | const anchorUponArrival = document.location.hash; 18 | setTimeout(() => { 19 | const $anchorElement = $(anchorUponArrival); 20 | if ($anchorElement.length) { 21 | $anchorElement.scrollTo({ duration: 1500 }); 22 | $anchorElement.trigger('focus'); 23 | } else { 24 | console.warn(`Anchor element "${anchorUponArrival}" not found in the document.`); 25 | } 26 | }, 100); 27 | } 28 | } ); 29 | $( '.js-header-nav-toggle' ).on( 'click', event => { 30 | const expanded = $( '.js-header-nav-toggle' ).attr('aria-expanded') === 'true' || false; 31 | $( '.js-header-nav-toggle' ).attr('aria-expanded', !expanded); 32 | $( '.header__nav' ).toggleClass( 'header__nav--active' ); 33 | } ); 34 | } ); 35 | 36 | // Check form field validity when focus changes 37 | const inputs = document.querySelectorAll( 'input, textarea' ); 38 | inputs.forEach( input => { 39 | input.addEventListener('invalid', () => { 40 | input.classList.add('error'); 41 | }); 42 | input.addEventListener( 'focus', function () { 43 | input.classList.remove( 'error' ); 44 | } ); 45 | input.addEventListener('blur', () => { 46 | if (!input.checkValidity()) { 47 | input.classList.add('error'); 48 | } else { 49 | input.classList.remove('error'); 50 | } 51 | }); 52 | } ); 53 | }, 54 | finalize() { 55 | // JavaScript to be fired on all pages, after page specific JS is fired 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /assets/scripts/routes/home.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * 4 | */ 5 | init() { 6 | // JavaScript to be fired on the home page 7 | }, 8 | /** 9 | * 10 | */ 11 | finalize() {}, 12 | }; 13 | -------------------------------------------------------------------------------- /assets/scripts/search-featured-books.js: -------------------------------------------------------------------------------- 1 | /* global PB_Ajax */ 2 | jQuery( function ( $ ) { 3 | /** 4 | * 5 | * @param container 6 | */ 7 | function initSearchControl( container ) { 8 | const $input = container.find( '.search-books-input' ); 9 | const $results = container.find( '.search-books-results' ); 10 | const settingId = $input.data( 'setting' ); 11 | 12 | $input.on( 'input', function () { 13 | const term = $( this ).val().trim(); 14 | if ( ! term.length ) { 15 | $results.empty(); 16 | $input 17 | .attr( 'aria-expanded', 'false' ) 18 | .removeAttr( 'aria-activedescendant' ); 19 | $results.hide(); 20 | 21 | wp.customize( settingId, function ( value ) { 22 | value.set( '' ); 23 | } ); 24 | return; 25 | } 26 | 27 | $.get( PB_Ajax.ajax_url, { 28 | action: 'pb_search_catalog_books', 29 | q: term, 30 | }, function ( response ) { 31 | $results.empty(); 32 | if ( response.success ) { 33 | response.data.forEach( function ( item ) { 34 | const $li = $( '
  • ' ) 35 | .attr( { 36 | role: 'option', 37 | tabindex: -1, 38 | id: `search-books-option-${ settingId }-${ item.id }`, 39 | 'data-value': item.id, 40 | } ) 41 | .text( item.text ); 42 | 43 | $results.append( $li ); 44 | } ); 45 | $input.attr( 'aria-expanded', response.data.length > 0 ); 46 | $results.show(); 47 | } else { 48 | $input 49 | .attr( 'aria-expanded', 'false' ) 50 | .removeAttr( 'aria-activedescendant' ); 51 | } 52 | } ); 53 | } ); 54 | 55 | $results.on( 'click', 'li', function () { 56 | const id = $( this ).data( 'value' ); 57 | const text = $( this ).text(); 58 | 59 | $input.val( text ); 60 | wp.customize( settingId, function ( value ) { 61 | value.set( id ); 62 | } ); 63 | 64 | $results.empty(); 65 | $input 66 | .attr( 'aria-expanded', 'false' ) 67 | .removeAttr( 'aria-activedescendant' ); 68 | $results.hide(); 69 | } ); 70 | 71 | $input.on( 'keydown', function ( e ) { 72 | const $options = $results.find( 'li' ); 73 | let $active = $results.find( '[aria-selected="true"]' ); 74 | 75 | if ( e.key === 'ArrowDown' ) { 76 | e.preventDefault(); 77 | if ( ! $active.length ) { 78 | $active = $options.first() 79 | .focus() 80 | .attr( 'aria-selected', 'true' ); 81 | $input.attr( 'aria-activedescendant', $active.attr( 'id' ) ); 82 | } else { 83 | const $next = $active.removeAttr( 'aria-selected' ).next( 'li' ); 84 | if ( $next.length ) { 85 | $active = $next 86 | .focus() 87 | .attr( 'aria-selected', 'true' ); 88 | $input.attr( 'aria-activedescendant', $active.attr( 'id' ) ); 89 | } 90 | } 91 | } 92 | 93 | if ( e.key === 'ArrowUp' ) { 94 | e.preventDefault(); 95 | if ( $active.length ) { 96 | const $prev = $active.removeAttr( 'aria-selected' ).prev( 'li' ); 97 | if ( $prev.length ) { 98 | $active = $prev 99 | .focus() 100 | .attr( 'aria-selected', 'true' ); 101 | $input.attr( 'aria-activedescendant', $active.attr( 'id' ) ); 102 | } 103 | } 104 | } 105 | 106 | if ( e.key === 'Enter' && $active.length ) { 107 | e.preventDefault(); 108 | $active.click(); 109 | } 110 | 111 | if ( e.key === 'Escape' ) { 112 | $results.empty(); 113 | $input 114 | .attr( 'aria-expanded', 'false' ) 115 | .removeAttr( 'aria-activedescendant' ); 116 | $results.hide(); 117 | } 118 | } ); 119 | 120 | $results.on( 'keydown', 'li', function ( e ) { 121 | const $current = $( this ); 122 | if ( e.key === 'ArrowDown' ) { 123 | e.preventDefault(); 124 | const $next = $current.next( 'li' ); 125 | if ( $next.length ) { 126 | $current.removeAttr( 'aria-selected' ); 127 | $next.attr( 'aria-selected', 'true' ).focus(); 128 | $input.attr( 'aria-activedescendant', $next.attr( 'id' ) ); 129 | } 130 | } 131 | if ( e.key === 'ArrowUp' ) { 132 | e.preventDefault(); 133 | const $prev = $current.prev( 'li' ); 134 | if ( $prev.length ) { 135 | $current.removeAttr( 'aria-selected' ); 136 | $prev.attr( 'aria-selected', 'true' ).focus(); 137 | $input.attr( 'aria-activedescendant', $prev.attr( 'id' ) ); 138 | } else { 139 | $current.removeAttr( 'aria-selected' ); 140 | $input.focus().removeAttr( 'aria-activedescendant' ); 141 | } 142 | } 143 | if ( e.key === 'Enter' ) { 144 | e.preventDefault(); 145 | $current.click(); 146 | } 147 | if ( e.key === 'Escape' ) { 148 | e.preventDefault(); 149 | $results.empty(); 150 | $input 151 | .attr( 'aria-expanded', 'false' ) 152 | .removeAttr( 'aria-activedescendant' ); 153 | $results.hide(); 154 | $input.focus(); 155 | } 156 | } ); 157 | } 158 | 159 | wp.customize.control.each( function ( control ) { 160 | const container = control.container; 161 | if ( container.find( '.search-books-input' ).length ) { 162 | initSearchControl( container ); 163 | } 164 | } ); 165 | } ); 166 | -------------------------------------------------------------------------------- /assets/scripts/util/Router.js: -------------------------------------------------------------------------------- 1 | import camelCase from './camelCase'; 2 | 3 | /** 4 | * DOM-based Routing 5 | * 6 | * Based on {@link http://goo.gl/EUTi53|Markup-based Unobtrusive Comprehensive DOM-ready Execution} by Paul Irish 7 | * 8 | * The routing fires all common scripts, followed by the page specific scripts. 9 | * Add additional events for more control over timing e.g. a finalize event 10 | */ 11 | class Router { 12 | /** 13 | * Create a new Router 14 | * 15 | * @param {object} routes 16 | */ 17 | constructor( routes ) { 18 | this.routes = routes; 19 | } 20 | 21 | /** 22 | * Fire Router events 23 | * 24 | * @param {string} route DOM-based route derived from body classes (``) 25 | * @param {string} [event] Events on the route. By default, `init` and `finalize` events are called. 26 | * @param {string} [arg] Any custom argument to be passed to the event. 27 | */ 28 | fire( route, event = 'init', arg ) { 29 | const fire = 30 | route !== '' && 31 | this.routes[route] && 32 | typeof this.routes[route][event] === 'function'; 33 | if ( fire ) { 34 | this.routes[route][event]( arg ); 35 | } 36 | } 37 | 38 | /** 39 | * Automatically load and fire Router events 40 | * 41 | * Events are fired in the following order: 42 | * common init 43 | * page-specific init 44 | * page-specific finalize 45 | * common finalize 46 | */ 47 | loadEvents() { 48 | // Fire common init JS 49 | this.fire( 'common' ); 50 | 51 | // Fire page-specific init JS, and then finalize JS 52 | document.body.className 53 | .toLowerCase() 54 | .replace( /-/g, '_' ) 55 | .split( /\s+/ ) 56 | .map( camelCase ) 57 | .forEach( className => { 58 | this.fire( className ); 59 | this.fire( className, 'finalize' ); 60 | } ); 61 | 62 | // Fire common finalize JS 63 | this.fire( 'common', 'finalize' ); 64 | } 65 | } 66 | 67 | export default Router; 68 | -------------------------------------------------------------------------------- /assets/scripts/util/camelCase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the most terrible camelizer on the internet, guaranteed! 3 | * 4 | * @param {string} str String that isn't camel-case, e.g., CAMeL_CaSEiS-harD 5 | * @returns {string} String converted to camel-case, e.g., camelCaseIsHard 6 | */ 7 | export default str => 8 | `${ str.charAt( 0 ).toLowerCase() }${ str 9 | .replace( /[\W_]/g, '|' ) 10 | .split( '|' ) 11 | .map( part => `${ part.charAt( 0 ).toUpperCase() }${ part.slice( 1 ) }` ) 12 | .join( '' ) 13 | .slice( 1 ) }`; 14 | -------------------------------------------------------------------------------- /assets/styles/aldine.scss: -------------------------------------------------------------------------------- 1 | @import "common/variables"; 2 | 3 | /** 4 | * Import npm dependencies 5 | * 6 | * Prefix your imports with `~` to grab from node_modules/ 7 | * @see https://github.com/webpack-contrib/sass-loader#imports 8 | */ 9 | @import "~aetna/assets/styles/aetna"; 10 | 11 | /** Import theme styles */ 12 | @import "common/global"; 13 | @import "components/book"; 14 | @import "components/featured_book"; 15 | @import "components/forms"; 16 | @import "layouts/footer"; 17 | @import "layouts/header"; 18 | @import "layouts/page"; 19 | @import "layouts/page-home"; 20 | @import "layouts/page-catalog"; 21 | @import "layouts/signup"; 22 | -------------------------------------------------------------------------------- /assets/styles/common/_global.scss: -------------------------------------------------------------------------------- 1 | // TODO 2 | :root { 3 | --reading-width: 50em; 4 | } 5 | -------------------------------------------------------------------------------- /assets/styles/common/_variables.scss: -------------------------------------------------------------------------------- 1 | // TODO 2 | -------------------------------------------------------------------------------- /assets/styles/components/_book.scss: -------------------------------------------------------------------------------- 1 | .book { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: flex-start; 5 | width: 100%; 6 | max-width: 22.9375rem; 7 | height: 24.125rem; 8 | margin: 0 0 2rem; 9 | padding: 1.5rem 1rem 2rem; 10 | @media #{$breakpoint-not-small} { padding: 1.5rem 1.85rem 2.1875rem; } 11 | 12 | border: solid 2px var(--accent); 13 | background: var(--accent); 14 | 15 | a, p { 16 | font-family: $font-family-sans-serif; 17 | color: var(--accent-fg); 18 | } 19 | 20 | a { 21 | margin: 0; 22 | text-decoration: none; 23 | text-align: center; 24 | hyphens: auto; 25 | } 26 | 27 | &__title { 28 | margin: 0; 29 | font-size: 1.25rem; 30 | @media #{$breakpoint-not-small} { font-size: 1.75rem; } 31 | 32 | font-weight: 500; 33 | line-height: 1.2; 34 | text-align: left; 35 | } 36 | 37 | &__subject { 38 | margin: 0; 39 | font-size: 0.75rem; 40 | @media #{$breakpoint-not-small} { 41 | font-size: 1rem; 42 | } 43 | 44 | text-align: left; 45 | } 46 | 47 | &__institutions { 48 | margin: .5rem 0 0; 49 | font-size: 0.875rem; 50 | text-align: left; 51 | display: flex; 52 | overflow: hidden; 53 | -webkit-line-clamp: 2; 54 | -webkit-box-orient: vertical; 55 | } 56 | 57 | &__read-more { 58 | margin: auto 0 0; 59 | font-size: 1rem; 60 | @media #{$breakpoint-not-small} { font-size: 1.125rem; } 61 | 62 | text-align: left; 63 | 64 | a { 65 | svg { 66 | width: 1rem; 67 | height: 1rem; 68 | @media #{$breakpoint-not-small} { 69 | width: 1.125rem; 70 | height: 1.125rem; 71 | margin-left: 0.5rem; 72 | } 73 | 74 | vertical-align: middle; 75 | } 76 | } 77 | } 78 | 79 | &:last-child { 80 | margin-bottom: 0; 81 | 82 | @media #{$breakpoint-large} { 83 | margin-bottom: 2rem; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /assets/styles/components/_featured_book.scss: -------------------------------------------------------------------------------- 1 | #latest-books { 2 | .books { 3 | align-items: flex-start; 4 | } 5 | } 6 | 7 | .featured_book { 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: space-between; 11 | width: 100%; 12 | height: 100%; 13 | border: solid 1px var(--accent); 14 | margin: 0 0 2rem; 15 | 16 | &__cover { 17 | aspect-ratio: 3 / 4; 18 | width: 100%; 19 | background-repeat: no-repeat; 20 | background-size: cover; 21 | background-position: center; 22 | min-height: 17rem; 23 | } 24 | 25 | a { 26 | margin: 0; 27 | text-decoration: none; 28 | text-align: center; 29 | hyphens: auto; 30 | } 31 | 32 | &__title { 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | font-weight: 500; 37 | margin-top: 1rem; 38 | min-height: 4rem; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /assets/styles/components/_forms.scss: -------------------------------------------------------------------------------- 1 | label { 2 | margin: 0 0 1rem; 3 | font-family: $font-family-sans-serif; 4 | } 5 | 6 | textarea { 7 | height: 7.5em; 8 | resize: none; 9 | } 10 | 11 | input[type="text"], 12 | input[type="email"], 13 | input[type="tel"], 14 | textarea { 15 | flex-grow: 1; 16 | border-top: 0; 17 | border-right: 0; 18 | border-left: 0; 19 | border-bottom: solid 2px var(--body-text); 20 | border-radius: 0; 21 | padding: 0 0 1.5em; 22 | font-size: 1rem; 23 | font-weight: 600; 24 | font-family: $font-family-sans-serif; 25 | background: transparent; 26 | appearance: none; 27 | 28 | &:focus { 29 | outline: none; 30 | border-bottom: solid 2px var(--accent); 31 | } 32 | } 33 | 34 | input[type="submit"] { 35 | cursor: pointer; 36 | } 37 | 38 | .form { 39 | max-width: rem(354); 40 | width: calc(100% - 2rem); 41 | 42 | ::placeholder { 43 | color: var(--black); 44 | } 45 | 46 | &__notice { 47 | margin-bottom: 2rem; 48 | font-size: 0.875rem; 49 | font-weight: $font-weight-bold; 50 | font-family: $font-family-sans-serif; 51 | text-transform: uppercase; 52 | text-align: center; 53 | 54 | &--error { 55 | color: var(--error); 56 | } 57 | 58 | &--success { 59 | color: var(--success); 60 | } 61 | } 62 | 63 | &__row { 64 | display: flex; 65 | flex-direction: row; 66 | align-items: center; 67 | justify-content: center; 68 | width: 100%; 69 | position: relative; 70 | margin-bottom: 2rem; 71 | 72 | label { 73 | position: absolute; 74 | left: 0; 75 | top: 0; 76 | transition: 0.2s; 77 | line-height: 1; 78 | } 79 | 80 | input, 81 | textarea { 82 | &:focus, 83 | &:valid, 84 | &.error { 85 | + label { 86 | top: 100%; 87 | margin-top: -1.125rem; 88 | font-size: 0.75rem; 89 | } 90 | } 91 | 92 | &.error { 93 | border-bottom: solid 2px var(--error); 94 | 95 | + label { 96 | color: var(--error); 97 | } 98 | } 99 | } 100 | 101 | &:last-child { 102 | margin-top: rem(48); 103 | } 104 | } 105 | } 106 | 107 | // Contact form 108 | 109 | #page .contact { 110 | display: flex; 111 | flex-direction: column; 112 | justify-content: center; 113 | align-items: center; 114 | width: 100%; 115 | padding: rem(65) 0 rem(120); 116 | background: var(--body-bg-alt); 117 | 118 | h2 { 119 | margin-bottom: rem(40); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /assets/styles/editor.scss: -------------------------------------------------------------------------------- 1 | @import '~aetna/assets/styles/common/variables'; 2 | @import '~aetna/assets/styles/common/global'; 3 | 4 | body#tinymce { 5 | margin: 12px !important; 6 | /* stylelint-disable no-invalid-position-at-import-rule */ 7 | @import '~aetna/assets/styles/components/buttons'; 8 | @import '~aetna/assets/styles/layouts/page-sections'; 9 | /* stylelint-enable */ 10 | } 11 | -------------------------------------------------------------------------------- /assets/styles/layouts/_footer.scss: -------------------------------------------------------------------------------- 1 | // TODO 2 | .footer__network { 3 | --primary: --accent-fg; 4 | } 5 | -------------------------------------------------------------------------------- /assets/styles/layouts/_header.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | background-position: bottom; 3 | background-repeat: no-repeat; 4 | background-size: cover; 5 | } 6 | 7 | button.header__nav-icon { 8 | appearance: none; 9 | background: transparent; 10 | border-color: transparent; 11 | border-radius: 0; 12 | color: transparent; 13 | padding: 0; 14 | } 15 | 16 | .home .header { 17 | background-position: bottom; 18 | background-repeat: no-repeat; 19 | background-size: cover; 20 | height: 15rem; 21 | 22 | .header__inside { 23 | padding-top: 1.5rem; 24 | } 25 | 26 | @media #{$breakpoint-large} { 27 | height: 55rem; 28 | 29 | .header__inside { 30 | position: relative; 31 | z-index: 99; 32 | } 33 | } 34 | } 35 | 36 | .home #content{ 37 | margin-top: -13rem; 38 | background: transparent; 39 | 40 | &.custom-homepage { 41 | margin-top: 0; 42 | } 43 | @media #{$breakpoint-large} { 44 | margin-top: -53rem; 45 | 46 | &.custom-homepage { 47 | margin-top: 0; 48 | } 49 | } 50 | } 51 | 52 | .home .entry-header { 53 | display: flex; 54 | flex-direction: column; 55 | justify-content: center; 56 | height: 15rem; 57 | 58 | @media #{$breakpoint-large} { 59 | height: 10rem; 60 | margin: 5rem 0 0; 61 | } 62 | 63 | .entry-title { 64 | font-family: $font-family-sans-serif; 65 | font-size: 1.875rem; 66 | margin-top: 1.5em; 67 | margin-bottom: 0; 68 | text-align: center; 69 | max-width: 100%; 70 | 71 | @media #{$breakpoint-large} { 72 | font-size: 3.25rem; 73 | line-height: 1.25em; 74 | } 75 | } 76 | 77 | .entry-description { 78 | font-family: $font-family-sans-serif; 79 | font-size: 1.125rem; 80 | @media #{$breakpoint-large} { 81 | font-size: 2rem; 82 | } 83 | 84 | margin-top: 0; 85 | margin-bottom: 0; 86 | text-align: center; 87 | max-width: 100%; 88 | } 89 | } 90 | 91 | .page .header { 92 | height: 15rem; 93 | 94 | @media #{$breakpoint-large} { 95 | height: 55rem; 96 | } 97 | } 98 | 99 | .page.page-template-page-catalog { 100 | .header { 101 | height: rem(381); 102 | } 103 | 104 | .page-header { 105 | display: flex; 106 | flex-direction: column; 107 | justify-content: center; 108 | height: rem(380); 109 | margin-top: rem(-380); 110 | } 111 | 112 | @media #{$breakpoint-large} { 113 | .header { 114 | height: rem(450); 115 | } 116 | 117 | .page-header { 118 | height: rem(381); 119 | margin-top: rem(-381); 120 | 121 | .font-size & { 122 | height: rem(350); 123 | margin-top: rem(-350); 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /assets/styles/layouts/_page-catalog.scss: -------------------------------------------------------------------------------- 1 | // stylelint-disable no-descending-specificity 2 | 3 | .network-catalog fieldset { 4 | border-top: solid 2px var(--accent); 5 | font-family: $font-family-sans-serif; 6 | 7 | h2, 8 | h3 { 9 | margin-bottom: 0; 10 | font-size: 1rem; 11 | font-weight: bold; 12 | text-align: left; 13 | text-transform: none; 14 | 15 | &::before { 16 | display: none; 17 | } 18 | 19 | button { 20 | display: flex; 21 | flex-direction: row; 22 | justify-content: space-between; 23 | all: inherit; 24 | width: 100%; 25 | padding: 1rem 1.1875rem; 26 | margin: 0; 27 | border-top: 0; 28 | 29 | svg { 30 | display: block; 31 | float: right; 32 | margin-top: 0.5rem; 33 | } 34 | 35 | &:hover, 36 | &:focus { 37 | color: var(--primary); 38 | background: var(--bg-body); 39 | } 40 | 41 | &:active { 42 | box-shadow: none; 43 | } 44 | } 45 | } 46 | 47 | [aria-expanded="true"] { 48 | color: var(--primary); 49 | 50 | svg { 51 | transform: rotate(180deg); 52 | } 53 | } 54 | 55 | h2 { 56 | [aria-expanded="true"] { 57 | border-bottom: solid 2px var(--accent); 58 | background: var(--body-bg-alt); 59 | } 60 | } 61 | 62 | [type="radio"] { 63 | position: absolute !important; 64 | width: 1px !important; 65 | height: 1px !important; 66 | padding: 0 !important; 67 | border: 0 !important; 68 | overflow: hidden !important; 69 | clip: rect(1px, 1px, 1px, 1px); 70 | } 71 | 72 | .label { 73 | display: inline-block; 74 | width: calc(100% - 2rem); 75 | } 76 | 77 | [type="radio"] + label { 78 | cursor: pointer; 79 | display: block; 80 | padding: 1rem 1.1875rem; 81 | margin-bottom: 0; 82 | 83 | svg { 84 | display: none; 85 | } 86 | } 87 | 88 | [type="radio"]:focus { 89 | label { 90 | cursor: pointer; 91 | display: block; 92 | } 93 | } 94 | 95 | [type="radio"]:checked + label { 96 | color: var(--primary); 97 | font-weight: bold; 98 | 99 | svg { 100 | display: block; 101 | float: right; 102 | margin-top: 0.5rem; 103 | width: 1rem; 104 | height: 1rem; 105 | fill: transparent; 106 | } 107 | } 108 | 109 | &:last-of-type { 110 | border-bottom: solid 2px var(--accent); 111 | margin-bottom: 1rem; 112 | } 113 | } 114 | 115 | .js .filter-sort [type="submit"] { 116 | display: none; 117 | } 118 | 119 | .clear-filters { 120 | width: calc(100% - 2rem); 121 | margin: 0 1rem; 122 | } 123 | 124 | .page-template-page-catalog { 125 | .books { 126 | width: calc(100% - 1rem); 127 | padding: 0; 128 | margin: 2rem 0 1rem 1rem; 129 | } 130 | 131 | .book, .featured_book { 132 | height: 14.375rem; 133 | width: calc(50% - 1rem); 134 | margin: 0 1rem 1rem 0; 135 | } 136 | } 137 | 138 | .catalog-navigation { 139 | display: flex; 140 | flex-direction: row; 141 | justify-content: center; 142 | width: 100%; 143 | margin-top: rem(40); 144 | margin-bottom: 2rem; 145 | align-items: baseline; 146 | font-family: $font-family-sans-serif; 147 | 148 | a { 149 | color: var(--body-text); 150 | 151 | &:hover, 152 | &:focus { 153 | color: var(--primary); 154 | } 155 | } 156 | 157 | .previous, 158 | .next { 159 | display: block; 160 | margin: 0 rem(26); 161 | font-family: $font-family-sans-serif; 162 | font-size: rem(16); 163 | 164 | svg { 165 | width: 1.2rem; 166 | height: 1rem; 167 | margin: 0.25rem rem(6) 0; 168 | 169 | path { 170 | fill: var(--primary); 171 | } 172 | } 173 | } 174 | 175 | .pages { 176 | display: inline-block; 177 | border-bottom: solid 2px #ececec; 178 | 179 | a, 180 | span { 181 | display: inline-block; 182 | width: rem(41); 183 | padding: rem(8) 0; 184 | font-size: rem(24); 185 | text-align: center; 186 | } 187 | 188 | .current { 189 | border-bottom: solid rem(6) var(--primary); 190 | } 191 | } 192 | } 193 | 194 | @media #{$breakpoint-medium} { 195 | .filter-sort { 196 | width: calc(100% - 1rem); 197 | height: 7rem; 198 | margin: 2rem 0 1rem 1rem; 199 | } 200 | 201 | fieldset { 202 | width: calc(100% / 3 - 1rem); 203 | margin: 0 1rem 1rem 0; 204 | float: left; 205 | border-top: 0; 206 | position: relative; 207 | 208 | &:last-of-type { 209 | border-bottom: 0; 210 | } 211 | } 212 | 213 | fieldset h2 button { 214 | border-bottom: solid 2px var(--accent); 215 | } 216 | 217 | fieldset div:not([hidden]) { 218 | position: absolute; 219 | width: 100%; 220 | background: var(--body-bg); 221 | z-index: 99; 222 | border-right: rem(1) solid #ececec; 223 | border-left: rem(1) solid #ececec; 224 | border-bottom: rem(1) solid #ececec; 225 | 226 | .subject-groups { 227 | width: calc(100% + 0.125rem); 228 | margin-left: -0.0612rem; 229 | 230 | div { 231 | position: relative; 232 | width: calc(100% + 0.125rem); 233 | margin-left: -0.0612rem; 234 | border-bottom: 0; 235 | } 236 | } 237 | } 238 | 239 | .clear-filters { 240 | width: calc(100% / 3 - 1rem); 241 | margin-left: 0; 242 | } 243 | 244 | .page-template-page-catalog .book { 245 | height: 16.25rem; 246 | width: calc(100% / 3 - 1rem); 247 | margin: 0 1rem 1rem 0; 248 | } 249 | } 250 | 251 | @media #{$breakpoint-large} { 252 | .page-template-page-catalog #content { 253 | width: calc(100% - 1rem); 254 | margin-left: auto; 255 | margin-right: auto; 256 | max-width: rem(1650); 257 | background: transparent; 258 | } 259 | 260 | .filter-sort { 261 | width: calc(25% - 2rem); 262 | height: auto; 263 | float: left; 264 | margin: 2rem 1rem 1rem; 265 | } 266 | 267 | fieldset { 268 | width: 100%; 269 | display: block; 270 | margin-bottom: 0; 271 | } 272 | 273 | fieldset div:not([hidden]) { 274 | position: relative; 275 | border: 0; 276 | } 277 | 278 | .clear-filters { 279 | width: 100%; 280 | margin-left: 0; 281 | } 282 | 283 | .page-template-page-catalog .books { 284 | width: 75%; 285 | float: right; 286 | margin-left: 0; 287 | } 288 | 289 | .page-template-page-catalog .book { 290 | height: 16.25rem; 291 | max-width: calc(100% / 3 - 1rem); 292 | margin: 0 1rem 1rem 0; 293 | } 294 | 295 | .catalog-navigation { 296 | width: 75%; 297 | float: right; 298 | padding-right: 1rem; 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /assets/styles/layouts/_page-home.scss: -------------------------------------------------------------------------------- 1 | .home #content { 2 | width: 100%; 3 | 4 | @media #{$breakpoint-extra-large} { 5 | max-width: 1440px; 6 | } 7 | } 8 | 9 | .latest-books { 10 | margin: 4rem 0 8rem; 11 | 12 | .slider { 13 | width: 100%; 14 | max-width: 22.9375rem; 15 | margin: 0 auto; 16 | position: relative; 17 | } 18 | 19 | .books { 20 | position: relative; 21 | display: flex; 22 | width: calc(100% - 4rem); 23 | margin: 0 auto; 24 | flex-direction: column; 25 | align-items: center; 26 | padding-left: 0; 27 | z-index: 99; 28 | } 29 | 30 | .booknav { 31 | position: absolute; 32 | margin-top: 0; 33 | top: 0; 34 | left: 0; 35 | width: 100%; 36 | max-width: 28.0625rem; 37 | height: 100%; 38 | display: flex; 39 | flex-direction: row; 40 | justify-content: center; 41 | } 42 | 43 | .previous, 44 | .next { 45 | display: flex; 46 | flex-direction: column; 47 | justify-content: center; 48 | position: absolute; 49 | width: rem(25); 50 | height: 100%; 51 | margin-top: 0; 52 | 53 | svg { 54 | width: rem(25); 55 | height: rem(25); 56 | margin-top: 0; 57 | 58 | path { 59 | fill: var(--primary); 60 | } 61 | } 62 | } 63 | 64 | .previous { 65 | left: 0; 66 | } 67 | 68 | .next { 69 | right: 0; 70 | } 71 | 72 | @media #{$breakpoint-large} { 73 | .slider { 74 | width: calc(100vw - 5.125rem); 75 | max-width: 75rem; 76 | } 77 | 78 | .books { 79 | flex-direction: row; 80 | justify-content: center; 81 | 82 | .book, .featured_book { 83 | margin-right: 1rem; 84 | margin-left: 1rem; 85 | } 86 | } 87 | 88 | .booknav { 89 | width: calc(100% + 4rem); 90 | max-width: 100vw; 91 | left: -2rem; 92 | } 93 | 94 | .previous, 95 | .next { 96 | width: rem(32); 97 | 98 | svg { 99 | width: rem(32); 100 | height: rem(32); 101 | } 102 | } 103 | } 104 | 105 | .catalog-link { 106 | text-align: center; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /assets/styles/layouts/_page.scss: -------------------------------------------------------------------------------- 1 | body { 2 | display: flex; 3 | min-height: 100vh; 4 | flex-direction: column; 5 | 6 | #page { 7 | flex: 1; 8 | display: flex; 9 | flex-direction: column; 10 | } 11 | 12 | #content { 13 | flex-grow: 1; 14 | 15 | .entry-content a { 16 | text-decoration: underline; 17 | } 18 | 19 | a.call-to-action { 20 | text-decoration: none; 21 | } 22 | } 23 | } 24 | 25 | .page article { 26 | margin-top: 0; 27 | } 28 | -------------------------------------------------------------------------------- /assets/styles/layouts/_signup.scss: -------------------------------------------------------------------------------- 1 | .wp-activate-container, 2 | .mu_register.wp-signup-container { 3 | @extend .page-section; 4 | 5 | margin: rem(120) auto 4rem; 6 | 7 | * { 8 | text-align: left; 9 | } 10 | 11 | h2 { 12 | text-align: center; 13 | order: -1; 14 | } 15 | 16 | .mu_alert { 17 | margin-bottom: 2rem; 18 | } 19 | 20 | form { 21 | margin-top: 0; 22 | } 23 | 24 | input:not([type="submit"]) { 25 | padding: 0; 26 | } 27 | 28 | .checkbox { 29 | margin-right: 1em; 30 | } 31 | 32 | .submit { 33 | text-align: center; 34 | } 35 | 36 | [type="submit"] { 37 | width: auto; 38 | margin: 0 auto; 39 | text-align: center; 40 | } 41 | 42 | ol, 43 | ul { 44 | list-style-position: outside; 45 | } 46 | 47 | span.h3 { 48 | padding: 0; 49 | font-size: 1rem; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /assets/styles/search-featured-books.scss: -------------------------------------------------------------------------------- 1 | .search-books-results { 2 | position: absolute; 3 | background: #fff; 4 | z-index: 1000; 5 | list-style: none; 6 | padding: 0; 7 | margin: 0; 8 | max-height: 200px; 9 | overflow-y: auto; 10 | border: 1px solid #ccc; 11 | } 12 | 13 | .search-books-results li { 14 | padding: 4px 8px; 15 | cursor: pointer; 16 | } 17 | 18 | .search-books-results li:hover { 19 | background: #f0f0f0; 20 | } 21 | 22 | .search-books-field { 23 | position: relative; 24 | display: inline-block; 25 | width: 100%; 26 | } 27 | 28 | .search-books-field .search-books-input { 29 | width: 100%; 30 | padding-right: 2rem; 31 | } 32 | 33 | .search-books-field .search-books-icon { 34 | position: absolute; 35 | top: 50%; 36 | right: 0.5rem; 37 | transform: translateY(-50%); 38 | pointer-events: none; 39 | } 40 | 41 | /* stylelint-disable selector-id-pattern */ 42 | #sub-accordion-section-pb_front_page_catalog, 43 | #customize-theme-controls { 44 | overflow: visible !important; 45 | height: auto !important; 46 | } 47 | /* stylelint-enable selector-id-pattern */ -------------------------------------------------------------------------------- /bin/i18n.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 3 | PARENT="$(dirname $DIR)" 4 | tx push -s 5 | tx pull -a --minimum-perc=25 6 | for file in $PARENT/languages/*.po ; do msgfmt $file -o `echo $file | sed 's/\(.*\.\)po/\1mo/'` ; done 7 | -------------------------------------------------------------------------------- /bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" 5 | exit 1 6 | fi 7 | 8 | DB_NAME=$1 9 | DB_USER=$2 10 | DB_PASS=$3 11 | DB_HOST=${4-localhost} 12 | WP_VERSION=${5-latest} 13 | SKIP_DB_CREATE=${6-false} 14 | 15 | TMPDIR=${TMPDIR-/tmp} 16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") 17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} 18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} 19 | 20 | download() { 21 | if [ `which curl` ]; then 22 | curl -s "$1" > "$2"; 23 | elif [ `which wget` ]; then 24 | wget -nv -O "$2" "$1" 25 | fi 26 | } 27 | 28 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then 29 | WP_TESTS_TAG="branches/$WP_VERSION" 30 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then 31 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 32 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 33 | WP_TESTS_TAG="tags/${WP_VERSION%??}" 34 | else 35 | WP_TESTS_TAG="tags/$WP_VERSION" 36 | fi 37 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 38 | WP_TESTS_TAG="trunk" 39 | else 40 | # http serves a single offer, whereas https serves multiple. we only want one 41 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 42 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 43 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 44 | if [[ -z "$LATEST_VERSION" ]]; then 45 | echo "Latest WordPress version could not be found" 46 | exit 1 47 | fi 48 | WP_TESTS_TAG="tags/$LATEST_VERSION" 49 | fi 50 | 51 | set -ex 52 | 53 | install_wp() { 54 | 55 | if [ -d $WP_CORE_DIR ]; then 56 | return; 57 | fi 58 | 59 | mkdir -p $WP_CORE_DIR 60 | 61 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 62 | mkdir -p $TMPDIR/wordpress-nightly 63 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip 64 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ 65 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR 66 | else 67 | if [ $WP_VERSION == 'latest' ]; then 68 | local ARCHIVE_NAME='latest' 69 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then 70 | # https serves multiple offers, whereas http serves single. 71 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json 72 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 73 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 74 | LATEST_VERSION=${WP_VERSION%??} 75 | else 76 | # otherwise, scan the releases and get the most up to date minor version of the major release 77 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` 78 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) 79 | fi 80 | if [[ -z "$LATEST_VERSION" ]]; then 81 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 82 | else 83 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION" 84 | fi 85 | else 86 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 87 | fi 88 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz 89 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR 90 | fi 91 | 92 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 93 | } 94 | 95 | install_pressbooks() { 96 | if [ -d $WP_CORE_DIR/wp-content/plugins/pressbooks ]; then 97 | cd $WP_CORE_DIR/wp-content/plugins/pressbooks && git pull 98 | return; 99 | fi 100 | 101 | git clone --depth=1 https://github.com/pressbooks/pressbooks.git $WP_CORE_DIR/wp-content/plugins/pressbooks 102 | cd $WP_CORE_DIR/wp-content/plugins/pressbooks && composer install --no-dev --optimize-autoloader 103 | } 104 | 105 | install_test_suite() { 106 | # portable in-place argument for both GNU sed and Mac OSX sed 107 | if [[ $(uname -s) == 'Darwin' ]]; then 108 | local ioption='-i .bak' 109 | else 110 | local ioption='-i' 111 | fi 112 | 113 | # set up testing suite if it doesn't yet exist 114 | if [ ! -d $WP_TESTS_DIR ]; then 115 | # set up testing suite 116 | mkdir -p $WP_TESTS_DIR 117 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 118 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data 119 | fi 120 | 121 | if [ ! -f wp-tests-config.php ]; then 122 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 123 | # remove all forward slashes in the end 124 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 125 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 126 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 127 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 128 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 129 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 130 | fi 131 | 132 | } 133 | 134 | install_db() { 135 | 136 | if [ ${SKIP_DB_CREATE} = "true" ]; then 137 | return 0 138 | fi 139 | 140 | # parse DB_HOST for port or socket references 141 | local PARTS=(${DB_HOST//\:/ }) 142 | local DB_HOSTNAME=${PARTS[0]}; 143 | local DB_SOCK_OR_PORT=${PARTS[1]}; 144 | local EXTRA="" 145 | 146 | if ! [ -z $DB_HOSTNAME ] ; then 147 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 148 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 149 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 150 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 151 | elif ! [ -z $DB_HOSTNAME ] ; then 152 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 153 | fi 154 | fi 155 | 156 | # create database 157 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 158 | } 159 | 160 | install_wp 161 | install_pressbooks 162 | install_test_suite 163 | install_db 164 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | parsers: 16 | gcov: 17 | branch_detection: 18 | conditional: yes 19 | loop: yes 20 | method: no 21 | macro: no 22 | 23 | comment: 24 | layout: "header, diff" 25 | behavior: default 26 | require_changes: no 27 | -------------------------------------------------------------------------------- /comments.php: -------------------------------------------------------------------------------- 1 | 22 | 23 |
    24 | 25 | 29 |

    30 | ' . get_the_title() . '' 37 | ); 38 | } else { 39 | printf( 40 | /* translators: 1: comment count number, 2: title. */ 41 | esc_html( _nx( '%1$s thought on “%2$s”', '%1$s thoughts on “%2$s”', $comment_count, 'comments title', 'pressbooks-aldine' ) ), 42 | number_format_i18n( $comment_count ), 43 | '' . get_the_title() . '' 44 | ); 45 | } 46 | ?> 47 |

    48 | 49 | 50 | 51 |
      52 | 'ol', 56 | 'short_ping' => true, 57 | ] 58 | ); 59 | ?> 60 |
    61 | 62 | 68 |

    69 | 76 | 77 |
    78 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pressbooks/pressbooks-aldine", 3 | "type": "wordpress-theme", 4 | "license": "GPL-3.0-or-later", 5 | "description": "Aldine is the default theme for the home page of Pressbooks networks. It is named for the Aldine Press, founded by Aldus Manutius in 1494, who is regarded by many as the world’s first publisher.", 6 | "homepage": "https://github.com/pressbooks/pressbooks-aldine/", 7 | "authors": [ 8 | { 9 | "name": "Pressbooks (Book Oven Inc.)", 10 | "email": "code@pressbooks.com", 11 | "homepage": "https://pressbooks.org" 12 | } 13 | ], 14 | "keywords": [ 15 | "publishing", 16 | "catalog", 17 | "pressbooks", 18 | "default-theme" 19 | ], 20 | "support": { 21 | "issues": "https://github.com/pressbooks/pressbooks-aldine/issues", 22 | "forum": "https://discourse.pressbooks.org/" 23 | }, 24 | "config": { 25 | "sort-packages": true, 26 | "allow-plugins": { 27 | "composer/installers": true, 28 | "dealerdirect/phpcodesniffer-composer-installer": true 29 | } 30 | }, 31 | "require": { 32 | "php": "^8.1", 33 | "composer/installers": "^2", 34 | "phpcompatibility/php-compatibility": "^9.3", 35 | "pressbooks/mix": "^2.1", 36 | "spatie/color": "^1.1" 37 | }, 38 | "require-dev": { 39 | "pressbooks/coding-standards": "^1.1", 40 | "yoast/phpunit-polyfills": "^1.1" 41 | }, 42 | "scripts": { 43 | "test": [ 44 | "vendor/bin/phpunit --configuration phpunit.xml" 45 | ], 46 | "test-coverage": [ 47 | "vendor/bin/phpunit --configuration phpunit.xml --coverage-clover coverage.xml --coverage-html=./coverage-reports" 48 | ], 49 | "standards": [ 50 | "vendor/bin/phpcs --standard=phpcs.ruleset.xml inc partials *.php" 51 | ], 52 | "fix": [ 53 | "vendor/bin/phpcbf --standard=phpcs.ruleset.xml inc partials *.php" 54 | ], 55 | "localize": [ 56 | "wp-pot -o=languages/pressbooks-aldine.pot -d=pressbooks-aldine -t 'Pressbooks (Book Oven Inc.) ' -s '**/*.php'" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /dist/fonts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/dist/fonts/.gitkeep -------------------------------------------------------------------------------- /dist/fonts/pressbooks-theme.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/dist/fonts/pressbooks-theme.woff -------------------------------------------------------------------------------- /dist/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/dist/images/.gitkeep -------------------------------------------------------------------------------- /dist/images/catalog-header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/dist/images/catalog-header.jpg -------------------------------------------------------------------------------- /dist/images/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/dist/images/header.jpg -------------------------------------------------------------------------------- /dist/images/logo.svg: -------------------------------------------------------------------------------- 1 | Pressbooks 2 | -------------------------------------------------------------------------------- /dist/images/pb.svg: -------------------------------------------------------------------------------- 1 | PB 2 | -------------------------------------------------------------------------------- /dist/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/scripts/aldine.js": "/scripts/aldine.js?id=de4012397a80da792d3c8b59ba2a9a68", 3 | "/scripts/call-to-action.js": "/scripts/call-to-action.js?id=33370b66c7af12320fc0e1250f6be399", 4 | "/scripts/catalog-admin.js": "/scripts/catalog-admin.js?id=3f55657b127eb8e1414db48701a5776b", 5 | "/scripts/customizer.js": "/scripts/customizer.js?id=14dca3944228dd789c27c772d55bf471", 6 | "/scripts/customizer-toggle.js": "/scripts/customizer-toggle.js?id=c31594589675d7c5662aaddcf7a9669a", 7 | "/scripts/page-section.js": "/scripts/page-section.js?id=19d5c30146ea1a763bcf2bd733a75e77", 8 | "/scripts/search-featured-books.js": "/scripts/search-featured-books.js?id=65f3fdc7e5ad5d74a913d6a1d71c716c", 9 | "/styles/search-featured-books.css": "/styles/search-featured-books.css?id=1ad889c456b5443725c5b7629504efea", 10 | "/styles/editor.css": "/styles/editor.css?id=699cda61d4bd0efc921b5234ac55d24b", 11 | "/styles/aldine.css": "/styles/aldine.css?id=02dccb4603b47ffaf3ba0b8df7a354bb", 12 | "/fonts/pressbooks-theme.woff": "/fonts/pressbooks-theme.woff?id=2a7aae81673f4707bbe78c8f12b72b64", 13 | "/images/catalog-header.jpg": "/images/catalog-header.jpg?id=223b9f7a23985f2a72df72e4e4a2ffdc", 14 | "/images/header.jpg": "/images/header.jpg?id=c6712212b6aa749cf1cf1f077c679f9f", 15 | "/images/logo.svg": "/images/logo.svg?id=d71cb98d33ef823ffd27b15ae07e723a", 16 | "/images/pb.svg": "/images/pb.svg?id=c08fb158c15a470648a74591e1fc94a4" 17 | } 18 | -------------------------------------------------------------------------------- /dist/scripts/aldine.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Isotope v3.0.6 3 | * 4 | * Licensed GPLv3 for open source use 5 | * or Isotope Commercial License for commercial use 6 | * 7 | * https://isotope.metafizzy.co 8 | * Copyright 2010-2018 Metafizzy 9 | */ 10 | 11 | /*! 12 | * Masonry layout mode 13 | * sub-classes Masonry 14 | * https://masonry.desandro.com 15 | */ 16 | 17 | /*! 18 | * Masonry v4.2.2 19 | * Cascading grid layout library 20 | * https://masonry.desandro.com 21 | * MIT License 22 | * by David DeSandro 23 | */ 24 | 25 | /*! 26 | * Outlayer v2.1.1 27 | * the brains and guts of a layout library 28 | * MIT license 29 | */ 30 | 31 | /*! 32 | * getSize v2.0.3 33 | * measure size of elements 34 | * MIT license 35 | */ 36 | 37 | /*! 38 | * jQuery JavaScript Library v3.7.1 39 | * https://jquery.com/ 40 | * 41 | * Copyright OpenJS Foundation and other contributors 42 | * Released under the MIT license 43 | * https://jquery.org/license 44 | * 45 | * Date: 2023-08-28T13:37Z 46 | */ 47 | -------------------------------------------------------------------------------- /dist/scripts/call-to-action.js: -------------------------------------------------------------------------------- 1 | tinymce.create("tinymce.plugins.aldine_call_to_action",{init:function(t,n){t.addButton("aldine_call_to_action",{title:aldine.call_to_action.title,icon:"icon dashicons-flag",onclick:function(){t.windowManager.open({title:aldine.call_to_action.title,body:[{type:"textbox",name:"text",label:aldine.call_to_action.text,value:aldine.call_to_action.title},{type:"textbox",name:"link",label:aldine.call_to_action.link,value:"#"}],onsubmit:function(n){t.insertContent('[aldine_call_to_action text="'+n.data.text+'" link="'+n.data.link+'"]')}})}})},createControl:function(t,n){return null}}),tinymce.PluginManager.add("aldine_call_to_action",tinymce.plugins.aldine_call_to_action); -------------------------------------------------------------------------------- /dist/scripts/catalog-admin.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v3.7.1 3 | * https://jquery.com/ 4 | * 5 | * Copyright OpenJS Foundation and other contributors 6 | * Released under the MIT license 7 | * https://jquery.org/license 8 | * 9 | * Date: 2023-08-28T13:37Z 10 | */ 11 | -------------------------------------------------------------------------------- /dist/scripts/customizer-toggle.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded",(function(){var t=document.getElementById("_customize-input-pb_network_contact_form"),e=document.getElementById("customize-control-pb_network_contact_email"),s=document.getElementById("customize-control-pb_network_contact_link"),n=document.getElementById("customize-control-pb_network_contact_form_title");function c(){!1===t.checked?(e.classList.add("hidden"),e.style.cssText=null,n.classList.add("hidden"),n.style.cssText=null,s.classList.remove("hidden"),s.style.cssText="display: list-item;"):(e.classList.remove("hidden"),e.style.cssText="display: list-item;",n.classList.remove("hidden"),n.style.cssText="display: list-item;",s.classList.add("hidden"),s.style.cssText=null)}t.addEventListener("click",c),c()})); -------------------------------------------------------------------------------- /dist/scripts/customizer.js: -------------------------------------------------------------------------------- 1 | wp.customize("blogname",(function(t){t.bind((function(t){return document.querySelector(".home .entry-title").textContent=t}))})),wp.customize("blogdescription",(function(t){t.bind((function(t){return document.querySelector(".home .entry-description").textContent=t}))})); -------------------------------------------------------------------------------- /dist/scripts/page-section.js: -------------------------------------------------------------------------------- 1 | tinymce.create("tinymce.plugins.aldine_page_section",{init:function(e,t){e.addButton("aldine_page_section",{title:aldine.page_section.title,icon:"icon dashicons-layout",onclick:function(){e.windowManager.open({title:aldine.page_section.title,body:[{type:"textbox",name:"title",label:aldine.page_section.title_label,value:aldine.page_section.title},{type:"listbox",name:"variant",label:"Variant",values:[{text:aldine.page_section.standard,value:""},{text:aldine.page_section.accent,value:"accent"},{text:aldine.page_section.bordered,value:"bordered"},{text:aldine.page_section.borderless,value:"borderless"}],value:""}],onsubmit:function(t){e.insertContent('[aldine_page_section title="'+t.data.title+'" variant="'+t.data.variant+'"]

    Insert your page section content here.

    [/aldine_page_section]')}})}})},createControl:function(e,t){return null}}),tinymce.PluginManager.add("aldine_page_section",tinymce.plugins.aldine_page_section); -------------------------------------------------------------------------------- /dist/scripts/search-featured-books.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v3.7.1 3 | * https://jquery.com/ 4 | * 5 | * Copyright OpenJS Foundation and other contributors 6 | * Released under the MIT license 7 | * https://jquery.org/license 8 | * 9 | * Date: 2023-08-28T13:37Z 10 | */ 11 | -------------------------------------------------------------------------------- /dist/scripts/shortcodes.js: -------------------------------------------------------------------------------- 1 | !function(t){var e={};function n(a){if(e[a])return e[a].exports;var i=e[a]={i:a,l:!1,exports:{}};return t[a].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=t,n.c=e,n.d=function(t,e,a){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:a})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=3)}({3:function(t,e,n){t.exports=n("GR5A")},GR5A:function(t,e){tinymce.create("tinymce.plugins.aldine_page_section",{init:function(t,e){t.addButton("aldine_page_section",{title:"Page Section",icon:"layout",onclick:function(){t.windowManager.open({title:"Page Section",body:[{type:"textbox",name:"title",label:"Title",value:"Page Section"},{type:"listbox",name:"variant",label:"Variant",values:[{text:"Standard",value:""},{text:"Accent",value:"accent"},{text:"Bordered",value:"bordered"},{text:"Borderless",value:"borderless"}],value:""}],onsubmit:function(e){t.insertContent('[aldine_page_section title="'+e.data.title+'" variant="'+e.data.variant+'"][/aldine_page_section]')}})}})},createControl:function(t,e){return null}}),tinymce.create("tinymce.plugins.aldine_call_to_action",{init:function(t,e){t.addButton("aldine_call_to_action",{title:"Call to Action",icon:"link",onclick:function(){t.windowManager.open({title:"Call to Action",body:[{type:"textbox",name:"text",label:"Text",value:"Call to Action"},{type:"textbox",name:"url",label:"URL",value:"#"}],onsubmit:function(e){t.insertContent('[aldine_call_to_action text="'+e.data.text+'" url="'+e.data.url+'"]')}})}})},createControl:function(t,e){return null}}),tinymce.PluginManager.add("aldine_call_to_action",tinymce.plugins.aldine_call_to_action)}}); -------------------------------------------------------------------------------- /dist/styles/editor.css: -------------------------------------------------------------------------------- 1 | :root{--primary:#b01109;--primary-dark:#7f0c07;--primary-alpha:rgba(176,17,9,.25);--primary-fg:#fff;--accent:#015d75;--accent-alpha:rgba(1,93,117,.25);--accent-dark:#013542;--accent-fg:#fff;--success:#070;--error:#d00;--warning:#e90;--body-bg:#fff;--body-bg-alt:#f6f6f6;--body-text:#222;--header-bg:#fff;--header-text:#222;--footer-bg:#444;--footer-text:#fff}body#tinymce{margin:12px!important}body#tinymce .button,body#tinymce a.call-to-action,body#tinymce button,body#tinymce input[type=submit]{border-radius:3px;border-style:solid;border-width:2px;display:inline-block;font-family:Karla,sans-serif;font-weight:600;line-height:1.5;padding:.875rem 3.25rem;text-align:center;text-decoration:none;text-transform:uppercase;vertical-align:middle}body#tinymce .button.focus,body#tinymce .button:focus,body#tinymce .button:hover,body#tinymce a.call-to-action.focus,body#tinymce a.call-to-action:focus,body#tinymce a.call-to-action:hover,body#tinymce button.focus,body#tinymce button:focus,body#tinymce button:hover,body#tinymce input[type=submit].focus,body#tinymce input[type=submit]:focus,body#tinymce input[type=submit]:hover{text-decoration:none}body#tinymce .button.focus,body#tinymce .button:focus,body#tinymce a.call-to-action.focus,body#tinymce a.call-to-action:focus,body#tinymce button.focus,body#tinymce button:focus,body#tinymce input[type=submit].focus,body#tinymce input[type=submit]:focus{outline:0}body#tinymce .button.disabled,body#tinymce .button:disabled,body#tinymce a.call-to-action.disabled,body#tinymce a.call-to-action:disabled,body#tinymce button.disabled,body#tinymce button:disabled,body#tinymce input[type=submit].disabled,body#tinymce input[type=submit]:disabled{-webkit-box-shadow:none;box-shadow:none;opacity:.65}body#tinymce .button.active,body#tinymce .button:active,body#tinymce a.call-to-action.active,body#tinymce a.call-to-action:active,body#tinymce button.active,body#tinymce button:active,body#tinymce input[type=submit].active,body#tinymce input[type=submit]:active{background-image:none;-webkit-box-shadow:0 0 0 3px var(--primary-alpha),inset 0 3px 5px rgba(0,0,0,.125);box-shadow:0 0 0 3px var(--primary-alpha),inset 0 3px 5px rgba(0,0,0,.125)}body#tinymce .button,body#tinymce button,body#tinymce input[type=submit]{background-color:var(--primary);border-color:var(--primary);color:var(--primary-fg)}body#tinymce .button.focus,body#tinymce .button:focus,body#tinymce .button:hover,body#tinymce button.focus,body#tinymce button:focus,body#tinymce button:hover,body#tinymce input[type=submit].focus,body#tinymce input[type=submit]:focus,body#tinymce input[type=submit]:hover{background-color:var(--primary-dark);border-color:var(--primary-dark);color:var(--primary-fg)}body#tinymce .button.focus,body#tinymce .button:focus,body#tinymce button.focus,body#tinymce button:focus,body#tinymce input[type=submit].focus,body#tinymce input[type=submit]:focus{-webkit-box-shadow:0 0 0 2px var(--primary-fg),0 0 0 4px var(--primary-dark);box-shadow:0 0 0 2px var(--primary-fg),0 0 0 4px var(--primary-dark)}body#tinymce .button.button--accent,body#tinymce button.button--accent,body#tinymce input[type=submit].button--accent{background-color:var(--accent);border-color:var(--accent);color:var(--accent-fg)}body#tinymce .button.button--accent.focus,body#tinymce .button.button--accent:focus,body#tinymce .button.button--accent:hover,body#tinymce button.button--accent.focus,body#tinymce button.button--accent:focus,body#tinymce button.button--accent:hover,body#tinymce input[type=submit].button--accent.focus,body#tinymce input[type=submit].button--accent:focus,body#tinymce input[type=submit].button--accent:hover{background-color:var(--accent-dark);border-color:var(--accent-dark);color:var(--accent-fg)}body#tinymce .button.button--accent.focus,body#tinymce .button.button--accent:focus,body#tinymce button.button--accent.focus,body#tinymce button.button--accent:focus,body#tinymce input[type=submit].button--accent.focus,body#tinymce input[type=submit].button--accent:focus{-webkit-box-shadow:0 0 0 2px var(--accent-fg),0 0 0 4px var(--accent-dark);box-shadow:0 0 0 2px var(--accent-fg),0 0 0 4px var(--accent-dark)}body#tinymce .button.button--accent.active,body#tinymce .button.button--accent:active,body#tinymce button.button--accent.active,body#tinymce button.button--accent:active,body#tinymce input[type=submit].button--accent.active,body#tinymce input[type=submit].button--accent:active{background-image:none;-webkit-box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125);box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125)}body#tinymce .call-to-action{background-color:var(--primary-fg);border-color:var(--primary);color:var(--primary)}body#tinymce .call-to-action.focus,body#tinymce .call-to-action:focus,body#tinymce .call-to-action:hover{background-color:var(--primary-fg);border-color:var(--primary-dark);color:var(--primary-dark)}body#tinymce .call-to-action.focus,body#tinymce .call-to-action:focus{-webkit-box-shadow:0 0 0 2px var(--primary-fg),0 0 0 4px var(--primary-dark);box-shadow:0 0 0 2px var(--primary-fg),0 0 0 4px var(--primary-dark)}body#tinymce .call-to-action.call-to-action--accent{background-color:var(--accent-fg);border-color:var(--accent);color:var(--accent)}body#tinymce .call-to-action.call-to-action--accent.focus,body#tinymce .call-to-action.call-to-action--accent:focus,body#tinymce .call-to-action.call-to-action--accent:hover{background-color:var(--accent-fg);border-color:var(--accent-dark);color:var(--accent-dark)}body#tinymce .call-to-action.call-to-action--accent.focus,body#tinymce .call-to-action.call-to-action--accent:focus{-webkit-box-shadow:0 0 0 2px var(--accent-fg),0 0 0 4px var(--accent-dark);box-shadow:0 0 0 2px var(--accent-fg),0 0 0 4px var(--accent-dark)}body#tinymce .call-to-action.call-to-action--accent.active,body#tinymce .call-to-action.call-to-action--accent:active{background-image:none;-webkit-box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125);box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125)}body#tinymce [class$="--accent"] .call-to-action{background-color:var(--accent);border-color:var(--accent-fg);color:var(--accent-fg)}body#tinymce [class$="--accent"] .call-to-action.focus,body#tinymce [class$="--accent"] .call-to-action:focus,body#tinymce [class$="--accent"] .call-to-action:hover{background-color:var(--accent-dark);border-color:var(--accent-fg);color:var(--accent-fg)}body#tinymce [class$="--accent"] .call-to-action.focus,body#tinymce [class$="--accent"] .call-to-action:focus{-webkit-box-shadow:0 0 0 2px var(--accent-dark),0 0 0 4px var(--accent-fg);box-shadow:0 0 0 2px var(--accent-dark),0 0 0 4px var(--accent-fg)}body#tinymce [class$="--accent"] .call-to-action.active,body#tinymce [class$="--accent"] .call-to-action:active{background-image:none;-webkit-box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125);box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125)}body#tinymce .button--circle{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;background-color:var(--primary);border-color:var(--primary);border-radius:50%;border-style:solid;border-width:2px;color:var(--primary-fg);display:-webkit-box;display:-ms-flexbox;display:flex;height:3.75rem;justify-content:center;width:3.75rem}body#tinymce .button--circle.focus,body#tinymce .button--circle:focus,body#tinymce .button--circle:hover{background-color:var(--primary-dark);border-color:var(--primary-dark);color:var(--primary-fg);text-decoration:none}body#tinymce .button--circle.focus,body#tinymce .button--circle:focus{-webkit-box-shadow:0 0 0 2px var(--primary-fg),0 0 0 4px var(--primary-dark);box-shadow:0 0 0 2px var(--primary-fg),0 0 0 4px var(--primary-dark)}body#tinymce .button--circle.active,body#tinymce .button--circle:active{background-image:none;-webkit-box-shadow:0 0 0 3px var(--primary-alpha),inset 0 3px 5px rgba(0,0,0,.125);box-shadow:0 0 0 3px var(--primary-alpha),inset 0 3px 5px rgba(0,0,0,.125)}body#tinymce .button--circle--accent{background-color:var(--accent);color:var(--accent-fg)}body#tinymce .button--circle--accent.focus,body#tinymce .button--circle--accent:focus,body#tinymce .button--circle--accent:hover{background-color:var(--accent-dark);border-color:var(--accent-dark);color:var(--accent-fg)}body#tinymce .button--circle--accent.focus,body#tinymce .button--circle--accent:focus{-webkit-box-shadow:0 0 0 2px var(--accent-fg),0 0 0 4px var(--accent-dark);box-shadow:0 0 0 2px var(--accent-fg),0 0 0 4px var(--accent-dark)}body#tinymce .button--circle--accent.active,body#tinymce .button--circle--accent:active{-webkit-box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125);box-shadow:0 0 0 3px var(--accent-alpha),inset 0 3px 5px rgba(0,0,0,.125)}body#tinymce .page-section{background:var(--body-bg);padding:4rem 1rem;text-align:center;width:100%}body#tinymce .page-section ol,body#tinymce .page-section ul{list-style-position:inside;padding-left:0}body#tinymce .page-section--accent{background:var(--accent);color:var(--accent-fg)}body#tinymce .page-section--accent h2{color:var(--accent-fg)}body#tinymce .page-section--accent h2:before{background:var(--accent-fg)}body#tinymce .page-section--bordered{border:4px solid var(--accent);margin:5rem auto 0;width:calc(100% - 2rem)}body#tinymce .page-section--borderless{-webkit-box-shadow:none;box-shadow:none}body#tinymce .block__subtitle{font-size:1.5rem;margin-bottom:1rem;text-transform:uppercase}@media screen and (min-width:768px){body#tinymce .block__subtitle{margin-bottom:1.5rem}}body#tinymce .block-toggle__cta{position:relative;text-align:center}@media screen and (min-width:60rem){body#tinymce .block-toggle__cta{display:none}}body#tinymce .block-toggle__cta button{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;background-color:unset;border:none;border-radius:50%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;height:3rem;justify-content:center;padding:0;width:3rem}body#tinymce .block-toggle__cta button:active,body#tinymce .block-toggle__cta button:focus{color:var(--primary)}body#tinymce .block-toggle__cta__blurb{font-size:.875rem;margin-bottom:0;padding-bottom:2.5rem}body#tinymce .block-toggle__cta__button{bottom:0;color:var(--primary);font-size:1.5rem;left:50%;margin:0;position:absolute;-webkit-transform:translate(-50%,50%);-o-transform:translate(-50%,50%);transform:translate(-50%,50%);z-index:10}body#tinymce .block-toggle__cta__button svg{height:2rem;width:1.5rem}body#tinymce .block-toggle__cta__button.--visible svg{-webkit-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}body#tinymce .block-toggle__cta__button:hover{color:var(--primary-dark)}body#tinymce .block.block-toggle{padding-bottom:0}@media screen and (max-width:59.975rem){body#tinymce .block.block-toggle.block-toggle--visible .block-toggle__cta__blurb,body#tinymce .block.block-toggle:not(.block-toggle--visible) .block-toggle__content{display:none}}body#tinymce .block.block-toggle.block-meta{padding-bottom:6rem}@media screen and (min-width:60rem){body#tinymce .has-sections .entry-content{-ms-flex-pack:distribute;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;justify-content:space-around;max-width:90rem;width:100%}body#tinymce .page-section{-webkit-box-shadow:-3px 5px 4px 2px hsla(0,0%,53%,.09);box-shadow:-3px 5px 4px 2px hsla(0,0%,53%,.09);margin:4rem auto;max-width:50rem;padding:4rem 5.625rem;width:100%}body#tinymce .page-section:first-of-type,body#tinymce .page-section:nth-of-type(2){width:calc(50% - 4rem)}body#tinymce .page-section:only-of-type{max-width:50rem;width:100%}body#tinymce .page-section--bordered{-webkit-box-shadow:none;box-shadow:none;margin:4rem auto;max-width:50rem;width:100%}body#tinymce .page-section--bordered:first-of-type,body#tinymce .page-section--bordered:nth-of-type(2){width:calc(50% - 4rem)}body#tinymce .page-section--bordered:only-of-type{max-width:50rem;width:100%}} 2 | -------------------------------------------------------------------------------- /dist/styles/search-featured-books.css: -------------------------------------------------------------------------------- 1 | .search-books-results{background:#fff;border:1px solid #ccc;list-style:none;margin:0;max-height:200px;overflow-y:auto;padding:0;position:absolute;z-index:1000}.search-books-results li{cursor:pointer;padding:4px 8px}.search-books-results li:hover{background:#f0f0f0}.search-books-field{display:inline-block;position:relative;width:100%}.search-books-field .search-books-input{padding-right:2rem;width:100%}.search-books-field .search-books-icon{pointer-events:none;position:absolute;right:.5rem;top:50%;-webkit-transform:translateY(-50%);-o-transform:translateY(-50%);transform:translateY(-50%)}#customize-theme-controls,#sub-accordion-section-pb_front_page_catalog{height:auto!important;overflow:visible!important} 2 | -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 | 13 | 14 | 41 | 42 |
  • 43 | 44 | 49 | 50 | 163 | 164 |
    165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | %1$s

    %2$s

    ', 19 | __( 'Dependencies Missing', 'pressbooks-aldine' ), 20 | __( 'You must run composer install from the Aldine directory.', 'pressbooks-aldine' ) 21 | ) 22 | ); 23 | } 24 | require_once $composer; 25 | } 26 | 27 | $includes = [ 28 | 'actions', 29 | 'activation', 30 | 'admin', 31 | 'customizer', 32 | 'filters', 33 | 'helpers', 34 | 'shortcodes', 35 | 'tags', 36 | ]; 37 | 38 | foreach ( $includes as $include ) { 39 | require get_template_directory() . "/inc/$include/namespace.php"; 40 | } 41 | 42 | \HM\Autoloader\register_class_path( 'Aldine', __DIR__ . '/inc' ); 43 | 44 | add_action( 'after_switch_theme', '\Aldine\Activation\create_default_content', 10 ); 45 | add_action( 'after_switch_theme', '\Aldine\Activation\create_menus', 11 ); 46 | add_action( 'after_switch_theme', '\Aldine\Activation\assign_menus', 12 ); 47 | add_action( 'admin_bar_init', '\Aldine\Actions\remove_admin_bar_callback' ); 48 | add_action( 'after_setup_theme', '\Aldine\Actions\setup' ); 49 | add_action( 'after_setup_theme', '\Aldine\Actions\content_width', 0 ); 50 | add_action( 'wp_head', '\Aldine\Actions\output_custom_colors' ); 51 | add_action( 'init', '\Aldine\Actions\add_editor_styles' ); 52 | add_action( 'admin_init', '\Aldine\Actions\hide_catalog_content_editor' ); 53 | foreach ( [ 'post.php', 'post-new.php' ] as $hook ) { 54 | add_action( "admin_head-$hook", '\Aldine\Actions\tinymce_l18n' ); 55 | } 56 | add_filter( 'body_class', '\Aldine\Filters\body_classes' ); 57 | add_filter( 'excerpt_more', '\Aldine\Filters\excerpt_more' ); 58 | add_filter( 'query_vars', '\Aldine\Filters\register_query_vars' ); 59 | add_filter( 'wp_nav_menu_items', '\Aldine\Filters\adjust_menu', 10, 2 ); 60 | add_filter( 'the_content', 'apply_shortcodes' ); 61 | add_filter( 'show_admin_bar', '__return_false' ); 62 | add_action( 'widgets_init', '\Aldine\Actions\widgets_init' ); 63 | add_action( 'widgets_init', '\Aldine\Actions\remove_widgets' ); 64 | add_action( 'wp_enqueue_scripts', '\Aldine\Actions\enqueue_assets' ); 65 | add_action( 'updated_option', '\Aldine\Actions\add_color_variants', 10, 3 ); 66 | add_action( 'customize_register', '\Aldine\Customizer\customize_register' ); 67 | add_action( 'customize_preview_init', '\Aldine\Customizer\customize_preview_js' ); 68 | add_action( 'customize_controls_enqueue_scripts', '\Aldine\Customizer\enqueue_color_contrast_validator' ); 69 | add_action( 'customize_controls_enqueue_scripts', '\Aldine\Customizer\featured_books_scripts' ); 70 | add_action( 'customize_controls_enqueue_scripts', '\Aldine\Customizer\enqueue_contact_form_tweaks' ); 71 | add_action( 'customize_controls_enqueue_scripts', '\Aldine\Customizer\enqueue_pb_a11y_in_customizer' ); 72 | add_action( 'customize_controls_enqueue_scripts', '\Aldine\Customizer\enqueue_catalog_search_control_assets' ); 73 | add_action( 'wp_ajax_pb_search_catalog_books', '\Aldine\Customizer\ajax_search_catalog_books' ); 74 | 75 | // Shortcodes. 76 | add_shortcode( 'aldine_page_section', '\Aldine\Shortcodes\page_section' ); 77 | add_shortcode( 'aldine_call_to_action', '\Aldine\Shortcodes\call_to_action' ); 78 | add_shortcode( 'latest-titles', '\Aldine\Shortcodes\latest_titles_shortcode' ); 79 | 80 | // Catalog page: Network admin controls. 81 | add_action( 'admin_enqueue_scripts', '\Aldine\Admin\admin_scripts' ); 82 | add_action( 'wp_ajax_pressbooks_aldine_update_catalog', '\Aldine\Admin\update_catalog' ); 83 | add_filter( 'wpmu_blogs_columns', '\Aldine\Admin\catalog_columns' ); 84 | add_action( 'manage_blogs_custom_column', '\Aldine\Admin\catalog_column', 1, 3 ); 85 | add_action( 'manage_sites_custom_column', '\Aldine\Admin\catalog_column', 1, 3 ); 86 | 87 | // Remove unwanted menu pages. 88 | add_action( 'admin_menu', '\Aldine\Actions\remove_menu_items' ); 89 | 90 | // Remove unwanted actions. 91 | remove_action( 'before_delete_post', '_reset_front_page_settings_for_post' ); 92 | remove_action( 'wp_trash_post', '_reset_front_page_settings_for_post' ); 93 | -------------------------------------------------------------------------------- /inc/actions/namespace.php: -------------------------------------------------------------------------------- 1 | setSrcDirectory( 'assets' )->setDistDirectory( 'dist' ); 23 | 24 | /* 25 | * Make theme available for translation. 26 | * Translations can be filed in the /languages/ directory. 27 | */ 28 | load_theme_textdomain( 'pressbooks-aldine', get_template_directory() . '/languages' ); 29 | 30 | /* 31 | * Let WordPress manage the document title. 32 | * By adding theme support, we declare that this theme does not use a 33 | * hard-coded tag in the document head, and expect WordPress to 34 | * provide it for us. 35 | */ 36 | add_theme_support( 'title-tag' ); 37 | 38 | /* 39 | * Enable support for Post Thumbnails on posts and pages. 40 | * 41 | * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ 42 | */ 43 | add_theme_support( 'post-thumbnails' ); 44 | 45 | // This theme uses wp_nav_menu() in two locations. 46 | register_nav_menus( 47 | [ 48 | 'primary-menu' => __( 'Primary Menu', 'pressbooks-aldine' ), 49 | 'network-footer-menu' => __( 'Footer Menu', 'pressbooks-aldine' ), 50 | ] 51 | ); 52 | 53 | /* 54 | * Switch default core markup for search form, comment form, and comments 55 | * to output valid HTML5. 56 | */ 57 | add_theme_support( 58 | 'html5', [ 59 | 'search-form', 60 | 'comment-form', 61 | 'comment-list', 62 | 'gallery', 63 | 'caption', 64 | ] 65 | ); 66 | 67 | // Set up the WordPress core custom header feature. 68 | add_theme_support( 69 | 'custom-header', [ 70 | 'default-image' => get_template_directory_uri() . '/dist/images/header.jpg', 71 | 'width' => 1920, 72 | 'height' => 884, 73 | 'default-text-color' => '#000', 74 | ] 75 | ); 76 | 77 | // Add theme support for selective refresh for widgets. 78 | add_theme_support( 'customize-selective-refresh-widgets' ); 79 | 80 | /** 81 | * Add support for core custom logo. 82 | * 83 | * @link https://codex.wordpress.org/Theme_Logo 84 | */ 85 | add_theme_support( 86 | 'custom-logo', [ 87 | 'height' => 140, 88 | 'width' => 560, 89 | 'flex-width' => true, 90 | 'flex-height' => true, 91 | ] 92 | ); 93 | 94 | // Add editor style. 95 | add_editor_style( $assets->getPath( 'styles/editor.css' ) ); 96 | 97 | // Add shortcode buttons. 98 | add_action( 'init', __NAMESPACE__ . '\register_shortcode_buttons' ); 99 | 100 | remove_theme_support( 'widgets-block-editor' ); 101 | 102 | // Disables the block editor from managing widgets in the Gutenberg plugin. 103 | add_filter( 'gutenberg_use_widgets_block_editor', '__return_false' ); 104 | // Disables the block editor from managing widgets. 105 | add_filter( 'use_widgets_block_editor', '__return_false' ); 106 | } 107 | 108 | /** 109 | * Register widget area. 110 | * 111 | * @link https://developer.wordpress.org/themes/functionality/sidebars/#registering-a-sidebar 112 | */ 113 | function widgets_init() { 114 | $config = [ 115 | 'before_widget' => '<div class="widget %1$s %2$s">', 116 | 'after_widget' => '</div>', 117 | 'before_title' => '<h2>', 118 | 'after_title' => '</h2>', 119 | ]; 120 | register_sidebar( 121 | [ 122 | 'name' => __( 'Network Footer Block 1', 'pressbooks-aldine' ), 123 | 'description' => __( 124 | 'Add content for your network’s customizeable footer here. 125 | Currently, only text and image widgets are supported. 126 | Content in this widget area will appear in the first row (on mobile) or the first column (on desktops).', 127 | 'aldine' 128 | ), 129 | 'id' => 'network-footer-block-1', 130 | ] + $config 131 | ); 132 | register_sidebar( 133 | [ 134 | 'name' => __( 'Network Footer Block 2', 'pressbooks-aldine' ), 135 | 'description' => __( 136 | 'Add content for your network’s customizeable footer here. 137 | Currently, only text and image widgets are supported. 138 | Content in this widget area will appear in the second row (on mobile) or the middle column (on desktop).', 139 | 'aldine' 140 | ), 141 | 'id' => 'network-footer-block-2', 142 | ] + $config 143 | ); 144 | } 145 | 146 | /** 147 | * Enqueue scripts and styles. 148 | */ 149 | function enqueue_assets() { 150 | $assets = new Assets( 'pressbooks-aldine', 'theme' ); 151 | $assets->setSrcDirectory( 'assets' )->setDistDirectory( 'dist' ); 152 | 153 | wp_enqueue_style( 'aldine/style', $assets->getPath( 'styles/aldine.css' ), false, null ); 154 | wp_enqueue_style( 'aldine/webfonts', 'https://fonts.googleapis.com/css?family=Karla:400,400i,700|Spectral:400,400i,600', false, null ); 155 | wp_enqueue_script( 'aldine/script', $assets->getPath( 'scripts/aldine.js' ), [ 'jquery' ], null, true ); 156 | } 157 | 158 | /** 159 | * Add editor styles. 160 | */ 161 | function add_editor_styles() { 162 | $assets = new Assets( 'pressbooks-aldine', 'theme' ); 163 | $assets->setSrcDirectory( 'assets' )->setDistDirectory( 'dist' ); 164 | add_editor_style( $assets->getPath( 'styles/editor.css' ) ); 165 | } 166 | 167 | /** 168 | * Set the content width in pixels, based on the theme's design and stylesheet. 169 | * 170 | * Priority 0 to make it available to lower priority callbacks. 171 | * 172 | * @global int $content_width 173 | */ 174 | function content_width() { 175 | $GLOBALS['content_width'] = apply_filters( 'pressbooks_aldine_content_width', 640 ); 176 | } 177 | 178 | /** 179 | * Output custom colors as CSS variables. 180 | * 181 | * @return void 182 | */ 183 | function output_custom_colors() { 184 | if ( defined( 'PB_PLUGIN_VERSION' ) ) { 185 | echo \Pressbooks\Admin\Branding\get_customizer_colors(); 186 | } else { 187 | $colors = [ 188 | 'header_bg', 189 | 'primary', 190 | 'accent', 191 | 'primary_fg', 192 | 'accent_fg', 193 | 'primary_dark', 194 | 'accent_dark', 195 | 'primary_alpha', 196 | 'accent_alpha', 197 | 'header_text', 198 | ]; 199 | $values = []; 200 | foreach ( $colors as $k ) { 201 | $v = get_option( "pb_network_color_$k" ); 202 | if ( $v ) { 203 | $values[ $k ] = $v; 204 | } 205 | } 206 | $output = ''; 207 | if ( ! empty( $values ) ) { 208 | $output .= '<style type="text/css">:root{'; 209 | foreach ( $values as $k => $v ) { 210 | $k = str_replace( '_', '-', $k ); 211 | $output .= "--$k:$v;"; 212 | } 213 | $output .= '}</style>'; 214 | } 215 | echo $output; 216 | } 217 | } 218 | 219 | /** 220 | * Remove Admin Bar callback. 221 | */ 222 | function remove_admin_bar_callback() { 223 | remove_action( 'wp_head', '_admin_bar_bump_cb' ); 224 | } 225 | 226 | /** 227 | * Hide content editor for Catalog page. 228 | */ 229 | function hide_catalog_content_editor() { 230 | $post_id = $_GET['post'] ?? null; 231 | if ( ! isset( $post_id ) ) { 232 | return; 233 | } 234 | $template = get_page_template_slug( $post_id ); 235 | if ( $template === 'page-catalog.php' ) { 236 | add_action( 237 | 'edit_form_after_title', function() { 238 | printf( '<p>%s</p>', __( 'This page displays your network catalog, so there is no content to edit.', 'pressbooks-aldine' ) ); 239 | } 240 | ); 241 | remove_post_type_support( 'page', 'editor' ); 242 | remove_post_type_support( 'page', 'thumbnail' ); 243 | } 244 | } 245 | 246 | /** 247 | * Add dark and alpha variants for customizer colors on update. 248 | * 249 | * @param array $option Option 250 | * @param string $old_value Unused parameter 251 | * @param string $value Color value 252 | * @since 1.0.0 253 | */ 254 | function add_color_variants( $option, $old_value, $value ) { 255 | if ( ! in_array( $option, [ 'pb_network_color_primary', 'pb_network_color_accent' ], true ) ) { 256 | return; 257 | } 258 | 259 | $color = Hex::fromString( $value ); 260 | $color = $color->toRgb(); 261 | $color_alpha = $color->toRgba( 0.25 ); 262 | $color_alpha = (string) $color_alpha; 263 | 264 | update_option( $option . '_alpha', $color_alpha ); 265 | } 266 | 267 | /** 268 | * Register shortcode buttons. 269 | * 270 | * @since 1.1.0 271 | */ 272 | function register_shortcode_buttons() { 273 | if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) { 274 | return; 275 | } 276 | 277 | if ( get_user_option( 'rich_editing' ) !== 'true' ) { 278 | return; 279 | } 280 | 281 | add_filter( 'mce_external_plugins', '\Aldine\filters\add_buttons' ); 282 | add_filter( 'mce_buttons', '\Aldine\filters\register_buttons' ); 283 | } 284 | 285 | /** 286 | * Localize shortcode button strings. 287 | * 288 | * @since 1.1.0 289 | */ 290 | function tinymce_l18n() { 291 | ?> 292 | <script type='text/javascript'> 293 | const aldine = { 294 | page_section: { 295 | 'title': '<?php _e( 'Page Section', 'pressbooks-aldine' ); ?>', 296 | 'title_label': '<?php _e( 'Title', 'pressbooks-aldine' ); ?>', 297 | 'standard': '<?php _e( 'Standard', 'pressbooks-aldine' ); ?>', 298 | 'accent': '<?php _e( 'Accent', 'pressbooks-aldine' ); ?>', 299 | 'bordered': '<?php _e( 'Bordered', 'pressbooks-aldine' ); ?>', 300 | 'borderless': '<?php _e( 'Borderless', 'pressbooks-aldine' ); ?>' 301 | }, 302 | call_to_action: { 303 | 'title': '<?php _e( 'Call to Action', 'pressbooks-aldine' ); ?>', 304 | 'text': '<?php _e( 'Text', 'pressbooks-aldine' ); ?>', 305 | 'link': '<?php _e( 'Link', 'pressbooks-aldine' ); ?>' 306 | } 307 | }; 308 | </script> 309 | <?php 310 | } 311 | 312 | /** 313 | * Remove unwanted menu pages. 314 | * 315 | * @since 1.15.0 316 | */ 317 | function remove_menu_items() { 318 | remove_menu_page( 'edit.php' ); 319 | remove_submenu_page( 'tools.php', 'import.php' ); 320 | remove_submenu_page( 'tools.php', 'export.php' ); 321 | remove_submenu_page( 'tools.php', 'tools.php' ); 322 | remove_submenu_page( 'options-general.php', 'options-writing.php' ); 323 | remove_submenu_page( 'options-general.php', 'options-reading.php' ); 324 | remove_submenu_page( 'options-general.php', 'options-permalink.php' ); 325 | } 326 | 327 | /** 328 | * Remove unwanted widgets. 329 | * 330 | * @since 1.15.0 331 | */ 332 | function remove_widgets() { 333 | unregister_widget( 'WP_Widget_Pages' ); 334 | unregister_widget( 'WP_Widget_Calendar' ); 335 | unregister_widget( 'WP_Widget_Archives' ); 336 | unregister_widget( 'WP_Widget_Links' ); 337 | unregister_widget( 'WP_Widget_Meta' ); 338 | unregister_widget( 'WP_Widget_Search' ); 339 | unregister_widget( 'WP_Widget_Categories' ); 340 | unregister_widget( 'WP_Widget_Recent_Posts' ); 341 | unregister_widget( 'WP_Widget_Recent_Comments' ); 342 | unregister_widget( 'WP_Widget_RSS' ); 343 | unregister_widget( 'WP_Widget_Tag_Cloud' ); 344 | unregister_widget( 'WP_Widget_Media_Audio' ); 345 | unregister_widget( 'WP_Nav_Menu_Widget' ); 346 | unregister_widget( 'WP_Widget_Custom_HTML' ); 347 | unregister_widget( 'WP_Widget_Media_Video' ); 348 | unregister_widget( 'Akismet_Widget' ); 349 | } 350 | -------------------------------------------------------------------------------- /inc/activation/namespace.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Activate Aldine Theme 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | namespace Aldine\Activation; 9 | 10 | use function Aldine\Helpers\get_catalog_page; 11 | 12 | /** 13 | * Create default page content, importing from Pressbooks Publisher, if possible. 14 | */ 15 | function create_default_content() { 16 | if ( ! get_option( 'pb_aldine_activated' ) ) { 17 | $mods = get_option( 'theme_mods_pressbooks-publisher' ); 18 | if ( $mods === false ) { 19 | $mods = get_option( 'mods_pressbooks-publisher' ); 20 | } 21 | if ( $mods && isset( $mods['pressbooks_publisher_intro_textbox'] ) ) { 22 | $home_content = apply_filters( 'the_content', $mods['pressbooks_publisher_intro_textbox'] ); 23 | } else { 24 | $home_content = apply_filters( 25 | 'pb_root_home_page_content', 26 | sprintf( 27 | '[aldine_page_section title="%1$s"]<p>%2$s</p><p>[aldine_call_to_action link="/about" text="%3$s"]</p>[/aldine_page_section]', 28 | __( 'About Pressbooks', 'pressbooks-aldine' ), 29 | __( 'Pressbooks is easy-to-use book writing software that lets you create a book in all the formats you need to publish.', 'pressbooks-aldine' ), 30 | __( 'Learn More', 'pressbooks-aldine' ) 31 | ) 32 | ); 33 | } 34 | 35 | $default_pages = [ 36 | 'about' => [ 37 | 'post_title' => __( 'About', 'pressbooks-aldine' ), 38 | 'post_content' => apply_filters( 39 | 'pb_root_about_page_content', 40 | sprintf( 41 | '<p>%1$s</p><p>%2$s</p><p>%3$s</p>', 42 | __( 'Pressbooks is simple book production software that makes it easy to write, develop, and share your ideas. You can use Pressbooks to publish open educational resources, textbooks, scholarly monographs, fiction and non-fiction books, white papers, syllabi, and more.', 'pressbooks-aldine' ), 43 | __( 'Pressbooks lets creators quickly publish their content to the web and produce exports in multiple formats, including accessible EPUBs and PDFs specially designed for print-on-demand or digital distribution.', 'pressbooks-aldine' ), 44 | sprintf( 45 | /* translators: %1$s: link to Pressbooks product page; %2$2: link to Pressbooks getting started page */ 46 | __( 'Pressbooks\' %1$s are used by hundreds of educational institutions and thousands of individual authors and publishers around the world. %2$s to learn more about how you or your institution can get started with Pressbooks.', 'pressbooks' ), 47 | sprintf( '<a href="https://pressbooks.com/our-products/">%s</a>', __( 'suite of products', 'pressbooks-aldine' ) ), 48 | sprintf( '<a href="https://pressbooks.com/get-started/">%s</a>', __( 'Contact us', 'pressbooks-aldine' ) ) 49 | ) 50 | ) 51 | ), 52 | ], 53 | 'help' => [ 54 | 'post_title' => __( 'Help', 'pressbooks-aldine' ), 55 | 'post_content' => apply_filters( 56 | 'pb_root_help_page_content', 57 | sprintf( 58 | '<p>%1$s</p><p>%2$s</p><p>%3$s</p><p>%4$s</p><p>%5$s</p>', 59 | sprintf( 60 | /* translators: %s: link to guide */ 61 | __( 'Are you looking for help on your Pressbooks project? The most comprehensive resource available is the %s, which contains everything you need to know about creating, enriching and exporting your work.', 'pressbooks-aldine' ), 62 | sprintf( '<a href="https://guide.pressbooks.com/">%s</a>', __( 'Pressbooks User Guide', 'pressbooks-aldine' ) ) 63 | ), 64 | sprintf( 65 | /* translators: %1$s: link to Pressbooks YouTube channel; %2$s: link to Fundamental of Pressbooks YouTube playlist */ 66 | __( 'You can find short video tutorials and webinars about features and product updates on the %1$s. If you’re just getting started with Pressbooks, this %2$s will guide you.', 'pressbooks-aldine' ), 67 | sprintf( '<a href="https://www.youtube.com/c/Pressbooks">%s</a>', __( 'Pressbooks YouTube channel', 'pressbooks-aldine' ) ), 68 | sprintf( '<a href="https://www.youtube.com/playlist?list=PLMFmJu3NJheuRt1rZwNCEElROtSjc5dJG">%s</a>', __( 'short video series', 'pressbooks-aldine' ) ) 69 | ), 70 | sprintf( 71 | /* translators: %s: link to Pressbooks webinar schedule */ 72 | __( 'If you learn best by learning by attending live training sessions, you can register for and attend one of Pressbooks\' %s.', 'pressbooks-aldine' ), 73 | sprintf( '<a href="https://pressbooks.com/webinars/">%s</a>', __( 'monthly webinars', 'pressbooks-aldine' ) ) 74 | ), 75 | sprintf( 76 | /* translators: %1$s: link to Pressbooks User Guide page; %2$s: link to Pressbooks community forum */ 77 | __( 'The %1$s also contains links to other useful support resources and has answers to some commonly asked questions. Pressbooks also maintains a %2$s where you can ask and answer questions of other users.', 'pressbooks-aldine' ), 78 | sprintf( '<a href="https://guide.pressbooks.com">%s</a>', __( 'Pressbooks User Guide', 'pressbooks-aldine' ) ), 79 | sprintf( '<a href="https://pressbooks.community/">%s</a>', __( 'community forum', 'pressbooks-aldine' ) ) 80 | ), 81 | sprintf( 82 | /* translators: %s: link to Pressbooks support request form */ 83 | __( 'For additional support needs, reach out to your institution’s Pressbooks network managers. If you don’t know who your network managers are, please fill out the %s to be put in touch with them.', 'pressbooks-aldine' ), 84 | sprintf( '<a href="https://pressbooks.com/pressbooksedu-support/">%s</a>', __( 'support request form', 'pressbooks-aldine' ) ) 85 | ) 86 | ) 87 | ), 88 | ], 89 | 'catalog' => [ 90 | 'post_title' => __( 'Catalog', 'pressbooks-aldine' ), 91 | 'post_content' => '', 92 | 'meta_input' => [ 93 | '_wp_page_template' => 'page-catalog.php', 94 | ], 95 | ], 96 | 'home' => [ 97 | 'post_title' => __( 'Home', 'pressbooks-aldine' ), 98 | 'post_content' => $home_content, 99 | ], 100 | ]; 101 | 102 | // Add our pages. 103 | $pages = []; 104 | 105 | foreach ( $default_pages as $slug => $page ) { 106 | $check = get_page_by_path( $slug ); 107 | if ( empty( $check ) ) { 108 | $pages[ $slug ] = wp_insert_post( 109 | array_merge( 110 | $page, [ 111 | 'post_type' => 'page', 112 | 'post_status' => 'publish', 113 | ] 114 | ) 115 | ); 116 | } else { 117 | $pages[ $slug ] = $check->ID; 118 | } 119 | } 120 | 121 | // Set front page to Home. 122 | update_option( 'show_on_front', 'page' ); 123 | update_option( 'page_on_front', $pages['home'] ); 124 | 125 | // Remove content generated by wp_install_defaults. 126 | wp_delete_post( 1, true ); 127 | wp_delete_post( 2, true ); 128 | wp_delete_comment( 1, true ); 129 | 130 | // Migrate site logo. 131 | if ( ! empty( $mods['custom_logo'] ) ) { 132 | set_theme_mod( 'custom_logo', $mods['custom_logo'] ); 133 | } 134 | 135 | // Add "pb_aldine_activated" option to enable check above. 136 | add_option( 'pb_aldine_activated', 1 ); 137 | } 138 | } 139 | 140 | /** 141 | * Create default primary and footer menus. 142 | */ 143 | function create_menus() { 144 | $menu_name = __( 'Primary Menu', 'pressbooks-aldine' ); 145 | 146 | if ( ! wp_get_nav_menu_object( $menu_name ) ) { 147 | $menu_id = wp_create_nav_menu( $menu_name ); 148 | 149 | $catalog = get_catalog_page(); 150 | if ( $catalog && defined( 'PB_PLUGIN_VERSION' ) ) { 151 | wp_update_nav_menu_item( 152 | $menu_id, 153 | 0, 154 | [ 155 | 'menu-item-title' => __( 'Catalog', 'pressbooks-aldine' ), 156 | 'menu-item-type' => 'post_type', 157 | 'menu-item-object' => 'page', 158 | 'menu-item-object-id' => $catalog->ID, 159 | 'menu-item-status' => 'publish', 160 | ] 161 | ); 162 | } 163 | } 164 | 165 | $menu_name = __( 'Footer Menu', 'pressbooks-aldine' ); 166 | 167 | if ( ! wp_get_nav_menu_object( $menu_name ) ) { 168 | $menu_id = wp_create_nav_menu( $menu_name ); 169 | 170 | $about = get_page_by_title( __( 'About', 'pressbooks-aldine' ) ); 171 | if ( $about ) { 172 | wp_update_nav_menu_item( 173 | $menu_id, 174 | 0, 175 | [ 176 | 'menu-item-title' => __( 'About', 'pressbooks-aldine' ), 177 | 'menu-item-type' => 'post_type', 178 | 'menu-item-object' => 'page', 179 | 'menu-item-object-id' => $about->ID, 180 | 'menu-item-status' => 'publish', 181 | ] 182 | ); 183 | } 184 | 185 | $catalog = get_catalog_page(); 186 | if ( $catalog && defined( 'PB_PLUGIN_VERSION' ) ) { 187 | wp_update_nav_menu_item( 188 | $menu_id, 189 | 0, 190 | [ 191 | 'menu-item-title' => __( 'Catalog', 'pressbooks-aldine' ), 192 | 'menu-item-type' => 'post_type', 193 | 'menu-item-object' => 'page', 194 | 'menu-item-object-id' => $catalog->ID, 195 | 'menu-item-status' => 'publish', 196 | ] 197 | ); 198 | } 199 | 200 | $help = get_page_by_title( __( 'Help', 'pressbooks-aldine' ) ); 201 | if ( $help ) { 202 | wp_update_nav_menu_item( 203 | $menu_id, 204 | 0, 205 | [ 206 | 'menu-item-title' => __( 'Help', 'pressbooks-aldine' ), 207 | 'menu-item-type' => 'post_type', 208 | 'menu-item-object' => 'page', 209 | 'menu-item-object-id' => $help->ID, 210 | 'menu-item-status' => 'publish', 211 | ] 212 | ); 213 | } 214 | } 215 | } 216 | 217 | /** 218 | * Check for presence of menus; if they exist, assign them to their locations. 219 | */ 220 | function assign_menus() { 221 | $locations = get_theme_mod( 'nav_menu_locations' ); 222 | 223 | if ( ! empty( $locations ) ) { 224 | foreach ( $locations as $id => $value ) { 225 | switch ( $id ) { 226 | case 'primary-menu': 227 | $menu = get_term_by( 'name', 'Primary Menu', 'nav_menu' ); 228 | break; 229 | 230 | case 'network-footer-menu': 231 | $menu = get_term_by( 'name', 'Footer Menu', 'nav_menu' ); 232 | break; 233 | } 234 | 235 | if ( $menu ) { 236 | $locations[ $id ] = $menu->term_id; 237 | } 238 | } 239 | 240 | set_theme_mod( 'nav_menu_locations', $locations ); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /inc/admin/namespace.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Aldine admin 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | namespace Aldine\Admin; 9 | 10 | use PressbooksMix\Assets; 11 | use Pressbooks\Admin\Network\SharingAndPrivacyOptions; 12 | use Pressbooks\BookDirectory; 13 | use Pressbooks\DataCollector\Book as BookDataCollector; 14 | 15 | /** 16 | * Uses old option to provide a simpler upgrade path from pressbooks-publisher theme 17 | */ 18 | const BLOG_OPTION = 'pressbooks_publisher_in_catalog'; 19 | 20 | /** 21 | * Load admin scripts 22 | * 23 | * @param string $hook Hook 24 | */ 25 | function admin_scripts( $hook ) { 26 | if ( 'sites.php' !== $hook ) { 27 | return; 28 | } 29 | 30 | $assets = new Assets( 'pressbooks-aldine', 'theme' ); 31 | $assets->setSrcDirectory( 'assets' )->setDistDirectory( 'dist' ); 32 | wp_enqueue_script( 'pressbooks-aldine-admin', $assets->getPath( 'scripts/catalog-admin.js' ), [ 'jquery' ] ); 33 | 34 | wp_localize_script( 35 | 'pressbooks-aldine-admin', 'PB_Aldine_Admin', [ 36 | 'aldineAdminNonce' => wp_create_nonce( 'pressbooks-aldine-admin' ), 37 | 'catalog_updated' => __( 'Catalog updated.', 'pressbooks-aldine' ), 38 | 'catalog_not_updated' => __( 'Sorry, but your catalog was not updated. Please try again.', 'pressbooks-aldine' ), 39 | 'dismiss_notice' => __( 'Dismiss this notice.', 'pressbooks-aldine' ), 40 | ] 41 | ); 42 | } 43 | 44 | /** 45 | * Update catalog 46 | */ 47 | function update_catalog() { 48 | if ( ! current_user_can( 'manage_network' ) || ! check_ajax_referer( 'pressbooks-aldine-admin' ) ) { 49 | return; 50 | } 51 | if ( isset( $_POST['book_id'] ) ) { 52 | $blog_id = absint( $_POST['book_id'] ); 53 | } 54 | if ( isset( $_POST['in_catalog'] ) ) { 55 | $in_catalog = wp_unslash( $_POST['in_catalog'] ); 56 | if ( $in_catalog === 'true' ) { 57 | update_blog_option( $blog_id, \Aldine\Admin\BLOG_OPTION, 1 ); 58 | update_site_meta( $blog_id, BookDataCollector::IN_CATALOG, 1 ); 59 | } else { 60 | delete_blog_option( $blog_id, \Aldine\Admin\BLOG_OPTION ); 61 | } 62 | } 63 | update_site_meta( $blog_id, BookDataCollector::IN_CATALOG, 0 ); 64 | // Exclude book when network option book directory non-catalog exclude is enabled. 65 | $option = get_site_option( 'pressbooks_sharingandprivacy_options', [], true ); 66 | if ( 67 | isset( $option[ SharingAndPrivacyOptions::NETWORK_DIRECTORY_EXCLUDED ] ) && 68 | ( (bool) $option[ SharingAndPrivacyOptions::NETWORK_DIRECTORY_EXCLUDED ] === true ) 69 | ) { 70 | BookDirectory::init()->deleteBookFromDirectory( [ $blog_id ] ); 71 | } 72 | update_blog_details( $blog_id, [ 'last_updated' => current_time( 'mysql', true ) ] ); 73 | } 74 | 75 | /** 76 | * Catalog columns 77 | * 78 | * @param array $columns Columns 79 | * 80 | * @return array 81 | */ 82 | function catalog_columns( $columns ) { 83 | $columns['in_catalog'] = __( 'In Catalog', 'pressbooks-aldine' ); 84 | return $columns; 85 | } 86 | 87 | /** 88 | * Catalog column 89 | * 90 | * @param string $column Column 91 | * @param int $blog_id Blog ID 92 | */ 93 | function catalog_column( $column, $blog_id ) { 94 | 95 | if ( 'in_catalog' === $column && ! is_main_site( $blog_id ) ) { ?> 96 | <input class="in-catalog" type="checkbox" name="in_catalog" value="1" aria-label="<?php echo esc_attr_x( 'Show in Catalog', 'pressbooks-aldine' ); ?>" <?php checked( get_blog_option( $blog_id, \Aldine\Admin\BLOG_OPTION ), 1 ); ?> <?php 97 | if ( ! get_blog_option( $blog_id, 'blog_public' ) ) { 98 | 99 | ?> 100 | disabled="disabled" title="<?php echo esc_attr_x( 'This book is private, so you can’t display it in your catalog.', 'pressbooks-aldine' ); ?>"<?php } ?> /> 101 | <?php 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /inc/customizer/class-searchfeaturedbooks.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Featured Books Search Control 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | namespace Aldine\Customizer; 9 | 10 | /** 11 | * Search Featured Books class 12 | */ 13 | class SearchFeaturedBooks extends \WP_Customize_Control { 14 | 15 | /** 16 | * The type of control being rendered. 17 | * 18 | * @var string 19 | */ 20 | public $type = 'search-books'; 21 | 22 | /** 23 | * Render the control's content. 24 | * 25 | * @return void 26 | */ 27 | public function render_content(): void { // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps 28 | $dc = \Pressbooks\DataCollector\Book::init(); 29 | $current_id = intval( $this->value() ); 30 | $current_title = $current_id 31 | ? $dc->get( $current_id, $dc::TITLE ) 32 | : ''; 33 | 34 | include get_template_directory() . '/partials/search-featured-books.php'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /inc/filters/namespace.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Aldine Filters 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | namespace Aldine\Filters; 9 | 10 | use function Aldine\Helpers\has_sections; 11 | use PressbooksMix\Assets; 12 | 13 | /** 14 | * Adds custom classes to the array of body classes. 15 | * 16 | * @see https://github.com/roots/sage/blob/master/app/filters.php 17 | * 18 | * @param array $classes Classes for the body element. 19 | * @return array 20 | */ 21 | function body_classes( array $classes ) { 22 | /** Add page slug if it doesn't exist */ 23 | if ( is_single() || is_page() && ! is_front_page() ) { 24 | if ( ! in_array( basename( get_permalink() ), $classes, true ) ) { 25 | $classes[] = basename( get_permalink() ); 26 | } 27 | } 28 | 29 | /** Add .has-sections if page content has sections */ 30 | if ( is_single() || is_front_page() || is_page() && has_sections( get_the_ID() ) ) { 31 | $classes[] = 'has-sections'; 32 | } 33 | 34 | /** Clean up class names for custom templates */ 35 | $classes = array_map( 36 | function ( $class ) { 37 | return preg_replace( [ '/-php$/', '/^page-template-views/' ], '', $class ); 38 | }, $classes 39 | ); 40 | 41 | return array_filter( $classes ); 42 | } 43 | 44 | /** 45 | * Add custom query vars for catalog. 46 | * 47 | * @param array $vars The array of available query variables. 48 | * 49 | * @link https://codex.wordpress.org/Plugin_API/Filter_Reference/query_vars 50 | */ 51 | function register_query_vars( $vars ) { 52 | $vars[] = 'license'; 53 | $vars[] = 'subject'; 54 | return $vars; 55 | } 56 | 57 | /** 58 | * Customize excerpt. 59 | * 60 | * @return string 61 | */ 62 | function excerpt_more() { 63 | return ' … <a href="' . get_permalink() . '">' . __( 'Continued', 'pressbooks-aldine' ) . '</a>'; 64 | } 65 | 66 | /** 67 | * Add things to the menu. 68 | * 69 | * @param string $items Items 70 | * @param object $args Args 71 | * @return string 72 | */ 73 | function adjust_menu( $items, $args ) { 74 | if ( $args->theme_location === 'primary-menu' ) { 75 | return \Aldine\Helpers\get_default_menu( $items ); 76 | } 77 | 78 | return $items; 79 | } 80 | 81 | /** 82 | * Add TinyMCE Buttons. 83 | * 84 | * @param array $plugin_array Plugin array 85 | * @since 1.1.0 86 | */ 87 | function add_buttons( $plugin_array ) { 88 | $assets = new Assets( 'pressbooks-aldine', 'theme' ); 89 | $assets->setSrcDirectory( 'assets' )->setDistDirectory( 'dist' ); 90 | 91 | $plugin_array['aldine_call_to_action'] = $assets->getPath( 'scripts/call-to-action.js' ); 92 | $plugin_array['aldine_page_section'] = $assets->getPath( 'scripts/page-section.js' ); 93 | return $plugin_array; 94 | } 95 | 96 | /** 97 | * Register TinyMCE Buttons. 98 | * 99 | * @param array $buttons TinyMCE Buttons 100 | * @since 1.1.0 101 | */ 102 | function register_buttons( $buttons ) { 103 | $c = count( $buttons ); 104 | $i = $c - 2; 105 | $new_items = [ 'aldine_page_section', 'aldine_call_to_action' ]; 106 | array_splice( $buttons, $i, 0, $new_items ); 107 | return $buttons; 108 | } 109 | -------------------------------------------------------------------------------- /inc/shortcodes/namespace.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Aldine Shortcodes 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | namespace Aldine\Shortcodes; 9 | 10 | use function Aldine\Helpers\get_featured_books; 11 | 12 | /** 13 | * Shortcode for Page Section. 14 | * 15 | * @param array $atts Shortcode attributes 16 | * @param string $content Page content 17 | * 18 | * @return string 19 | */ 20 | function page_section( $atts, $content = null ) { 21 | $atts = shortcode_atts( 22 | [ 23 | 'title' => '', 24 | 'variant' => '', 25 | ], 26 | $atts, 27 | 'aldine_page_section' 28 | ); 29 | 30 | return sprintf( 31 | '<div class="page-section%1$s">%2$s%3$s</div>', 32 | ( $atts['variant'] ) ? " page-section--{$atts['variant']}" : '', 33 | ( $atts['title'] ) ? "<h2>{$atts['title']}</h2>" : '', 34 | $content 35 | ); 36 | } 37 | 38 | /** 39 | * Shortcode for custom Call to Action. 40 | * 41 | * @param array $atts Shortcode attributes 42 | * 43 | * @return string 44 | */ 45 | function call_to_action( $atts ) { 46 | $atts = shortcode_atts( 47 | [ 48 | 'link' => '#', 49 | 'url' => false, 50 | 'text' => 'Call To Action', 51 | ], 52 | $atts, 53 | 'aldine_call_to_action' 54 | ); 55 | 56 | // Fallback for shortcodes using the old url attribute. 57 | if ( $atts['link'] === '#' && $atts['url'] ) { 58 | $atts['link'] = $atts['url']; 59 | } 60 | 61 | return sprintf( 62 | '<a class="call-to-action" href="%1$s" title="%2$s">%2$s</a>', 63 | $atts['link'], 64 | $atts['text'] 65 | ); 66 | } 67 | 68 | /** 69 | * Shortcode to display featured books anywhere. 70 | * 71 | * Usage: [latest-titles title="Custom Title"] 72 | * 73 | * @param array $atts Shortcode attributes. 74 | * @return string HTML markup of featured books. 75 | */ 76 | function latest_titles_shortcode( $atts ): string { 77 | $atts = shortcode_atts( [ 78 | 'title' => get_option( 'pb_front_page_catalog_title', __( 'Our Latest Titles', 'pressbooks-aldine' ) ), 79 | ], $atts, 'latest-titles' ); 80 | 81 | $books = get_featured_books(); 82 | if ( empty( $books ) || empty( $books['books'] ) ) { 83 | return ''; 84 | } 85 | 86 | $output = '<div id="latest-books" class="latest-books">'; 87 | $output .= '<h2 id="latest-books-title">' . esc_html( $atts['title'] ) . '</h2>'; 88 | $output .= '<div class="books">'; 89 | foreach ( $books['books'] as $book ) { 90 | ob_start(); 91 | $template = locate_template( 'partials/featured-book.php', false, false ); 92 | if ( $template ) { 93 | include $template; 94 | } 95 | $output .= ob_get_clean(); 96 | } 97 | $output .= '</div>'; 98 | $output .= '</div>'; 99 | 100 | return $output; 101 | } 102 | -------------------------------------------------------------------------------- /inc/tags/namespace.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Aldine Template Tags 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | namespace Aldine\Tags; 9 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The main template file 4 | * 5 | * This is the most generic template file in a WordPress theme 6 | * and one of the two required files for a theme (the other being style.css). 7 | * It is used to display a page when nothing more specific matches a query. 8 | * E.g., it puts together the home page when no home.php file exists. 9 | * 10 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 11 | * 12 | * @package Aldine 13 | */ 14 | 15 | get_header(); ?> 16 | 17 | <div id="primary" class="content-area"> 18 | <main id="main" class="site-main"> 19 | 20 | <?php 21 | if ( have_posts() ) : 22 | 23 | if ( is_home() && ! is_front_page() ) : 24 | ?> 25 | <header> 26 | <h1 class="page-title screen-reader-text"><?php single_post_title(); ?></h1> 27 | </header> 28 | 29 | <?php 30 | endif; 31 | 32 | /* Start the Loop */ 33 | while ( have_posts() ) : 34 | the_post(); 35 | 36 | /* 37 | * Include the Post-Format-specific template for the content. 38 | * If you want to override this in a child theme, then include a file 39 | * called content-___.php (where ___ is the Post Format name) and that will be used instead. 40 | */ 41 | get_template_part( 'template-parts/content', get_post_format() ); 42 | 43 | endwhile; 44 | 45 | the_posts_navigation(); 46 | 47 | else : 48 | 49 | get_template_part( 'template-parts/content', 'none' ); 50 | 51 | endif; 52 | ?> 53 | 54 | </main><!-- #main --> 55 | </div><!-- #primary --> 56 | 57 | <?php 58 | get_sidebar(); 59 | get_footer(); 60 | -------------------------------------------------------------------------------- /languages/bg_BG.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/bg_BG.mo -------------------------------------------------------------------------------- /languages/de_CH.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/de_CH.mo -------------------------------------------------------------------------------- /languages/de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/de_DE.mo -------------------------------------------------------------------------------- /languages/en_CA.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/en_CA.mo -------------------------------------------------------------------------------- /languages/en_GB.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/en_GB.mo -------------------------------------------------------------------------------- /languages/es_ES.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/es_ES.mo -------------------------------------------------------------------------------- /languages/fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/fr_FR.mo -------------------------------------------------------------------------------- /languages/he.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/he.mo -------------------------------------------------------------------------------- /languages/hr.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/hr.mo -------------------------------------------------------------------------------- /languages/hu_HU.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/hu_HU.mo -------------------------------------------------------------------------------- /languages/it_IT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/it_IT.mo -------------------------------------------------------------------------------- /languages/ja.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/ja.mo -------------------------------------------------------------------------------- /languages/kn.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/kn.mo -------------------------------------------------------------------------------- /languages/lt_LT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/lt_LT.mo -------------------------------------------------------------------------------- /languages/lv.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/lv.mo -------------------------------------------------------------------------------- /languages/nl_NL.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/nl_NL.mo -------------------------------------------------------------------------------- /languages/ru_RU.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/ru_RU.mo -------------------------------------------------------------------------------- /languages/tr_TR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/tr_TR.mo -------------------------------------------------------------------------------- /languages/zh_CN.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/zh_CN.mo -------------------------------------------------------------------------------- /languages/zh_TW.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/languages/zh_TW.mo -------------------------------------------------------------------------------- /lib/customizer-validate-wcag-color-contrast/README.md: -------------------------------------------------------------------------------- 1 | # Validate WCAG Color Contrast for Customizer Color Control 2 | 3 | The validator measures the color contrast between 2 or more color controls. It will post a warning if the contrast is less than 4.5 4 | 5 | BTW, if the contrast >= 7, the score is a WCAG AAA. If the contrast is between 7 and 4.5 the score is a WCAG AA. 6 | 7 | <img src="assets/wcag-color-contrast.gif" width="650" /> 8 | 9 | ## Demo 10 | 11 | I've added this validator to my [customizer demo theme](https://github.com/soderlind/2016-customizer-demo). 12 | 13 | ## Installing the validator 14 | 15 | Clone this repository and include the [javascript code](customizer-validate-wcag-color-contrast.js): 16 | 17 | ```php 18 | /** 19 | * Enqueue customizer control scripts. 20 | */ 21 | add_action( 'customize_controls_enqueue_scripts', 'on_customize_controls_enqueue_scripts' ); 22 | 23 | function on_customize_controls_enqueue_scripts() { 24 | $handle = 'wcag-validate-customizer-color-contrast'; 25 | $src = get_stylesheet_directory_uri() . '/js/customizer-validate-wcag-color-contrast.js'; 26 | $deps = [ 'customize-controls' ]; 27 | wp_enqueue_script( $handle, $src, $deps ); 28 | 29 | $exports = [ 30 | 'validate_color_contrast' => [ 31 | // key = current color control , values = array with color controls to check color contrast against 32 | 'page_background_color' => [ 'main_text_color', 'secondary_text_color' ], 33 | 'main_text_color' => [ 'page_background_color' ], 34 | 'secondary_text_color' => [ 'page_background_color' ], 35 | ], 36 | ]; 37 | wp_scripts()->add_data( $handle, 'data', sprintf( 'var _validateWCAGColorContrastExports = %s;', wp_json_encode( $exports ) ) ); 38 | } 39 | ``` 40 | 41 | **Note:** You have to add color control setting ids to the `validate_color_contrast` above. See inline comment. 42 | 43 | ## Credits ## 44 | 45 | - [WCAG contrast ratio measurement and scoring](https://github.com/tmcw/wcag-contrast) - Copyright (c) 2017, Tom MacWright. All rights reserved. 46 | - [hex-rgb](https://github.com/sindresorhus/hex-rgb) - Copyright (c) Sindre Sorhus 47 | - [relative-luminance](https://github.com/tmcw/relative-luminance) 48 | 49 | 50 | 51 | ## Copyright and License 52 | 53 | The Validate WCAG Color Contrast for Customizer Color Control is copyright 2017 Per Soderlind 54 | 55 | The Validate WCAG Color Contrast for Customizer Color Control is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. 56 | 57 | The Validate WCAG Color Contrast for Customizer Color Control is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 58 | 59 | You should have received a copy of the GNU Lesser General Public License along with the Extension. If not, see http://www.gnu.org/licenses/. 60 | -------------------------------------------------------------------------------- /lib/customizer-validate-wcag-color-contrast/customizer-validate-wcag-color-contrast.js: -------------------------------------------------------------------------------- 1 | /* global wp, _validateWCAGColorContrastExports */ 2 | /* exported validateWCAGColorContrast */ 3 | let validateWCAGColorContrast = ( function ( $, api, exports ) { 4 | let self = { validate_color_contrast: [] }; 5 | if ( exports ) { 6 | $.extend( self, exports ); 7 | } 8 | /** 9 | * Add contrast validation to a control if it is entitled (is a valid color control). 10 | * 11 | * @param {wp.customize.Control} setting - Control. 12 | * @param {wp.customize.Value} setting.validationMessage - Validation message. 13 | * @return {boolean} Whether validation was added. 14 | */ 15 | self.addWCAGColorContrastValidation = function ( setting ) { 16 | let initialValidate; 17 | 18 | if ( ! self.isColorControl( setting ) ) { 19 | return false; 20 | } 21 | initialValidate = setting.validate; 22 | 23 | /** 24 | * Wrap the setting's validate() method to do validation on the value to be sent to the server. 25 | * 26 | * @param {mixed} value - New value being assigned to the setting. 27 | * @returns {*} 28 | */ 29 | setting.validate = function ( value ) { 30 | let setting = this, title, validationError; 31 | let current_color = value; 32 | let current_id = this.id; 33 | 34 | let all_color_controls = _.union( _.flatten( _.values( self.validate_color_contrast ) ) ); 35 | 36 | // remove other (old) notifications 37 | _.each( _.without( all_color_controls, current_id ), function ( other_color_control_id ) { 38 | let other_control = api.control.instance( other_color_control_id ); 39 | notice = other_control.container.find( '.notice' ); 40 | notice.hide(); 41 | } ); 42 | 43 | // find other color controls and check contrast with current color control 44 | let other_color_controls = self.validate_color_contrast[ current_id ]; 45 | 46 | _.each( other_color_controls, function ( other_color_control_id ) { 47 | let other_control = api.control.instance( other_color_control_id ); 48 | let other_color = other_control.container.find( '.color-picker-hex' ).val(); 49 | let name = $( '#customize-control-' + other_color_control_id + ' .customize-control-title' ).text(); 50 | let contrast = self.hex( current_color, other_color ); 51 | let score = self.score( contrast ); 52 | 53 | // contrast >= 7 ? "AAA" : contrast >= 4.5 ? "AA" : "" 54 | if ( contrast < 4.5 ) { 55 | setting.notifications.remove( other_color_control_id ); 56 | validationWarning = new api.Notification( other_color_control_id, { message: self.sprintf( 'WCAG conflict with "%s"<br/>contrast: %s', name, contrast ), type: 'warning' } ); 57 | setting.notifications.add( validationWarning.code, validationWarning ); 58 | // console.log( color_control_id + ' ' + color + ' ' + contrast + ' ' + score ); 59 | } else { 60 | setting.notifications.remove( other_color_control_id ); 61 | } 62 | } ); 63 | 64 | return value; 65 | }; 66 | 67 | return true; 68 | }; 69 | 70 | /** 71 | * Return whether the setting is entitled (i.e. if it is a title or has a title). 72 | * 73 | * @param {wp.customize.Setting} setting - Setting. 74 | * @returns {boolean} 75 | */ 76 | self.isColorControl = function ( setting ) { 77 | return _.findKey( self.validate_color_contrast, function ( key, value ) { 78 | return value == setting.id; 79 | } ); 80 | }; 81 | 82 | api.bind( 'add', function ( setting ) { 83 | self.addWCAGColorContrastValidation( setting ); 84 | } ); 85 | 86 | self.sprintf = function ( format ) { 87 | for ( let i=1; i < arguments.length; i++ ) { 88 | format = format.replace( /%s/, arguments[i] ); 89 | } 90 | return format; 91 | }; 92 | 93 | /** 94 | * Methods used to calculate WCAG Color Contrast 95 | */ 96 | 97 | // from https://github.com/sindresorhus/hex-rgb 98 | self.hexRgb = function ( hex ) { 99 | if ( typeof hex !== 'string' ) { 100 | throw new TypeError( 'Expected a string' ); 101 | } 102 | 103 | hex = hex.replace( /^#/, '' ); 104 | 105 | if ( hex.length === 3 ) { 106 | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 107 | } 108 | 109 | let num = parseInt( hex, 16 ); 110 | 111 | return [ num >> 16, num >> 8 & 255, num & 255 ]; 112 | }; 113 | 114 | // from https://github.com/tmcw/relative-luminance 115 | // # Relative luminance 116 | // 117 | // http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef 118 | // https://en.wikipedia.org/wiki/Luminance_(relative) 119 | // https://en.wikipedia.org/wiki/Luminosity_function 120 | // https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients 121 | 122 | // red, green, and blue coefficients 123 | let rc = 0.2126, 124 | gc = 0.7152, 125 | bc = 0.0722, 126 | // low-gamma adjust coefficient 127 | lowc = 1 / 12.92; 128 | 129 | self.adjustGamma = function ( g ) { 130 | return Math.pow( ( g + 0.055 ) / 1.055, 2.4 ); 131 | }; 132 | 133 | /** 134 | * Given a 3-element array of R, G, B varying from 0 to 255, return the luminance 135 | * as a number from 0 to 1. 136 | * @param {Array<number>} rgb 3-element array of a color 137 | * @returns {number} luminance, between 0 and 1 138 | * @example 139 | * var luminance = require('relative-luminance'); 140 | * var black_lum = luminance([0, 0, 0]); // 0 141 | */ 142 | self.relativeLuminance = function ( rgb ) { 143 | let rsrgb = rgb[0] / 255; 144 | let gsrgb = rgb[1] / 255; 145 | let bsrgb = rgb[2] / 255; 146 | 147 | let r = rsrgb <= 0.03928 ? rsrgb * lowc : self.adjustGamma( rsrgb ), 148 | g = gsrgb <= 0.03928 ? gsrgb * lowc : self.adjustGamma( gsrgb ), 149 | b = bsrgb <= 0.03928 ? bsrgb * lowc : self.adjustGamma( bsrgb ); 150 | 151 | return r * rc + g * gc + b * bc; 152 | }; 153 | 154 | // from https://github.com/tmcw/wcag-contrast 155 | /** 156 | * Get the contrast ratio between two relative luminance values 157 | * @param {number} a luminance value 158 | * @param {number} b luminance value 159 | * @returns {number} contrast ratio 160 | * @example 161 | * luminance(1, 1); // = 1 162 | */ 163 | self.luminance = function ( a, b ) { 164 | let l1 = Math.max( a, b ); 165 | let l2 = Math.min( a, b ); 166 | return ( l1 + 0.05 ) / ( l2 + 0.05 ); 167 | }; 168 | 169 | /** 170 | * Get a score for the contrast between two colors as rgb triplets 171 | * @param {array} a 172 | * @param {array} b 173 | * @returns {number} contrast ratio 174 | * @example 175 | * rgb([0, 0, 0], [255, 255, 255]); // = 21 176 | */ 177 | self.rgb = function ( a, b ) { 178 | return self.luminance( self.relativeLuminance( a ), self.relativeLuminance( b ) ); 179 | }; 180 | 181 | /** 182 | * Get a score for the contrast between two colors as hex strings 183 | * @param {string} a hex value 184 | * @param {string} b hex value 185 | * @returns {number} contrast ratio 186 | * @example 187 | * hex('#000', '#fff'); // = 21 188 | */ 189 | self.hex = function ( a, b ) { 190 | return self.rgb( self.hexRgb( a ), self.hexRgb( b ) ); 191 | }; 192 | 193 | /** 194 | * Get a textual score from a numeric contrast value 195 | * @param {number} contrast 196 | * @returns {string} score 197 | * @example 198 | * score(10); // = 'AAA' 199 | */ 200 | self.score = function ( contrast ) { 201 | return contrast >= 7 ? 'AAA' : contrast >= 4.5 ? 'AA' : ''; 202 | }; 203 | 204 | return self; 205 | 206 | }( jQuery, wp.customize, _validateWCAGColorContrastExports ) ); 207 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pressbooks/pressbooks-aldine", 3 | "author": "Pressbooks (Book Oven Inc.) <code@pressbooks.com>", 4 | "homepage": "https://github.com/pressbooks/pressbooks-aldine/", 5 | "description": "Aldine is the default theme for the home page of Pressbooks networks. It is named for the Aldine Press, founded by Aldus Manutius in 1494, who is regarded by many as the world’s first publisher.", 6 | "keywords": [ 7 | "publishing", 8 | "catalog", 9 | "pressbooks", 10 | "default-theme" 11 | ], 12 | "private": true, 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/pressbooks/pressbooks-aldine.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/pressbooks/pressbooks-aldine/issues" 19 | }, 20 | "licenses": [ 21 | { 22 | "type": "GPL-3.0-or-later", 23 | "url": "https://github.com/pressbooks/pressbooks-aldine/tree/production/LICENSE.md" 24 | } 25 | ], 26 | "browserslist": [ 27 | "last 2 versions", 28 | "android 4", 29 | "opera 12" 30 | ], 31 | "eslintConfig": { 32 | "extends": "./node_modules/pressbooks-build-tools/config/eslint.js", 33 | "globals": { 34 | "$": true, 35 | "aldine": true, 36 | "PB_A11y": true, 37 | "tinymce": true 38 | }, 39 | "rules": { 40 | "import/no-anonymous-default-export": [ 41 | "error", 42 | { 43 | "allowArrowFunction": true, 44 | "allowObject": true 45 | } 46 | ], 47 | "jsdoc/require-param-type": "off", 48 | "jsdoc/require-returns": "off", 49 | "jsdoc/require-param-description": "off", 50 | "jsdoc/no-undefined-types": "off" 51 | } 52 | }, 53 | "eslintIgnore": [ 54 | "assets/scripts/catalog-admin.js", 55 | "assets/scripts/routes/common.js" 56 | ], 57 | "stylelint": { 58 | "extends": "./node_modules/pressbooks-build-tools/config/stylelint.js", 59 | "rules": { 60 | "scss/at-extend-no-missing-placeholder": null 61 | } 62 | }, 63 | "scripts": { 64 | "build": "npm run production", 65 | "lint:scripts": "eslint \"assets/scripts/**/*.js\"", 66 | "lint:styles": "stylelint \"assets/styles/**/*.scss\"", 67 | "lint": "run-s lint:*", 68 | "lint:fix": "eslint --fix \"assets/scripts/**/*.js\"", 69 | "production": "mix --production", 70 | "rmdist": "rimraf dist", 71 | "start": "mix watch", 72 | "test": "npm run lint" 73 | }, 74 | "engines": { 75 | "node": ">= 18" 76 | }, 77 | "devDependencies": { 78 | "npm-run-all": "^4.1.5", 79 | "pressbooks-build-tools": "^4.0.0" 80 | }, 81 | "dependencies": { 82 | "aetna": "^1.0.5", 83 | "isotope-layout": "^3.0.6", 84 | "jquery-bridget": "^3.0.1" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /page-catalog.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The template for displaying the catalog page 4 | * 5 | * Template Name: Catalog 6 | * 7 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 8 | * 9 | * @package Aldine 10 | */ 11 | 12 | if ( has_filter( 'pb_network_catalog' ) ) { 13 | echo apply_filters( 'pb_network_catalog', '' ); 14 | return; 15 | } 16 | 17 | use function Aldine\Helpers\get_available_institutions; 18 | use function Aldine\Helpers\get_available_licenses; 19 | use function Aldine\Helpers\get_available_subjects; 20 | use function Aldine\Helpers\get_catalog_licenses; 21 | use function Aldine\Helpers\get_institutions; 22 | use function Aldine\Helpers\get_paginated_catalog_data; 23 | 24 | $current_page = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1; 25 | $orderby = ( get_query_var( 'orderby' ) ) ? get_query_var( 'orderby' ) : 'title'; 26 | $subject = ( get_query_var( 'subject' ) ) ? get_query_var( 'subject' ) : ''; 27 | $license = ( get_query_var( 'license' ) ) ? get_query_var( 'license' ) : ''; 28 | $institution = get_query_var( 'institution' ) ?? ''; 29 | $catalog_data = get_paginated_catalog_data(); 30 | $previous_page = ( $current_page > 1 ) ? $current_page - 1 : 0; 31 | $next_page = $current_page + 1; 32 | $licenses = get_catalog_licenses(); 33 | $available_licenses = get_available_licenses( $catalog_data ); 34 | $institutions = get_institutions(); 35 | $available_institutions = get_available_institutions( $catalog_data ); 36 | $subjects = ( defined( 'PB_PLUGIN_VERSION' ) ) ? \Pressbooks\Metadata\get_thema_subjects() : []; 37 | $available_subjects = get_available_subjects( $catalog_data ); 38 | 39 | if ( ! empty( $catalog_data['books'] ) ) : 40 | 41 | get_header(); ?> 42 | 43 | <div id="primary" class="content-area"> 44 | <main id="main" class="site-main"> 45 | 46 | <?php include( locate_template( 'partials/content-page-catalog.php' ) ); ?> 47 | 48 | </main><!-- #main --> 49 | </div><!-- #primary --> 50 | 51 | <?php 52 | get_sidebar(); 53 | get_footer(); 54 | 55 | else : 56 | global $wp_query; 57 | $wp_query->set_404(); 58 | status_header( 404 ); 59 | get_header(); 60 | ?> 61 | <div id="primary" class="content-area"> 62 | <main id="main" class="site-main"> 63 | <article class="page"> 64 | <header class="entry-header"> 65 | <h1 class="entry-title"><?php _e( 'No Books Found', 'pressbooks-aldine' ); ?></h1> 66 | </header> 67 | <div class="entry-content" style="text-align:center;"> 68 | <p><?php _e( 'No books have been added to the catalog yet.', 'pressbooks-aldine' ); ?></p> 69 | </div> 70 | </article> 71 | </main> 72 | </div> 73 | <?php 74 | get_footer(); 75 | exit(); 76 | endif; 77 | -------------------------------------------------------------------------------- /page-featured-books.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The template for displaying the Our Last Titles section 4 | * 5 | * Template Name: Featured Books 6 | * 7 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 8 | * 9 | * @package Aldine 10 | */ 11 | 12 | get_header(); ?> 13 | 14 | <div id="primary" class="content-area wider-page"> 15 | <main id="main" class="site-main"> 16 | 17 | <?php 18 | while ( have_posts() ) : 19 | the_post(); 20 | 21 | get_template_part( 'partials/content-page-featured-books', 'page' ); 22 | 23 | endwhile; 24 | ?> 25 | 26 | </main><!-- #main --> 27 | </div><!-- #primary --> 28 | 29 | <?php 30 | get_sidebar(); 31 | get_footer(); 32 | -------------------------------------------------------------------------------- /page.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The template for displaying all pages 4 | * 5 | * This is the template that displays all pages by default. 6 | * Please note that this is the WordPress construct of pages 7 | * and that other 'pages' on your WordPress site may use a 8 | * different template. 9 | * 10 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 11 | * 12 | * @package Aldine 13 | */ 14 | 15 | get_header(); ?> 16 | 17 | <div id="primary" class="content-area"> 18 | <main id="main" class="site-main"> 19 | 20 | <?php 21 | if ( is_front_page() ) : 22 | 23 | while ( have_posts() ) : 24 | the_post(); 25 | 26 | get_template_part( 'partials/content', 'front-page' ); 27 | 28 | endwhile; // End of the loop. 29 | 30 | else : 31 | 32 | while ( have_posts() ) : 33 | the_post(); 34 | 35 | get_template_part( 'partials/content', 'page' ); 36 | 37 | endwhile; // End of the loop. 38 | endif; 39 | ?> 40 | 41 | </main><!-- #main --> 42 | </div><!-- #primary --> 43 | 44 | <?php 45 | get_sidebar(); 46 | get_footer(); 47 | -------------------------------------------------------------------------------- /partials/book.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template for displaying books in network catalog 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | use function \Aldine\Helpers\maybe_truncate_string; 9 | use function \Pressbooks\Metadata\is_bisac; 10 | 11 | $subject = ( isset( $book['subject'] ) && ! is_bisac( $book['subject'] ) ) ? substr( $book['subject'], 0, 2 ) : ''; 12 | $date = ( isset( $book['metadata']['datePublished'] ) ) ? str_replace( '-', '', $book['metadata']['datePublished'] ) : ''; 13 | $institution_codes = array_map( static function ( $item ) { 14 | return $item['code']; 15 | }, $book['metadata']['institutions'] ?? [] ); 16 | $institution_names = array_map( static function ( $item ) { 17 | return \Pressbooks\Metadata\get_institution_name( $item['code'] ); 18 | }, $book['metadata']['institutions'] ?? [] ); 19 | ?> 20 | <li class="book" 21 | <?php 22 | if ( $date ) { 23 | ?> 24 | data-date-published="<?php echo $date; ?>"<?php } ?> 25 | data-license="<?php echo ( new \Pressbooks\Licensing() )->getLicenseFromUrl( $book['metadata']['license']['url'] ); ?>" 26 | data-institution="<?php echo implode( ',', $institution_codes ); ?>" 27 | <?php 28 | if ( ! empty( $subject ) ) { 29 | ?> 30 | data-subject="<?php echo $subject ?>"<?php } ?> 31 | > 32 | <p class="book__title"> 33 | <a href="<?php echo $book['link']; ?>"><?php echo maybe_truncate_string( $book['metadata']['name'] ); ?></a> 34 | </p> 35 | <?php 36 | /* 37 | <?php if (isset( $book['metadata']['author'] ) ) { ?> 38 | <p class="book__author"> 39 | <?php _e( 'By', 'pressbooks-aldine' ); ?> <?php foreach ( $book['metadata']['author'] as $author ) { 40 | echo $author['name']; 41 | } ?> 42 | </p> 43 | <?php } ?> 44 | */ 45 | ?> 46 | <?php if ( ! empty( $subject ) ) { ?> 47 | <p class="book__subject"> 48 | <a href="<?php echo network_home_url( "/catalog/#$subject" ) ?>"><?php echo \Pressbooks\Metadata\get_subject_from_thema( $book['subject'] ); ?></a> 49 | </p> 50 | <?php } ?> 51 | <?php if ( $institution_names ) : ?> 52 | <p class="book__institutions"> 53 | <?php echo implode( ', ', $institution_names ); ?> 54 | </p> 55 | <?php endif; ?> 56 | <p class="book__read-more"> 57 | <a href="<?php echo $book['link']; ?>"><?php _e( 'About this book', 'pressbooks-aldine' ); ?> <svg aria-hidden="true"><use xlink:href="#arrow-right" /></svg></a> 58 | </p> 59 | </li> 60 | -------------------------------------------------------------------------------- /partials/contact-form.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying the contact form 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | ?> 9 | 10 | <?php 11 | 12 | $pb_network_contact_form_title = get_option( 'pb_network_contact_form_title' ); 13 | $contact_form_title = ( ! empty( $pb_network_contact_form_title ) ) ? $pb_network_contact_form_title : __( 'Contact Us', 'pressbooks-aldine' ); 14 | $contact_form_response = \Aldine\Helpers\handle_contact_form_submission(); 15 | $honeypot = 'firstname' . wp_rand(); 16 | 17 | ?> 18 | 19 | <aside class="contact" id="contact"> 20 | <h2 class="contact__title"><?php echo $contact_form_title; ?></h2> 21 | <form class="form" action="<?php echo network_home_url( '/#contact' ); ?>" method="post"> 22 | <?php if ( $contact_form_response ) : ?> 23 | <p class="form__notice form__notice--<?php echo $contact_form_response['status']; ?>"><?php echo $contact_form_response['message']; ?></p> 24 | <?php endif; ?> 25 | <?php wp_nonce_field( 'pb_root_contact_form', 'pb_root_contact_form_nonce' ); ?> 26 | <input type="hidden" name="submitted" value="1"> 27 | <p class="form__row" style="display:none;"> 28 | <input type="text" name="<?php echo $honeypot; ?>" id="<?php echo $honeypot; ?>"/> 29 | <label for="<?php echo $honeypot; ?>"> 30 | <?php _e( 'Keep this field blank (required)', 'pressbooks-aldine' ); ?> 31 | </label> 32 | </p> 33 | <p class="form__row"> 34 | <input 35 | id="contact-name" 36 | <?php if ( isset( $contact_form_response['field'] ) && $contact_form_response['field'] === 'visitor_name' ) : ?> 37 | class="error" 38 | <?php endif; ?> 39 | type="text" 40 | name="visitor_name" 41 | <?php if ( $contact_form_response && $contact_form_response['status'] === 'error' ) : ?> 42 | value="<?php echo $contact_form_response['values']['visitor_name']; ?>" 43 | <?php endif; ?> 44 | required 45 | /> 46 | <label for="contact-name"> 47 | <?php _e( 'Your name (required)', 'pressbooks-aldine' ); ?> 48 | </label> 49 | </p> 50 | <p class="form__row"> 51 | <input 52 | id="contact-email" 53 | <?php if ( isset( $contact_form_response['field'] ) && $contact_form_response['field'] === 'visitor_email' ) : ?> 54 | class="error" 55 | <?php endif; ?> 56 | type="email" 57 | name="visitor_email" 58 | <?php if ( $contact_form_response && $contact_form_response['status'] === 'error' ) : ?> 59 | value="<?php echo $contact_form_response['values']['visitor_email']; ?>" 60 | <?php endif; ?> 61 | required 62 | /> 63 | <label for="contact-email"> 64 | <?php _e( 'Your email address (required)', 'pressbooks-aldine' ); ?> 65 | </label> 66 | </p> 67 | <p class="form__row"> 68 | <input 69 | id="contact-institution" 70 | <?php if ( isset( $contact_form_response['field'] ) && $contact_form_response['field'] === 'visitor_institution' ) : ?> 71 | class="error" 72 | <?php endif; ?> 73 | type="text" 74 | name="visitor_institution" 75 | <?php if ( $contact_form_response && $contact_form_response['status'] === 'error' ) : ?> 76 | value="<?php echo $contact_form_response['values']['visitor_institution']; ?>" 77 | <?php endif; ?> 78 | required 79 | /> 80 | <label for="contact-institution"> 81 | <?php _e( 'Your institution (required)', 'pressbooks-aldine' ); ?> 82 | </label> 83 | </p> 84 | <p class="form__row"> 85 | <textarea 86 | id="contact-message" 87 | <?php if ( isset( $contact_form_response['field'] ) && $contact_form_response['field'] === 'message' ) : ?> 88 | class="error" 89 | <?php endif; ?> 90 | name="message" 91 | required 92 | ><?php if ( $contact_form_response && $contact_form_response['status'] === 'error' ) : ?> 93 | <?php echo $contact_form_response['values']['message']; ?> 94 | <?php endif; ?></textarea> 95 | <label for="contact-message"> 96 | <?php _e( 'Your message (required)', 'pressbooks-aldine' ); ?> 97 | </label> 98 | </p> 99 | <p class="form__row"> 100 | <input class="button button--small button--outline" type="submit" value="<?php _e( 'Send', 'pressbooks-aldine' ); ?>" /></p> 101 | </form> 102 | </aside> 103 | -------------------------------------------------------------------------------- /partials/content-front-page.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying the front page 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | use function Aldine\Helpers\get_catalog_page; 11 | use function Aldine\Helpers\get_featured_books; 12 | use function Aldine\Helpers\has_sections; 13 | 14 | $pb_front_page_catalog_title = get_option( 'pb_front_page_catalog_title' ); 15 | $latest_books_title = ( ! empty( $pb_front_page_catalog_title ) ) ? $pb_front_page_catalog_title : __('Our Latest Titles', 16 | 'pressbooks-aldine'); 17 | if ( get_option( 'pb_front_page_catalog' ) ) { 18 | $catalog_data = get_featured_books(); 19 | } 20 | 21 | $catalog_page = get_catalog_page(); 22 | if ( $catalog_page ) { 23 | $catalog_permalink = get_permalink( $catalog_page->ID ); 24 | } 25 | 26 | ?> 27 | 28 | <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> 29 | <header class="entry-header"> 30 | <h1 class="entry-title"><?php echo get_bloginfo( 'name', 'display' ); ?></h1> 31 | <p class="entry-description"><?php echo get_bloginfo( 'description', 'display' ); ?></p> 32 | </header><!-- .entry-header --> 33 | 34 | <div class="entry-content"> 35 | <?php 36 | if ( has_sections( $post->ID ) ) { 37 | the_content(); 38 | } else { 39 | $content = get_post_field( 'post_content', $post ); 40 | if ( ! empty( $content ) ) { 41 | echo apply_filters( 42 | 'the_content', 43 | sprintf( 44 | '[aldine_page_section]%s[/aldine_page_section]', 45 | $content 46 | ) 47 | ); 48 | } 49 | } 50 | ?> 51 | </div><!-- .entry-content --> 52 | </article><!-- #post-<?php the_ID(); ?> --> 53 | <?php if ( get_option( 'pb_front_page_catalog' ) && ! empty( $catalog_data['books'] ) ) : ?> 54 | <div id="latest-books" class="latest-books"> 55 | <h2 id="latest-books-title"><?php echo $latest_books_title; ?></h2> 56 | <div class="books"> 57 | <?php 58 | foreach ( $catalog_data['books'] as $book ) : 59 | include( locate_template( 'partials/featured-book.php' ) ); 60 | endforeach; 61 | ?> 62 | </div> 63 | <p class="catalog-link"> 64 | <a class="call-to-action" href="<?php echo $catalog_permalink ?? ''; ?>"> 65 | <?php 66 | _e('View Complete Catalog', 67 | 'pressbooks-aldine'); 68 | ?> 69 | </a> 70 | </p> 71 | </div> 72 | <?php endif; ?> 73 | -------------------------------------------------------------------------------- /partials/content-none.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying a message that posts cannot be found 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | 12 | <section class="no-results not-found"> 13 | <header class="page-header"> 14 | <h1 class="page-title"><?php esc_html_e( 'Nothing Found', 'pressbooks-aldine' ); ?></h1> 15 | </header><!-- .page-header --> 16 | 17 | <div class="page-content"> 18 | <?php 19 | if ( is_home() && current_user_can( 'publish_posts' ) ) : 20 | ?> 21 | 22 | <p> 23 | <?php 24 | printf( 25 | wp_kses( 26 | /* translators: 1: link to WP admin new post page. */ 27 | __( 'Ready to publish your first post? <a href="%1$s">Get started here</a>.', 'pressbooks-aldine' ), 28 | [ 29 | 'a' => [ 30 | 'href' => [], 31 | ], 32 | ] 33 | ), 34 | esc_url( admin_url( 'post-new.php' ) ) 35 | ); 36 | ?> 37 | </p> 38 | 39 | <?php elseif ( is_search() ) : ?> 40 | 41 | <p><?php esc_html_e( 'Sorry, but nothing matched your search terms. Please try again with some different keywords.', 'pressbooks-aldine' ); ?></p> 42 | <?php 43 | get_search_form(); 44 | 45 | else : 46 | ?> 47 | 48 | <p><?php esc_html_e( 'It seems we can’t find what you’re looking for. Perhaps searching can help.', 'pressbooks-aldine' ); ?></p> 49 | <?php 50 | get_search_form(); 51 | 52 | endif; 53 | ?> 54 | </div><!-- .page-content --> 55 | </section><!-- .no-results --> 56 | -------------------------------------------------------------------------------- /partials/content-page-catalog.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying the catalog page content 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | 12 | <?php get_template_part( 'partials/page', 'header' ); ?> 13 | <section class="network-catalog"> 14 | <form role="form" class="filter-sort" method="get"> 15 | <input type="hidden" name="paged" value="<?php echo $current_page; ?>" /> 16 | <fieldset class="subject-filters"> 17 | <h2><?php _e( 'Filter by Subject', 'pressbooks-aldine' ); ?></h2> 18 | <input type="radio" name="subject" id="all-subjects" value="" <?php checked( $subject, '' ); ?>> 19 | <label for="all-subjects"><?php _e( 'All Subjects', 'pressbooks-aldine' ); ?> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 20 | <div class="subject-groups"> 21 | <?php 22 | foreach ( $subjects as $key => $val ) : 23 | if ( array_key_exists( $key, $available_subjects ) ) : 24 | ?> 25 | <h3><span class="label"><?php echo $val['label']; ?></span></h3> 26 | <?php 27 | foreach ( $val['children'] as $k => $v ) : 28 | if ( in_array( $k, $available_subjects[ $key ], true ) ) : 29 | ?> 30 | <input type="radio" name="subject" id="<?php echo $k; ?>" value="<?php echo $k; ?>" <?php checked( $subject, $k ); ?>> 31 | <label for="<?php echo $k; ?>"><span class="label"><?php echo $v; ?></span> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 32 | <?php endif; ?> 33 | <?php endforeach; ?> 34 | <?php endif; ?> 35 | <?php endforeach; ?> 36 | </div> 37 | </fieldset> 38 | <fieldset class="institution-filters"> 39 | <h2><?php _e( 'Filter by Institution', 'pressbooks-aldine' ); ?></h2> 40 | <input type="radio" name="institution" id="all-institutions" value="" <?php checked( $institution, '' ); ?>> 41 | <label for="all-institutions"><?php _e( 'All Institutions', 'pressbooks-aldine' ); ?> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 42 | <?php 43 | foreach ( $institutions as $key => $value ) : 44 | if ( array_key_exists( $key, $available_institutions ) ) : 45 | ?> 46 | <input type="radio" name="institution" id="<?php echo $key; ?>" value="<?php echo $key; ?>" <?php checked( $institution, $key ); ?>> 47 | <label for="<?php echo $key; ?>"><?php echo $value; ?> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 48 | <?php 49 | endif; 50 | endforeach; 51 | ?> 52 | </fieldset> 53 | <fieldset class="license-filters"> 54 | <h2><?php _e( 'Filter by License', 'pressbooks-aldine' ); ?></h2> 55 | <input type="radio" name="license" id="all-licenses" value="" <?php checked( $license, '' ); ?>> 56 | <label for="all-licenses"><?php _e( 'All Licenses', 'pressbooks-aldine' ); ?> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 57 | <?php 58 | foreach ( $licenses as $key => $value ) : 59 | if ( in_array( $key, $available_licenses, true ) ) : 60 | ?> 61 | <input type="radio" name="license" id="<?php echo $key; ?>" value="<?php echo $key; ?>" <?php checked( $license, $key ); ?>> 62 | <label for="<?php echo $key; ?>"><?php echo $value; ?> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 63 | <?php 64 | endif; 65 | endforeach; 66 | ?> 67 | </fieldset> 68 | <fieldset class="sorts"> 69 | <h2><?php _e( 'Sort by', 'pressbooks-aldine' ); ?></h2> 70 | <?php 71 | $sorts = [ 72 | 'title' => __( 'Title', 'pressbooks-aldine' ), 73 | 'subject' => __( 'Subject', 'pressbooks-aldine' ), 74 | 'latest' => __( 'Latest', 'pressbooks-aldine' ), 75 | ]; 76 | foreach ( $sorts as $key => $value ) { 77 | ?> 78 | <input type="radio" name="orderby" id="<?php echo $key ?>" value="<?php echo $key ?>" <?php checked( $orderby, $key ); ?>> 79 | <label for="<?php echo $key ?>"><?php echo $value; ?> <svg class="checked"><use xlink:href="#checkmark" /></svg></label> 80 | <?php } ?> 81 | </fieldset> 82 | <button type="button" class="clear-filters" hidden><?php _e( 'Clear Filters', 'pressbooks-aldine' ); ?></button> 83 | <button type="submit"><?php _e( 'Submit', 'pressbooks-aldine' ); ?></button> 84 | </form> 85 | <ul class="books"> 86 | <?php 87 | foreach ( $catalog_data['books'] as $book ) : 88 | include( locate_template( 'partials/book.php' ) ); 89 | endforeach; 90 | ?> 91 | </ul> 92 | <?php if ( isset( $catalog_data['pages'] ) && $catalog_data['pages'] > 1 ) : ?> 93 | <nav class="catalog-navigation"> 94 | <?php 95 | if ( $previous_page ) : 96 | ?> 97 | <a class="previous" rel="previous" data-page="<?php echo $previous_page; ?>" href="<?php echo network_home_url( "/catalog/page/$previous_page/" ); ?>"><span class="screen-reader-text"><?php _e( 'Previous Page', 'pressbooks' ); ?></span> 98 | <svg aria-hidden="true"> 99 | <use xlink:href="#arrow-left" /> 100 | </svg></a><?php endif; ?> 101 | <div class="pages"> 102 | <?php 103 | for ( $i = 1; $i <= $catalog_data['pages']; $i++ ) : 104 | if ( $i === $current_page ) : 105 | ?> 106 | <span class="current"><?php echo $i; ?></span> 107 | <?php else : ?> 108 | <a href="<?php echo network_home_url( "/catalog/page/$i/" ); ?>"><?php echo $i; ?></a> 109 | <?php endif; ?> 110 | <?php endfor; ?> 111 | </div> 112 | <?php 113 | if ( $next_page <= $catalog_data['pages'] ) : 114 | ?> 115 | <a class="next" rel="next" data-page="<?php echo $next_page; ?>" href="<?php echo network_home_url( "/catalog/page/$next_page/" ); ?>"><span class="screen-reader-text"><?php _e( 'Next Page', 'pressbooks' ); ?></span> 116 | <svg aria-hidden="true"> 117 | <use xlink:href="#arrow-right" /> 118 | </svg></a><?php endif; ?> 119 | </nav> 120 | <?php endif; ?> 121 | </section> 122 | -------------------------------------------------------------------------------- /partials/content-page-featured-books.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying page content in page-titles.php 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | 12 | <article id="post-<?php the_ID(); ?>" <?php post_class( 'page-titles' ); ?>> 13 | <header class="entry-header"> 14 | <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?> 15 | </header><!-- .entry-header --> 16 | 17 | <div class="entry-content"> 18 | <?php 19 | $content = get_the_content(); 20 | $has_titles_shortcode = preg_match( '/\[latest-titles.*?\]/', $content, $shortcodes ); 21 | if ( $has_titles_shortcode ) { 22 | $main_content = preg_replace( '/\[latest-titles.*?\]/', '', $content ); 23 | $main_content = apply_filters( 'the_content', $main_content ); 24 | $main_content = str_replace( ']]>', ']]>', $main_content ); 25 | echo $main_content; 26 | } else { 27 | the_content(); 28 | } 29 | 30 | wp_link_pages( 31 | [ 32 | 'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'pressbooks-aldine' ), 33 | 'after' => '</div>', 34 | ] 35 | ); 36 | ?> 37 | </div><!-- .entry-content --> 38 | 39 | <?php if ( get_edit_post_link() ) : ?> 40 | <footer class="entry-footer"> 41 | <?php 42 | edit_post_link( 43 | sprintf( 44 | wp_kses( 45 | /* translators: %s: Name of current post. Only visible to screen readers */ 46 | __( 'Edit <span class="screen-reader-text">%s</span>', 'pressbooks-aldine' ), 47 | [ 48 | 'span' => [ 49 | 'class' => [], 50 | ], 51 | ] 52 | ), 53 | get_the_title() 54 | ), 55 | '<span class="edit-link">', 56 | '</span>' 57 | ); 58 | ?> 59 | </footer><!-- .entry-footer --> 60 | <?php endif; ?> 61 | </article> 62 | <?php 63 | if ( $has_titles_shortcode && $shortcodes[0] ) { 64 | echo do_shortcode( $shortcodes[0] ); 65 | } 66 | ?> 67 | -------------------------------------------------------------------------------- /partials/content-page.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying page content in page.php 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | 12 | <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> 13 | <header class="entry-header"> 14 | <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?> 15 | </header><!-- .entry-header --> 16 | 17 | <div class="entry-content"> 18 | <?php 19 | the_content(); 20 | 21 | wp_link_pages( 22 | [ 23 | 'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'pressbooks-aldine' ), 24 | 'after' => '</div>', 25 | ] 26 | ); 27 | ?> 28 | </div><!-- .entry-content --> 29 | 30 | <?php if ( get_edit_post_link() ) : ?> 31 | <footer class="entry-footer"> 32 | <?php 33 | edit_post_link( 34 | sprintf( 35 | wp_kses( 36 | /* translators: %s: Name of current post. Only visible to screen readers */ 37 | __( 'Edit <span class="screen-reader-text">%s</span>', 'pressbooks-aldine' ), 38 | [ 39 | 'span' => [ 40 | 'class' => [], 41 | ], 42 | ] 43 | ), 44 | get_the_title() 45 | ), 46 | '<span class="edit-link">', 47 | '</span>' 48 | ); 49 | ?> 50 | </footer><!-- .entry-footer --> 51 | <?php endif; ?> 52 | </article><!-- #post-<?php the_ID(); ?> --> 53 | -------------------------------------------------------------------------------- /partials/content-search.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying results in search pages 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | 12 | <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> 13 | <header class="entry-header"> 14 | <?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?> 15 | 16 | <?php if ( 'post' === get_post_type() ) : ?> 17 | <div class="entry-meta"> 18 | <?php pressbooks_aldine_posted_on(); ?> 19 | </div><!-- .entry-meta --> 20 | <?php endif; ?> 21 | </header><!-- .entry-header --> 22 | 23 | <div class="entry-summary"> 24 | <?php the_excerpt(); ?> 25 | </div><!-- .entry-summary --> 26 | 27 | <footer class="entry-footer"> 28 | <?php pressbooks_aldine_entry_footer(); ?> 29 | </footer><!-- .entry-footer --> 30 | </article><!-- #post-<?php the_ID(); ?> --> 31 | -------------------------------------------------------------------------------- /partials/content-single.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Single page template 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | // TODO. 11 | 12 | -------------------------------------------------------------------------------- /partials/content.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template part for displaying posts 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | 12 | <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> 13 | <header class="entry-header"> 14 | <?php 15 | if ( is_singular() ) : 16 | the_title( '<h1 class="entry-title">', '</h1>' ); 17 | else : 18 | the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' ); 19 | endif; 20 | 21 | if ( 'post' === get_post_type() ) : 22 | ?> 23 | <div class="entry-meta"> 24 | <?php pressbooks_aldine_posted_on(); ?> 25 | </div><!-- .entry-meta --> 26 | <?php 27 | endif; 28 | ?> 29 | </header><!-- .entry-header --> 30 | 31 | <div class="entry-content"> 32 | <?php 33 | the_content( 34 | sprintf( 35 | wp_kses( 36 | /* translators: %s: Name of current post. Only visible to screen readers */ 37 | __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'pressbooks-aldine' ), 38 | [ 39 | 'span' => [ 40 | 'class' => [], 41 | ], 42 | ] 43 | ), 44 | get_the_title() 45 | ) 46 | ); 47 | 48 | wp_link_pages( 49 | [ 50 | 'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'pressbooks-aldine' ), 51 | 'after' => '</div>', 52 | ] 53 | ); 54 | ?> 55 | </div><!-- .entry-content --> 56 | 57 | <footer class="entry-footer"> 58 | <?php pressbooks_aldine_entry_footer(); ?> 59 | </footer><!-- .entry-footer --> 60 | </article><!-- #post-<?php the_ID(); ?> --> 61 | -------------------------------------------------------------------------------- /partials/featured-book.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Template for displaying books in network catalog 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | use function \Aldine\Helpers\maybe_truncate_string; 9 | ?> 10 | <div class="featured_book"> 11 | <a href="<?php echo $book['link']; ?>" aria-label="<?php echo esc_attr( $book['metadata']['name'] ); ?>"> 12 | <div class="featured_book__cover" style="background-image: url('<?php echo $book['metadata']['image']; ?>' );"></div> 13 | <p class="featured_book__title"> 14 | <?php echo maybe_truncate_string( $book['metadata']['name'] ); ?> 15 | </p> 16 | </a> 17 | </div> 18 | -------------------------------------------------------------------------------- /partials/page-block.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Page block template 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | // TODO. 11 | 12 | -------------------------------------------------------------------------------- /partials/page-header.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Page header template 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | <div class="page-header"> 12 | <h1><?php echo get_the_title(); ?></h1> 13 | </div> 14 | -------------------------------------------------------------------------------- /partials/paged-navigation.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Book navigation template 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/ 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | ?> 11 | <nav class="booknav" aria-labelledby="latest-books-title book-navigation"> 12 | <span class="screen-reader-text"><?php _e( 'Navigation', 'pressbooks-aldine' ); ?></span> 13 | <nav class="booknav" aria-labelledby="catalog-books"> 14 | <span class="screen-reader-text" id="catalog-books"><?php _e( 'Book Catalog Navigation', 'pressbooks-aldine' ); ?></span> 15 | <?php if ( $previous_page ) : ?> 16 | <a class="previous" rel="previous" data-page="<?php echo $previous_page; ?>" href="<?php echo network_home_url( "/page/$previous_page/#latest-books" ); ?>"> 17 | <span class="screen-reader-text"><?php _e( 'Previous Page', 'pressbooks' ); ?></span> 18 | <svg aria-hidden="true"> 19 | <use xlink:href="#arrow-left" /> 20 | </svg> 21 | </a> 22 | <?php endif; ?> 23 | <?php if ( $next_page <= $catalog_data['pages'] ) : ?> 24 | <a class="next" rel="next" data-page="<?php echo $next_page; ?>" href="<?php echo network_home_url( "/page/$next_page/#latest-books" ); ?>"> 25 | <span class="screen-reader-text"><?php _e( 'Next Page', 'pressbooks' ); ?></span> 26 | <svg aria-hidden="true"> 27 | <use xlink:href="#arrow-right" /> 28 | </svg> 29 | </a> 30 | <?php endif; ?> 31 | </nav> 32 | -------------------------------------------------------------------------------- /partials/search-featured-books.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Featured Books Search Control 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | ?> 9 | 10 | <label data-setting="<?php echo esc_attr( $this->id ); ?>"> 11 | <span id="search-books-label-<?php echo esc_attr( $this->id ); ?>" class="customize-control-title"><?php echo esc_html( $this->label ); ?></span> 12 | <div class="search-books-field"> 13 | <input type="text" 14 | id="search-books-input-<?php echo esc_attr( $this->id ); ?>" 15 | class="search-books-input" 16 | data-setting="<?php echo esc_attr( $this->id ); ?>" 17 | value="<?php echo esc_attr( $current_title ); ?>" 18 | autocomplete="off" 19 | placeholder="<?php esc_attr_e( 'Search catalog', 'aldine' ); ?>" 20 | role="combobox" 21 | aria-autocomplete="list" 22 | aria-expanded="false" 23 | aria-controls="search-books-results-<?php echo esc_attr( $this->id ); ?>" 24 | aria-labelledby="search-books-label-<?php echo esc_attr( $this->id ); ?>" 25 | /> 26 | <span class="search-books-icon dashicons dashicons-search" aria-hidden="true"></span> 27 | </div> 28 | <ul 29 | id="search-books-results-<?php echo esc_attr( $this->id ); ?>" 30 | role="listbox" 31 | aria-labelledby="search-books-label-<?php echo esc_attr( $this->id ); ?>" 32 | class="search-books-results" 33 | ></ul> 34 | </label> 35 | -------------------------------------------------------------------------------- /phpcs.ruleset.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0"?> 2 | <ruleset> 3 | <!-- Use Pressbooks Coding Standards --> 4 | <rule ref="vendor/pressbooks/coding-standards"> 5 | <!-- TODO: Enable this again before merging --> 6 | <exclude name="Pressbooks.Security.EscapeOutput.OutputNotEscaped"/> 7 | <exclude name="Pressbooks.Security.ValidatedSanitizedInput"/> 8 | </rule> 9 | 10 | <!-- Disable Side Effects and MissingNamespace rules for bootstrapping files: --> 11 | <rule ref="PSR1.Files.SideEffects"> 12 | <exclude-pattern>/functions.php</exclude-pattern> 13 | </rule> 14 | <rule ref="HM.Functions.NamespacedFunctions.MissingNamespace"> 15 | <exclude-pattern>/functions.php</exclude-pattern> 16 | </rule> 17 | <!-- Run against the PHPCompatibility ruleset --> 18 | <rule ref="PHPCompatibility"/> 19 | <config name="testVersion" value="8.1-8.2"/> 20 | </ruleset> 21 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0"?> 2 | <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="tests/bootstrap.php" backupGlobals="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> 3 | <coverage> 4 | <include> 5 | <directory suffix=".php">./inc</directory> 6 | </include> 7 | </coverage> 8 | <php> 9 | <const name="WP_TESTS_MULTISITE" value="1"/> 10 | </php> 11 | <testsuites> 12 | <testsuite name="Pressbooks Aldine"> 13 | <directory prefix="test-" suffix=".php">./tests/</directory> 14 | </testsuite> 15 | </testsuites> 16 | </phpunit> 17 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 3 | "release-type": "simple", 4 | "include-component-in-tag": false, 5 | "include-v-in-tag": false, 6 | "packages": { 7 | ".": { 8 | "extra-files": [ 9 | "style.css" 10 | ] 11 | } 12 | }, 13 | "changelog-sections": [ 14 | { 15 | "type": "feat", 16 | "section": "Features" 17 | }, 18 | { 19 | "type": "fix", 20 | "section": "Bug Fixes" 21 | }, 22 | { 23 | "type": "docs", 24 | "section": "Documentation" 25 | }, 26 | { 27 | "type": "chore", 28 | "section": "Chores" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pressbooks/pressbooks-aldine/21dd7fcdc71bc24495db48fd0494a58eb0065ebf/screenshot.png -------------------------------------------------------------------------------- /search.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The template for displaying search results pages 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/#search-result 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | get_header(); ?> 11 | 12 | <section id="primary" class="content-area"> 13 | <main id="main" class="site-main"> 14 | 15 | <?php 16 | if ( have_posts() ) : 17 | ?> 18 | 19 | <header class="page-header"> 20 | <h1 class="page-title"> 21 | <?php 22 | /* translators: %s: search query. */ 23 | printf( esc_html__( 'Search Results for: %s', 'pressbooks-aldine' ), '<span>' . get_search_query() . '</span>' ); 24 | ?> 25 | </h1> 26 | </header><!-- .page-header --> 27 | 28 | <?php 29 | /* Start the Loop */ 30 | while ( have_posts() ) : 31 | the_post(); 32 | 33 | /** 34 | * Run the loop for the search to output the results. 35 | * If you want to overload this in a child theme then include a file 36 | * called content-search.php and that will be used instead. 37 | */ 38 | get_template_part( 'template-parts/content', 'search' ); 39 | 40 | endwhile; 41 | 42 | the_posts_navigation(); 43 | 44 | else : 45 | 46 | get_template_part( 'template-parts/content', 'none' ); 47 | 48 | endif; 49 | ?> 50 | 51 | </main><!-- #main --> 52 | </section><!-- #primary --> 53 | 54 | <?php 55 | get_sidebar(); 56 | get_footer(); 57 | -------------------------------------------------------------------------------- /searchform.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The catalog search form 4 | * 5 | * @package Aldine 6 | */ 7 | 8 | ?> 9 | <form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>"> 10 | <label for="s"> 11 | <?php _ex( 'Search Catalog', 'label', 'pressbooks-aldine' ); ?> 12 | <input id="s" type="search" class="search-field" value="<?php echo get_search_query(); ?>" name="s" /> 13 | </label> 14 | <input type="submit" class="search-submit" value="<?php _ex( 'Search', 'submit button', 'pressbooks-aldine' ); ?>" /> 15 | </form> 16 | -------------------------------------------------------------------------------- /sidebar.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The sidebar containing the main widget area 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-files/#template-partials 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | if ( ! is_active_sidebar( 'sidebar-1' ) ) { 11 | return; 12 | } 13 | ?> 14 | 15 | <aside id="secondary" class="widget-area"> 16 | <?php dynamic_sidebar( 'sidebar-primary' ); ?> 17 | </aside><!-- #secondary --> 18 | -------------------------------------------------------------------------------- /single.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * The template for displaying all single posts 4 | * 5 | * @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post 6 | * 7 | * @package Aldine 8 | */ 9 | 10 | get_header(); ?> 11 | 12 | <div id="primary" class="content-area"> 13 | <main id="main" class="site-main"> 14 | 15 | <?php 16 | while ( have_posts() ) : 17 | the_post(); 18 | 19 | get_template_part( 'template-parts/content', get_post_type() ); 20 | 21 | the_post_navigation(); 22 | 23 | // If comments are open or we have at least one comment, load up the comment template. 24 | if ( comments_open() || get_comments_number() ) : 25 | comments_template(); 26 | endif; 27 | 28 | endwhile; // End of the loop. 29 | ?> 30 | 31 | </main><!-- #main --> 32 | </div><!-- #primary --> 33 | 34 | <?php 35 | get_sidebar(); 36 | get_footer(); 37 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Theme Name: Aldine 3 | Theme URI: https://github.com/pressbooks/pressbooks-aldine/ 4 | Author: Pressbooks (Book Oven Inc.) 5 | Author URI: https://pressbooks.org 6 | Description: Aldine is the default theme for the home page of Pressbooks networks. It is named for the Aldine Press, founded by Aldus Manutius in 1494, who is regarded by many as the world’s first publisher. 7 | Tags: publishing, catalog, pressbooks, default-theme 8 | x-release-please-start-version 9 | Version: 1.21.0 10 | x-release-please-end 11 | Requires at least: 6.4.3 12 | Tested up to: 6.6.2 13 | Requires PHP: 8.1 14 | License: GNU GPL v3 or later 15 | License URI: https://github.com/pressbooks/pressbooks-aldine/tree/production/LICENSE.md 16 | Text Domain: pressbooks-aldine 17 | GitHub Theme URI: pressbooks/pressbooks-aldine 18 | Release Asset: true 19 | 20 | Aldine is based on Underscores https://underscores.me/, (C) 2012-2017 Automattic, Inc. 21 | Underscores is distributed under the terms of the GNU GPL v2 or later. 22 | */ 23 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * PHPUnit bootstrap file 4 | * 5 | * @package Pressbooks_Aldine 6 | */ 7 | 8 | $_tests_dir = getenv( 'WP_TESTS_DIR' ); 9 | if ( ! $_tests_dir ) { 10 | $_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib'; 11 | } 12 | 13 | if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) { 14 | throw new Exception( "Could not find $_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?" ); 15 | } 16 | 17 | // Give access to tests_add_filter() function. 18 | require_once $_tests_dir . '/includes/functions.php'; 19 | 20 | function _manually_load_plugin() { 21 | $_pressbooks = '/tmp/wordpress/wp-content/plugins/pressbooks/pressbooks.php'; 22 | if ( file_exists( $_pressbooks ) ) { 23 | require_once( $_pressbooks ); 24 | } else { 25 | require_once( __DIR__ . '/../../../plugins/pressbooks/pressbooks.php' ); 26 | } 27 | } 28 | 29 | /** 30 | * Registers theme 31 | */ 32 | function _register_theme() { 33 | 34 | $theme_dir = dirname( __DIR__ ); 35 | $current_theme = basename( $theme_dir ); 36 | $theme_root = dirname( $theme_dir ); 37 | 38 | add_filter( 'theme_root', function() use ( $theme_root ) { 39 | return $theme_root; 40 | } ); 41 | 42 | register_theme_directory( $theme_root ); 43 | 44 | add_filter( 'pre_option_template', function() use ( $current_theme ) { 45 | return $current_theme; 46 | }); 47 | add_filter( 'pre_option_stylesheet', function() use ( $current_theme ) { 48 | return $current_theme; 49 | }); 50 | } 51 | tests_add_filter( 'muplugins_loaded', '_register_theme' ); 52 | tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); 53 | 54 | 55 | // Start up the WP testing environment. 56 | require $_tests_dir . '/includes/bootstrap.php'; 57 | -------------------------------------------------------------------------------- /tests/test-filters.php: -------------------------------------------------------------------------------- 1 | <?php 2 | /** 3 | * Class Filters 4 | * 5 | * @package Pressbooks_Aldine 6 | */ 7 | 8 | use function \Aldine\Filters\register_query_vars; 9 | 10 | /** 11 | * Filters test case. 12 | */ 13 | class FiltersTest extends WP_UnitTestCase { 14 | public function test_register_query_vars() { 15 | $vars = register_query_vars([]); 16 | $this->assertContains('license', $vars); 17 | $this->assertContains('subject', $vars); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require( 'laravel-mix' ); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel application. By default, we are compiling the Sass 10 | | file for your application, as well as bundling up your JS files. 11 | | 12 | */ 13 | 14 | const inc = 'inc'; 15 | const partials = 'partials'; 16 | const assets = 'assets'; 17 | const dist = 'dist'; 18 | 19 | mix.setPublicPath( dist ); 20 | mix.setResourceRoot( '../' ); 21 | 22 | // BrowserSync 23 | mix.browserSync( { 24 | host: 'localhost', 25 | proxy: 'https://pressbooks.test', 26 | port: 3100, 27 | files: [ 28 | '*.php', 29 | `${inc}/**/*.php`, 30 | `${partials}/**/*.php`, 31 | `${dist}/**/*.css`, 32 | `${dist}/**/*.js`, 33 | ], 34 | } ); 35 | 36 | // Sass 37 | mix.sass( `${assets}/styles/aldine.scss`, `${dist}/styles/aldine.css` ); 38 | mix.sass( `${assets}/styles/editor.scss`, `${dist}/styles/editor.css` ); 39 | mix.sass( `${assets}/styles/search-featured-books.scss`, `${dist}/styles/search-featured-books.css` ); 40 | 41 | // Javascript 42 | mix.autoload( { jquery: [ '$', 'window.jQuery', 'jQuery' ] } ); 43 | 44 | mix 45 | .js( `${assets}/scripts/aldine.js`, `${dist}/scripts` ) 46 | .js( `${assets}/scripts/call-to-action.js`, `${dist}/scripts` ) 47 | .js( `${assets}/scripts/catalog-admin.js`, `${dist}/scripts` ) 48 | .js( `${assets}/scripts/customizer.js`, `${dist}/scripts` ) 49 | .js( `${assets}/scripts/customizer-toggle.js`, `${dist}/scripts` ) 50 | .js( `${assets}/scripts/page-section.js`, `${dist}/scripts` ) 51 | .js( `${assets}/scripts/search-featured-books.js`, `${dist}/scripts` ); 52 | 53 | // Assets 54 | mix 55 | .copy( `${assets}/fonts`, `${dist}/fonts`, false ) 56 | .copy( `${assets}/images`, `${dist}/images`, false ); 57 | 58 | // Options 59 | mix.options( { processCssUrls: false } ); 60 | 61 | // Source maps when not in production. 62 | if ( ! mix.inProduction() ) { 63 | mix.sourceMaps(); 64 | } 65 | 66 | // Hash and version files in production. 67 | if ( mix.inProduction() ) { 68 | mix.version(); 69 | } 70 | 71 | // Add Isotope support. 72 | mix.webpackConfig( { 73 | resolve: { 74 | alias: { 75 | masonry: 'masonry-layout', 76 | isotope: 'isotope-layout', 77 | }, 78 | }, 79 | stats: { 80 | children: true 81 | } 82 | } ); 83 | --------------------------------------------------------------------------------