├── drupal
├── keys
│ ├── .keep
│ ├── web.config
│ └── .htaccess
├── config
│ ├── claro.settings.yml
│ ├── system.image.yml
│ ├── dblog.settings.yml
│ ├── system.image.gd.yml
│ ├── system.logging.yml
│ ├── automated_cron.settings.yml
│ ├── field.settings.yml
│ ├── field_ui.settings.yml
│ ├── node.settings.yml
│ ├── system.rss.yml
│ ├── comment.settings.yml
│ ├── system.feature_flags.yml
│ ├── text.settings.yml
│ ├── menu_ui.settings.yml
│ ├── system.advisories.yml
│ ├── system.theme.yml
│ ├── system.diff.yml
│ ├── announcements_feed.settings.yml
│ ├── filter.settings.yml
│ ├── jsonapi.settings.yml
│ ├── system.file.yml
│ ├── taxonomy.settings.yml
│ ├── user.flood.yml
│ ├── system.cron.yml
│ ├── contact.settings.yml
│ ├── image.settings.yml
│ ├── system.maintenance.yml
│ ├── shortcut.set.default.yml
│ ├── core.menu.static_menu_link_overrides.yml
│ ├── system.date.yml
│ ├── system.mail.yml
│ ├── core.date_format.html_date.yml
│ ├── core.date_format.html_week.yml
│ ├── core.date_format.html_year.yml
│ ├── core.date_format.html_month.yml
│ ├── core.date_format.html_time.yml
│ ├── core.date_format.short.yml
│ ├── system.menu.footer.yml
│ ├── system.menu.main.yml
│ ├── core.date_format.long.yml
│ ├── core.date_format.fallback.yml
│ ├── core.date_format.medium.yml
│ ├── system.menu.admin.yml
│ ├── core.date_format.html_datetime.yml
│ ├── core.date_format.html_yearless_date.yml
│ ├── system.menu.tools.yml
│ ├── user.role.administrator.yml
│ ├── update.settings.yml
│ ├── simple_oauth.oauth2_token.bundle.auth_code.yml
│ ├── system.menu.account.yml
│ ├── block_content.type.basic.yml
│ ├── comment.type.comment.yml
│ ├── core.entity_view_mode.node.rss.yml
│ ├── simple_oauth.oauth2_token.bundle.access_token.yml
│ ├── simple_oauth.oauth2_token.bundle.refresh_token.yml
│ ├── core.entity_view_mode.node.teaser.yml
│ ├── core.entity_form_mode.user.register.yml
│ ├── core.entity_view_mode.node.full.yml
│ ├── core.entity_view_mode.user.compact.yml
│ ├── core.entity_view_mode.user.full.yml
│ ├── search.page.help_search.yml
│ ├── search.page.user_search.yml
│ ├── core.date_format.olivero_medium.yml
│ ├── taxonomy.vocabulary.tags.yml
│ ├── core.entity_view_mode.comment.full.yml
│ ├── core.entity_view_mode.node.search_index.yml
│ ├── contact.form.personal.yml
│ ├── search.page.node_search.yml
│ ├── system.action.node_save_action.yml
│ ├── core.entity_view_mode.block_content.full.yml
│ ├── system.action.node_delete_action.yml
│ ├── system.action.node_publish_action.yml
│ ├── contact.form.feedback.yml
│ ├── core.entity_view_mode.taxonomy_term.full.yml
│ ├── system.action.comment_save_action.yml
│ ├── system.action.node_promote_action.yml
│ ├── system.action.user_add_role_action.oauth.yml
│ ├── core.entity_view_mode.node.search_result.yml
│ ├── system.action.comment_delete_action.yml
│ ├── system.action.node_make_sticky_action.yml
│ ├── system.action.node_unpublish_action.yml
│ ├── system.action.user_block_user_action.yml
│ ├── system.action.comment_publish_action.yml
│ ├── system.action.node_make_unsticky_action.yml
│ ├── system.action.node_unpromote_action.yml
│ ├── system.action.user_remove_role_action.oauth.yml
│ ├── system.action.user_unblock_user_action.yml
│ ├── system.site.yml
│ ├── system.action.comment_unpublish_action.yml
│ ├── system.action.user_cancel_user_action.yml
│ ├── system.theme.global.yml
│ ├── node.type.page.yml
│ ├── simple_oauth.settings.yml
│ ├── system.action.taxonomy_term_publish_action.yml
│ ├── system.action.user_add_role_action.administrator.yml
│ ├── system.action.user_add_role_action.content_editor.yml
│ ├── node.type.article.yml
│ ├── system.action.taxonomy_term_unpublish_action.yml
│ ├── system.action.user_remove_role_action.administrator.yml
│ ├── system.action.user_remove_role_action.content_editor.yml
│ ├── search.settings.yml
│ ├── file.settings.yml
│ ├── olivero.settings.yml
│ ├── block.block.claro_help.yml
│ ├── block.block.claro_page_title.yml
│ ├── block.block.olivero_help.yml
│ ├── block.block.olivero_page_title.yml
│ ├── field.storage.node.body.yml
│ ├── block.block.claro_local_actions.yml
│ ├── simple_oauth.oauth2_scope.oauth.yml
│ ├── block.block.claro_content.yml
│ ├── field.storage.comment.comment_body.yml
│ ├── block.block.olivero_content.yml
│ ├── field.storage.node.comment.yml
│ ├── block.block.claro_breadcrumbs.yml
│ ├── block.block.claro_messages.yml
│ ├── block.block.olivero_primary_admin_actions.yml
│ ├── field.storage.block_content.body.yml
│ ├── block.block.olivero_breadcrumbs.yml
│ ├── block.block.olivero_messages.yml
│ ├── block.block.react_example_theme_help.yml
│ ├── block.block.react_example_theme_page_title.yml
│ ├── block.block.claro_primary_local_tasks.yml
│ ├── block.block.olivero_powered.yml
│ ├── block.block.olivero_syndicate.yml
│ ├── field.storage.node.field_tags.yml
│ ├── user.settings.yml
│ ├── user.role.oauth.yml
│ ├── block.block.claro_secondary_local_tasks.yml
│ ├── block.block.olivero_primary_local_tasks.yml
│ ├── block.block.olivero_secondary_local_tasks.yml
│ ├── system.performance.yml
│ ├── block.block.olivero_search_form_wide.yml
│ ├── block.block.olivero_search_form_narrow.yml
│ ├── block.block.react_example_theme_content.yml
│ ├── block.block.react_example_theme_primary_admin_actions.yml
│ ├── block.block.react_example_theme_messages.yml
│ ├── block.block.react_example_theme_breadcrumbs.yml
│ ├── block.block.react_example_theme_powered.yml
│ ├── core.base_field_override.node.page.promote.yml
│ ├── user.role.anonymous.yml
│ ├── block.block.react_example_theme_syndicate.yml
│ ├── block.block.react_example_theme_primary_local_tasks.yml
│ ├── block.block.react_example_theme_secondary_local_tasks.yml
│ ├── block.block.react_example_theme_search_form_wide.yml
│ ├── block.block.olivero_site_branding.yml
│ ├── block.block.react_example_theme_search_form_narrow.yml
│ ├── field.field.comment.comment.comment_body.yml
│ ├── image.style.wide.yml
│ ├── core.entity_view_display.block_content.basic.default.yml
│ ├── image.style.large.yml
│ ├── block.block.olivero_main_menu.yml
│ ├── field.field.node.page.body.yml
│ ├── image.style.medium.yml
│ ├── image.style.thumbnail.yml
│ ├── field.field.node.article.body.yml
│ ├── block.block.olivero_account_menu.yml
│ ├── block.block.react_example_theme_site_branding.yml
│ ├── core.entity_view_display.node.page.default.yml
│ ├── block.block.react_example_theme_main_menu.yml
│ ├── core.entity_view_display.comment.comment.default.yml
│ ├── field.field.block_content.basic.body.yml
│ ├── filter.format.plain_text.yml
│ ├── block.block.react_example_theme_account_menu.yml
│ ├── core.entity_view_display.node.page.teaser.yml
│ ├── user.role.authenticated.yml
│ ├── core.entity_view_display.node.article.rss.yml
│ ├── block.block.claro_help_search.yml
│ ├── field.storage.node.field_image.yml
│ ├── field.storage.user.user_picture.yml
│ ├── core.entity_view_display.user.user.compact.yml
│ ├── core.entity_view_display.user.user.default.yml
│ ├── .htaccess
│ ├── field.field.node.article.comment.yml
│ ├── field.field.node.article.field_tags.yml
│ ├── core.entity_form_display.comment.comment.default.yml
│ ├── core.entity_form_display.block_content.basic.default.yml
│ ├── core.entity_form_display.user.user.default.yml
│ ├── filter.format.restricted_html.yml
│ ├── filter.format.full_html.yml
│ ├── views.settings.yml
│ ├── core.extension.yml
│ ├── field.field.node.article.field_image.yml
│ ├── field.field.user.user.user_picture.yml
│ ├── user.role.content_editor.yml
│ ├── core.entity_view_display.node.article.teaser.yml
│ ├── filter.format.basic_html.yml
│ ├── editor.editor.basic_html.yml
│ ├── core.entity_view_display.node.article.default.yml
│ ├── core.entity_form_display.node.page.default.yml
│ ├── editor.editor.full_html.yml
│ ├── core.entity_form_display.node.article.default.yml
│ └── user.mail.yml
├── backup.sql.gz
├── backup-d9.sql.gz
├── web
│ ├── themes
│ │ └── react_example_theme
│ │ │ ├── .babelrc
│ │ │ ├── js
│ │ │ ├── .DS_Store
│ │ │ └── src
│ │ │ │ ├── .DS_Store
│ │ │ │ ├── components
│ │ │ │ ├── NodeAdd.jsx
│ │ │ │ ├── NodeEdit.jsx
│ │ │ │ ├── NodeDelete.jsx
│ │ │ │ ├── DrupalProjectStats.jsx
│ │ │ │ ├── NodeListOnly.jsx
│ │ │ │ └── NodeForm.jsx
│ │ │ │ ├── index.jsx
│ │ │ │ └── utils
│ │ │ │ └── fetch.js
│ │ │ ├── .proxyrc
│ │ │ ├── react_example_theme.info.yml
│ │ │ ├── react_example_theme.theme
│ │ │ ├── package.json
│ │ │ ├── react_example_theme.libraries.yml
│ │ │ └── webpack.config.js
│ ├── sites
│ │ └── default
│ │ │ ├── .gitignore
│ │ │ └── settings.php
│ └── README.md
├── drush
│ └── .gitignore
├── .editorconfig
├── composer.json
└── .gitattributes
├── react-decoupled-vite
├── src
│ ├── index.css
│ ├── components
│ │ ├── NodeAdd.jsx
│ │ ├── NodeEdit.jsx
│ │ ├── Login.jsx
│ │ ├── AuthCallback.jsx
│ │ ├── NodeDelete.jsx
│ │ ├── DrupalProjectStats.jsx
│ │ └── NodeListOnly.jsx
│ ├── main.jsx
│ ├── App.css
│ ├── App.jsx
│ ├── utils
│ │ └── fetch.js
│ └── assets
│ │ └── react.svg
├── vite.config.js
├── .gitignore
├── index.html
├── package.json
├── README.md
├── eslint.config.js
└── public
│ └── vite.svg
├── react-decoupled
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── components
│ │ ├── NodeAdd.jsx
│ │ ├── NodeEdit.jsx
│ │ ├── Login.jsx
│ │ ├── AuthCallback.jsx
│ │ ├── NodeDelete.jsx
│ │ ├── DrupalProjectStats.jsx
│ │ └── NodeListOnly.jsx
│ ├── setupTests.js
│ ├── index.js
│ ├── App.test.js
│ ├── index.css
│ ├── App.css
│ ├── App.js
│ ├── utils
│ │ └── fetch.js
│ └── logo.svg
├── .gitignore
├── .vscode
│ └── launch.json
├── package.json
└── README.md
├── README.md
└── .gitignore
/drupal/keys/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/react-decoupled-vite/src/index.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/drupal/config/claro.settings.yml:
--------------------------------------------------------------------------------
1 | third_party_settings:
2 | shortcut:
3 | module_link: true
4 |
--------------------------------------------------------------------------------
/drupal/backup.sql.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/drupal/backup.sql.gz
--------------------------------------------------------------------------------
/react-decoupled/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/drupal/backup-d9.sql.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/drupal/backup-d9.sql.gz
--------------------------------------------------------------------------------
/drupal/config/system.image.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: durWHaKeBaq4d9Wpi4RqwADj1OufDepcnJuhVLmKN24
3 | toolkit: gd
4 |
--------------------------------------------------------------------------------
/drupal/config/dblog.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: e883aGsrt1wFrsydlYU584PZONCSfRy0DtkZ9KzHb58
3 | row_limit: 1000
4 |
--------------------------------------------------------------------------------
/drupal/config/system.image.gd.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: eNXaHfkJJUThHeF0nvkoXyPLRrKYGxgHRjORvT4F5rQ
3 | jpeg_quality: 75
4 |
--------------------------------------------------------------------------------
/drupal/config/system.logging.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: u3-njszl92FaxjrCMiq0yDcjAfcdx72w1zT1O9dx6aA
3 | error_level: hide
4 |
--------------------------------------------------------------------------------
/drupal/config/automated_cron.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: fUksROt4FfkAU9BV4hV2XvhTBSS2nTNrZS4U7S-tKrs
3 | interval: 10800
4 |
--------------------------------------------------------------------------------
/drupal/config/field.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: nJk0TAQBzlNo52ehiHI7bIEPLGi0BYqZvPdEn7Chfu0
3 | purge_batch_size: 50
4 |
--------------------------------------------------------------------------------
/drupal/config/field_ui.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: Q1nMi90W6YQxKzZAgJQw7Ag9U4JrsEUwkomF0lhvbIM
3 | field_prefix: field_
4 |
--------------------------------------------------------------------------------
/drupal/config/node.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: W0cgFPhPJ3gAdqm06-az48BLf5MVcoZVS0HdByoofi0
3 | use_admin_theme: true
4 |
--------------------------------------------------------------------------------
/drupal/config/system.rss.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: MIpNzlG4gPunfS7vTCwUPum6QH3GUsEBMj-qS631Jw0
3 | items:
4 | view_mode: rss
5 |
--------------------------------------------------------------------------------
/react-decoupled/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/react-decoupled/public/favicon.ico
--------------------------------------------------------------------------------
/react-decoupled/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/react-decoupled/public/logo192.png
--------------------------------------------------------------------------------
/react-decoupled/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/react-decoupled/public/logo512.png
--------------------------------------------------------------------------------
/drupal/config/comment.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: YNUW2Ij5uE7a4oaXp3i_2lvaFdYM1zNKPPfnEjB0jEc
3 | log_ip_addresses: false
4 |
--------------------------------------------------------------------------------
/drupal/config/system.feature_flags.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: ZYyVj1FtPGV40Cf65YDVTUIc7YgLH6trXlotuevfs2I
3 | linkset_endpoint: false
4 |
--------------------------------------------------------------------------------
/drupal/config/text.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: Bkewb77RBOK3_aXMPsp8p87gbc03NvmC5gBLzPl7hVA
3 | default_summary_length: 600
4 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/drupal/config/menu_ui.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: SqMarzIjxC3F8dZo9FEOxfqDKD_sdW1tbcFTV1BA2zU
3 | override_parent_selector: false
4 |
--------------------------------------------------------------------------------
/drupal/config/system.advisories.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: x0FuQ_7Cg81mSDQwG028_Z0CjH3R9ib5IDlHeV2BbAo
3 | enabled: true
4 | interval_hours: 6
5 |
--------------------------------------------------------------------------------
/drupal/config/system.theme.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: eJ529VM1gSIA_vgTy2PdiDvJuG0xhSxfQjCyl5WKlv4
3 | admin: claro
4 | default: react_example_theme
5 |
--------------------------------------------------------------------------------
/drupal/config/system.diff.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: 1WanmaEhxW_vM8_5Ktsdntj8MaO9UBHXg0lN603PsWM
3 | context:
4 | lines_leading: 2
5 | lines_trailing: 2
6 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/drupal/web/themes/react_example_theme/js/.DS_Store
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrupalizeMe/react-and-drupal-examples/HEAD/drupal/web/themes/react_example_theme/js/src/.DS_Store
--------------------------------------------------------------------------------
/drupal/config/announcements_feed.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: 0G5pZBcxbg8ONYzNLd1RJIsvuFFewm9htnS4I-ABKJ8
3 | max_age: 86400
4 | cron_interval: 21600
5 | limit: 10
6 |
--------------------------------------------------------------------------------
/drupal/config/filter.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: FiPjM3WdB__ruFA7B6TLwni_UcZbmek5G4b2dxQItxA
3 | fallback_format: plain_text
4 | always_show_fallback_choice: false
5 |
--------------------------------------------------------------------------------
/drupal/keys/web.config:
--------------------------------------------------------------------------------
1 |
The requested URL "@path" was not found on this server.
' 14 | js: 15 | preprocess: true 16 | gzip: true 17 | -------------------------------------------------------------------------------- /drupal/config/block.block.olivero_search_form_wide.yml: -------------------------------------------------------------------------------- 1 | uuid: 9feda69f-d7de-415b-bba0-1a57920e8546 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - search 7 | theme: 8 | - olivero 9 | _core: 10 | default_config_hash: imMyHD6LYci0gtXq56qr9ZKGHzbEG9uFydrN5EhKtSU 11 | id: olivero_search_form_wide 12 | theme: olivero 13 | region: secondary_menu 14 | weight: -5 15 | provider: null 16 | plugin: search_form_block 17 | settings: 18 | id: search_form_block 19 | label: 'Search form (wide)' 20 | label_display: '0' 21 | provider: search 22 | page_id: '' 23 | visibility: { } 24 | -------------------------------------------------------------------------------- /drupal/config/block.block.olivero_search_form_narrow.yml: -------------------------------------------------------------------------------- 1 | uuid: 753ec493-7b3c-40c3-8701-153ec9522f4a 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - search 7 | theme: 8 | - olivero 9 | _core: 10 | default_config_hash: yEBET0cqDbk8dkWzaJw-8CKft0961VBflsISoSR6Lj8 11 | id: olivero_search_form_narrow 12 | theme: olivero 13 | region: primary_menu 14 | weight: -4 15 | provider: null 16 | plugin: search_form_block 17 | settings: 18 | id: search_form_block 19 | label: 'Search form (narrow)' 20 | label_display: '0' 21 | provider: search 22 | page_id: '' 23 | visibility: { } 24 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_content.yml: -------------------------------------------------------------------------------- 1 | uuid: d70ae40a-b020-4959-9d5a-646956ad12cd 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - system 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: erQSEZF2XUjNmgTl0uNRBzmg18ZGXwUcw2FhApoeuHk 11 | id: react_example_theme_content 12 | theme: react_example_theme 13 | region: content 14 | weight: 0 15 | provider: null 16 | plugin: system_main_block 17 | settings: 18 | id: system_main_block 19 | label: 'Main page content' 20 | label_display: '0' 21 | provider: system 22 | visibility: { } 23 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_primary_admin_actions.yml: -------------------------------------------------------------------------------- 1 | uuid: 91ade099-43a7-47b4-91fe-0dcb164c6284 2 | langcode: en 3 | status: true 4 | dependencies: 5 | theme: 6 | - react_example_theme 7 | _core: 8 | default_config_hash: Q9_2whdOj1YIomfvsIfopROW4FT_X5pY0DjdOiOaQ5U 9 | id: react_example_theme_primary_admin_actions 10 | theme: react_example_theme 11 | region: highlighted 12 | weight: -5 13 | provider: null 14 | plugin: local_actions_block 15 | settings: 16 | id: local_actions_block 17 | label: 'Primary admin actions' 18 | label_display: '0' 19 | provider: core 20 | visibility: { } 21 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_messages.yml: -------------------------------------------------------------------------------- 1 | uuid: c3a99074-8690-4b08-8f9e-ef2410b695d3 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - system 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: BZ5tpW7H8X4PVGRm3MImTIHd2tN0eF7zOtp4SpRYUA0 11 | id: react_example_theme_messages 12 | theme: react_example_theme 13 | region: highlighted 14 | weight: -5 15 | provider: null 16 | plugin: system_messages_block 17 | settings: 18 | id: system_messages_block 19 | label: 'Status messages' 20 | label_display: '0' 21 | provider: system 22 | visibility: { } 23 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_breadcrumbs.yml: -------------------------------------------------------------------------------- 1 | uuid: 36864cae-8028-41dc-a65a-a08d878ce95f 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - system 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: VhBzWb7lMRtIOg9G7VSw_0uopi-7zXeHq4vXqqV1HFE 11 | id: react_example_theme_breadcrumbs 12 | theme: react_example_theme 13 | region: breadcrumb 14 | weight: 0 15 | provider: null 16 | plugin: system_breadcrumb_block 17 | settings: 18 | id: system_breadcrumb_block 19 | label: Breadcrumbs 20 | label_display: '0' 21 | provider: system 22 | visibility: { } 23 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_powered.yml: -------------------------------------------------------------------------------- 1 | uuid: 6c18f090-3e60-48fa-aaf0-6c5739fabd8b 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - system 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: eYL19CLDyinGTWYQfBD1DswWzglEotE_kHnHx3AxTXM 11 | id: react_example_theme_powered 12 | theme: react_example_theme 13 | region: sidebar_first 14 | weight: 0 15 | provider: null 16 | plugin: system_powered_by_block 17 | settings: 18 | id: system_powered_by_block 19 | label: 'Powered by Drupal' 20 | label_display: '0' 21 | provider: system 22 | visibility: { } 23 | -------------------------------------------------------------------------------- /drupal/config/core.base_field_override.node.page.promote.yml: -------------------------------------------------------------------------------- 1 | uuid: 27002b90-af62-4cc9-bdc0-3c1e316e815a 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - node.type.page 7 | _core: 8 | default_config_hash: fPUEnm4T5zfZRr3ttDUqq7yCDd2uW3clWD-pvos4tlQ 9 | id: node.page.promote 10 | field_name: promote 11 | entity_type: node 12 | bundle: page 13 | label: 'Promoted to front page' 14 | description: '' 15 | required: false 16 | translatable: false 17 | default_value: 18 | - 19 | value: 0 20 | default_value_callback: '' 21 | settings: 22 | on_label: 'On' 23 | off_label: 'Off' 24 | field_type: boolean 25 | -------------------------------------------------------------------------------- /drupal/config/user.role.anonymous.yml: -------------------------------------------------------------------------------- 1 | uuid: ebf859fa-c86f-4a94-8777-d7d3763485e9 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - filter.format.restricted_html 7 | module: 8 | - comment 9 | - contact 10 | - filter 11 | - search 12 | - system 13 | _core: 14 | default_config_hash: 6WavjUYXIegP9AAg2zXGx54MWIVoomC3SZhNiqe-Dyk 15 | id: anonymous 16 | label: 'Anonymous user' 17 | weight: 0 18 | is_admin: false 19 | permissions: 20 | - 'access comments' 21 | - 'access content' 22 | - 'access site-wide contact form' 23 | - 'search content' 24 | - 'use text format restricted_html' 25 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_syndicate.yml: -------------------------------------------------------------------------------- 1 | uuid: 8b753819-0ddf-4a72-b10f-bbd92f8ca69b 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - node 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: 0gq3VPg-_UM69FCCWurLFIrrnIjC2HLKhwo9iQNtcUo 11 | id: react_example_theme_syndicate 12 | theme: react_example_theme 13 | region: sidebar_first 14 | weight: 0 15 | provider: null 16 | plugin: node_syndicate_block 17 | settings: 18 | id: node_syndicate_block 19 | label: 'RSS feed' 20 | label_display: '0' 21 | provider: node 22 | block_count: 10 23 | visibility: { } 24 | -------------------------------------------------------------------------------- /react-decoupled/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_primary_local_tasks.yml: -------------------------------------------------------------------------------- 1 | uuid: dde95bee-5ead-48a9-a3f5-1e44d12edb5b 2 | langcode: en 3 | status: true 4 | dependencies: 5 | theme: 6 | - react_example_theme 7 | _core: 8 | default_config_hash: nGE3EoPQQaQCuqTUtZgw0-KIzmrqdKDzdNQf2JyPUt4 9 | id: react_example_theme_primary_local_tasks 10 | theme: react_example_theme 11 | region: highlighted 12 | weight: -4 13 | provider: null 14 | plugin: local_tasks_block 15 | settings: 16 | id: local_tasks_block 17 | label: 'Primary tabs' 18 | label_display: '0' 19 | provider: core 20 | primary: true 21 | secondary: false 22 | visibility: { } 23 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_secondary_local_tasks.yml: -------------------------------------------------------------------------------- 1 | uuid: d7b16937-8d45-4bba-a57c-88e26b858624 2 | langcode: en 3 | status: true 4 | dependencies: 5 | theme: 6 | - react_example_theme 7 | _core: 8 | default_config_hash: ydSxdq7R66I8UMC460rOzlfzvlUL4VRbdwc6z9DWaUI 9 | id: react_example_theme_secondary_local_tasks 10 | theme: react_example_theme 11 | region: highlighted 12 | weight: -2 13 | provider: null 14 | plugin: local_tasks_block 15 | settings: 16 | id: local_tasks_block 17 | label: 'Secondary tabs' 18 | label_display: '0' 19 | provider: core 20 | primary: false 21 | secondary: true 22 | visibility: { } 23 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_search_form_wide.yml: -------------------------------------------------------------------------------- 1 | uuid: 4f6cf7aa-a1b5-40f5-8d71-64d1477e50c2 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - search 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: imMyHD6LYci0gtXq56qr9ZKGHzbEG9uFydrN5EhKtSU 11 | id: react_example_theme_search_form_wide 12 | theme: react_example_theme 13 | region: secondary_menu 14 | weight: -5 15 | provider: null 16 | plugin: search_form_block 17 | settings: 18 | id: search_form_block 19 | label: 'Search form (wide)' 20 | label_display: '0' 21 | provider: search 22 | page_id: '' 23 | visibility: { } 24 | -------------------------------------------------------------------------------- /drupal/config/block.block.olivero_site_branding.yml: -------------------------------------------------------------------------------- 1 | uuid: c42cfcec-d790-466a-8cf6-1920c2d5aa48 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - system 7 | theme: 8 | - olivero 9 | _core: 10 | default_config_hash: n_nlgjggHVfQt2H__zvLOKB2YtjPDbQ5tHijF9LE1aM 11 | id: olivero_site_branding 12 | theme: olivero 13 | region: header 14 | weight: 0 15 | provider: null 16 | plugin: system_branding_block 17 | settings: 18 | id: system_branding_block 19 | label: 'Site branding' 20 | label_display: '0' 21 | provider: system 22 | use_site_logo: true 23 | use_site_name: true 24 | use_site_slogan: false 25 | visibility: { } 26 | -------------------------------------------------------------------------------- /drupal/config/block.block.react_example_theme_search_form_narrow.yml: -------------------------------------------------------------------------------- 1 | uuid: bc4243c3-5b3f-4956-9720-d2922c8ebf65 2 | langcode: en 3 | status: true 4 | dependencies: 5 | module: 6 | - search 7 | theme: 8 | - react_example_theme 9 | _core: 10 | default_config_hash: yEBET0cqDbk8dkWzaJw-8CKft0961VBflsISoSR6Lj8 11 | id: react_example_theme_search_form_narrow 12 | theme: react_example_theme 13 | region: primary_menu 14 | weight: -4 15 | provider: null 16 | plugin: search_form_block 17 | settings: 18 | id: search_form_block 19 | label: 'Search form (narrow)' 20 | label_display: '0' 21 | provider: search 22 | page_id: '' 23 | visibility: { } 24 | -------------------------------------------------------------------------------- /drupal/config/field.field.comment.comment.comment_body.yml: -------------------------------------------------------------------------------- 1 | uuid: 6e476071-b31c-4c4b-a2b2-6da59ed5720d 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - comment.type.comment 7 | - field.storage.comment.comment_body 8 | module: 9 | - text 10 | _core: 11 | default_config_hash: 62rlTOx3sPVrYhBnSBHZStXwiO02v1UpHSMrhWDsuG8 12 | id: comment.comment.comment_body 13 | field_name: comment_body 14 | entity_type: comment 15 | bundle: comment 16 | label: Comment 17 | description: '' 18 | required: true 19 | translatable: true 20 | default_value: { } 21 | default_value_callback: '' 22 | settings: 23 | allowed_formats: { } 24 | field_type: text_long 25 | -------------------------------------------------------------------------------- /drupal/config/image.style.wide.yml: -------------------------------------------------------------------------------- 1 | uuid: 4152ba66-9338-41bd-b5d1-0b01ac62a741 2 | langcode: en 3 | status: true 4 | dependencies: { } 5 | _core: 6 | default_config_hash: XHh3ATMH7z4ljwmzdndM47qNMkgLnoYsP98rGxVgCOw 7 | name: wide 8 | label: 'Wide (1090)' 9 | effects: 10 | 09959c15-59ce-4f6d-90df-e2d7cf32bce5: 11 | uuid: 09959c15-59ce-4f6d-90df-e2d7cf32bce5 12 | id: image_scale 13 | weight: 1 14 | data: 15 | width: 1090 16 | height: null 17 | upscale: false 18 | 294c5f76-42a4-43ce-82c2-81c2f4723da0: 19 | uuid: 294c5f76-42a4-43ce-82c2-81c2f4723da0 20 | id: image_convert 21 | weight: 2 22 | data: 23 | extension: webp 24 | -------------------------------------------------------------------------------- /drupal/config/core.entity_view_display.block_content.basic.default.yml: -------------------------------------------------------------------------------- 1 | uuid: 1612c6a4-32cd-4947-84cc-a42f59cade64 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - block_content.type.basic 7 | - field.field.block_content.basic.body 8 | module: 9 | - text 10 | _core: 11 | default_config_hash: hBNNDTFwakREOTa6GGMqN899Iyrii0hInwSJtQ7Kj30 12 | id: block_content.basic.default 13 | targetEntityType: block_content 14 | bundle: basic 15 | mode: default 16 | content: 17 | body: 18 | type: text_default 19 | label: hidden 20 | settings: { } 21 | third_party_settings: { } 22 | weight: 0 23 | region: content 24 | hidden: { } 25 | -------------------------------------------------------------------------------- /drupal/config/image.style.large.yml: -------------------------------------------------------------------------------- 1 | uuid: 75c261e7-332f-4a55-abac-e1242b2e9b70 2 | langcode: en 3 | status: true 4 | dependencies: { } 5 | _core: 6 | default_config_hash: rDR2BOewa2UFH9yG4tVvrGxEVv8U7LQo-RLkJhFpERs 7 | name: large 8 | label: 'Large (480×480)' 9 | effects: 10 | ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d: 11 | uuid: ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d 12 | id: image_scale 13 | weight: 0 14 | data: 15 | width: 480 16 | height: 480 17 | upscale: false 18 | 6e8fe467-84c1-4ef0-a73b-7eccf1cc20e8: 19 | uuid: 6e8fe467-84c1-4ef0-a73b-7eccf1cc20e8 20 | id: image_convert 21 | weight: 2 22 | data: 23 | extension: webp 24 | -------------------------------------------------------------------------------- /drupal/config/block.block.olivero_main_menu.yml: -------------------------------------------------------------------------------- 1 | uuid: ab6c69fc-4519-4aef-b5c2-72d535501c7d 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - system.menu.main 7 | module: 8 | - system 9 | theme: 10 | - olivero 11 | _core: 12 | default_config_hash: KWAiziL39uEzmOJEql_wbUP2RtqGceL3WM2CfxhMelE 13 | id: olivero_main_menu 14 | theme: olivero 15 | region: primary_menu 16 | weight: 0 17 | provider: null 18 | plugin: 'system_menu_block:main' 19 | settings: 20 | id: 'system_menu_block:main' 21 | label: 'Main navigation' 22 | label_display: '0' 23 | provider: system 24 | level: 1 25 | depth: 2 26 | expand_all_items: true 27 | visibility: { } 28 | -------------------------------------------------------------------------------- /drupal/config/field.field.node.page.body.yml: -------------------------------------------------------------------------------- 1 | uuid: 1b0a0b67-3951-4967-94ea-47e637a8dc71 2 | langcode: en 3 | status: true 4 | dependencies: 5 | config: 6 | - field.storage.node.body 7 | - node.type.page 8 | module: 9 | - text 10 | _core: 11 | default_config_hash: KHu9I-pR4FnP79crRJ5gVD_1mKGSZcJsdIm9bn64iFg 12 | id: node.page.body 13 | field_name: body 14 | entity_type: node 15 | bundle: page 16 | label: Body 17 | description: '' 18 | required: false 19 | translatable: true 20 | default_value: { } 21 | default_value_callback: '' 22 | settings: 23 | display_summary: true 24 | required_summary: false 25 | allowed_formats: { } 26 | field_type: text_with_summary 27 | -------------------------------------------------------------------------------- /drupal/config/image.style.medium.yml: -------------------------------------------------------------------------------- 1 | uuid: 3026f1a4-54e6-4901-b940-fd09b3efc3c7 2 | langcode: en 3 | status: true 4 | dependencies: { } 5 | _core: 6 | default_config_hash: dlar76VBuGj5iMGTruB_uMZX8VbivXt9_QLemaG2q4E 7 | name: medium 8 | label: 'Medium (220×220)' 9 | effects: 10 | bddf0d06-42f9-4c75-a700-a33cafa25ea0: 11 | uuid: bddf0d06-42f9-4c75-a700-a33cafa25ea0 12 | id: image_scale 13 | weight: 0 14 | data: 15 | width: 220 16 | height: 220 17 | upscale: false 18 | c410ed2f-aa30-4d9c-a224-d2865d9188cd: 19 | uuid: c410ed2f-aa30-4d9c-a224-d2865d9188cd 20 | id: image_convert 21 | weight: 2 22 | data: 23 | extension: webp 24 | -------------------------------------------------------------------------------- /react-decoupled/src/components/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { beginLogin, logout, isLoggedIn } from '../utils/oauth'; 3 | 4 | export default function Login() { 5 | const [loggedIn, setLoggedIn] = useState(false); 6 | useEffect(() => { isLoggedIn().then(setLoggedIn); }, []); 7 | 8 | if (loggedIn) { 9 | return ( 10 |You're currently signed in.
12 | 13 |You're currently signed in.
12 | 13 |{message}
19 | 20 |{message}
19 | 20 | -
-
-
'
24 | filter_html_help: true
25 | filter_html_nofollow: false
26 | filter_url:
27 | id: filter_url
28 | provider: filter
29 | status: true
30 | weight: 0
31 | settings:
32 | filter_url_length: 72
33 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react_example_theme",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "js/src/index.jsx",
6 | "scripts": {
7 | "build": "NODE_ENV=production webpack --mode=production",
8 | "build:dev": "webpack",
9 | "start": "webpack --watch",
10 | "start:hmr": "webpack-dev-server --hot --progress --color"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "devDependencies": {
16 | "@babel/core": "^7.23.9",
17 | "@babel/preset-env": "^7.23.9",
18 | "@babel/preset-react": "^7.23.3",
19 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
20 | "babel-loader": "^9.1.3",
21 | "react-refresh": "^0.14.0",
22 | "webpack": "^5.90.1",
23 | "webpack-cli": "^5.1.4",
24 | "webpack-dev-server": "^4.15.1"
25 | },
26 | "dependencies": {
27 | "prop-types": "^15.8.1",
28 | "react": "^18.2.0",
29 | "react-dom": "^18.2.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/react-decoupled/src/utils/fetch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Helper function wraps a normal fetch call with a fetch request that first
3 | * retrieves a CSRF token and then adds the token as an X-CSRF-Token header
4 | * to the options for desired fetch call.
5 | *
6 | * @param {string} csrfUrl
7 | * URL where we can retrieve a CSRF token for the current user.
8 | * @param {string} fetchUrl
9 | * URL to fetch with X-CSRF-Token header included.
10 | * @param {object} fetchOptions
11 | * Options to pass to fetch for the call to fetchUrl.
12 | */
13 | export const fetchWithCSRFToken = (csrfUrl, fetchUrl, fetchOptions) => {
14 | if (!fetchOptions.headers.get('X-CSRF-Token')) {
15 | return fetch(csrfUrl)
16 | .then(response => response.text())
17 | .then((csrfToken) => {
18 | fetchOptions.headers.append('X-CSRF-Token', csrfToken);
19 | return fetch(fetchUrl, fetchOptions);
20 | });
21 | }
22 | else {
23 | return fetch(fetchUrl, fetchOptions);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/react-decoupled-vite/src/utils/fetch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Helper function wraps a normal fetch call with a fetch request that first
3 | * retrieves a CSRF token and then adds the token as an X-CSRF-Token header
4 | * to the options for desired fetch call.
5 | *
6 | * @param {string} csrfUrl
7 | * URL where we can retrieve a CSRF token for the current user.
8 | * @param {string} fetchUrl
9 | * URL to fetch with X-CSRF-Token header included.
10 | * @param {object} fetchOptions
11 | * Options to pass to fetch for the call to fetchUrl.
12 | */
13 | export const fetchWithCSRFToken = (csrfUrl, fetchUrl, fetchOptions) => {
14 | if (!fetchOptions.headers.get('X-CSRF-Token')) {
15 | return fetch(csrfUrl)
16 | .then(response => response.text())
17 | .then((csrfToken) => {
18 | fetchOptions.headers.append('X-CSRF-Token', csrfToken);
19 | return fetch(fetchUrl, fetchOptions);
20 | });
21 | }
22 | else {
23 | return fetch(fetchUrl, fetchOptions);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 |
4 | /* Import Components */
5 | import DrupalProjectStats from './components/DrupalProjectStats';
6 | import NodeListOnly from "./components/NodeListOnly";
7 | import NodeReadWrite from "./components/NodeReadWrite";
8 |
9 | // Define Main as a functional component
10 | const Main = () => (
11 | <>
12 | {
13 | //
14 | }
15 | {
16 | //
17 | }
18 | {
19 |
20 | }
21 | >
22 | );
23 |
24 | // Get the container for your app
25 | const container = document.getElementById('react-app');
26 |
27 | // Check if the container exists to avoid null errors
28 | if (container) {
29 | // Create a root
30 | const root = ReactDOM.createRoot(container);
31 |
32 | // Render the Main component
33 | root.render();
34 | } else {
35 | console.error('Failed to find the root element');
36 | }
37 |
--------------------------------------------------------------------------------
/drupal/config/filter.format.full_html.yml:
--------------------------------------------------------------------------------
1 | uuid: 436694fe-e123-4d8f-8a82-261039aa5b19
2 | langcode: en
3 | status: true
4 | dependencies:
5 | module:
6 | - editor
7 | _core:
8 | default_config_hash: vpZysv3RHJjhYzq7O_q5q8mVlHdrSzKELmXyiX6RtV0
9 | name: 'Full HTML'
10 | format: full_html
11 | weight: 2
12 | filters:
13 | editor_file_reference:
14 | id: editor_file_reference
15 | provider: editor
16 | status: true
17 | weight: 11
18 | settings: { }
19 | filter_align:
20 | id: filter_align
21 | provider: filter
22 | status: true
23 | weight: 8
24 | settings: { }
25 | filter_caption:
26 | id: filter_caption
27 | provider: filter
28 | status: true
29 | weight: 9
30 | settings: { }
31 | filter_htmlcorrector:
32 | id: filter_htmlcorrector
33 | provider: filter
34 | status: true
35 | weight: 10
36 | settings: { }
37 | filter_image_lazy_load:
38 | id: filter_image_lazy_load
39 | provider: filter
40 | status: true
41 | weight: 15
42 | settings: { }
43 |
--------------------------------------------------------------------------------
/drupal/config/views.settings.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: 8oDr9oPVb_ostrNnVV6V7VdphwoH_u-NqTS0u7SE7qc
3 | display_extenders: { }
4 | sql_signature: false
5 | ui:
6 | show:
7 | additional_queries: false
8 | advanced_column: false
9 | default_display: false
10 | performance_statistics: false
11 | preview_information: true
12 | sql_query:
13 | enabled: false
14 | where: above
15 | display_embed: false
16 | always_live_preview: true
17 | exposed_filter_any_label: old_any
18 | field_rewrite_elements:
19 | div: DIV
20 | span: SPAN
21 | h1: H1
22 | h2: H2
23 | h3: H3
24 | h4: H4
25 | h5: H5
26 | h6: H6
27 | p: P
28 | header: HEADER
29 | footer: FOOTER
30 | article: ARTICLE
31 | section: SECTION
32 | aside: ASIDE
33 | details: DETAILS
34 | blockquote: BLOCKQUOTE
35 | figure: FIGURE
36 | address: ADDRESS
37 | code: CODE
38 | pre: PRE
39 | var: VAR
40 | samp: SAMP
41 | kbd: KBD
42 | strong: STRONG
43 | em: EM
44 | del: DEL
45 | ins: INS
46 | q: Q
47 | s: S
48 |
--------------------------------------------------------------------------------
/drupal/config/core.extension.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: 4GIX5Esnc_umpXUBj4IIocRX7Mt5fPhm4AgXfE3E56E
3 | module:
4 | announcements_feed: 0
5 | automated_cron: 0
6 | big_pipe: 0
7 | block: 0
8 | block_content: 0
9 | breakpoint: 0
10 | ckeditor5: 0
11 | comment: 0
12 | config: 0
13 | consumers: 0
14 | contact: 0
15 | contextual: 0
16 | datetime: 0
17 | dblog: 0
18 | devel_generate: 0
19 | dynamic_page_cache: 0
20 | editor: 0
21 | field: 0
22 | field_ui: 0
23 | file: 0
24 | filter: 0
25 | help: 0
26 | history: 0
27 | image: 0
28 | jsonapi: 0
29 | link: 0
30 | menu_link_content: 0
31 | menu_ui: 0
32 | mysql: 0
33 | node: 0
34 | options: 0
35 | page_cache: 0
36 | path: 0
37 | path_alias: 0
38 | search: 0
39 | serialization: 0
40 | shortcut: 0
41 | simple_oauth: 0
42 | system: 0
43 | taxonomy: 0
44 | text: 0
45 | toolbar: 0
46 | update: 0
47 | user: 0
48 | views_ui: 0
49 | views: 10
50 | standard: 1000
51 | theme:
52 | olivero: 0
53 | claro: 0
54 | react_example_theme: 0
55 | profile: standard
56 |
--------------------------------------------------------------------------------
/drupal/config/field.field.node.article.field_image.yml:
--------------------------------------------------------------------------------
1 | uuid: beec865e-6d50-4e59-8221-cd57f179f389
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - field.storage.node.field_image
7 | - node.type.article
8 | module:
9 | - image
10 | _core:
11 | default_config_hash: j0riO_-77ZFWNLtj0iJ31HnohiNjdn8HUL86RueCF-M
12 | id: node.article.field_image
13 | field_name: field_image
14 | entity_type: node
15 | bundle: article
16 | label: Image
17 | description: ''
18 | required: false
19 | translatable: true
20 | default_value: { }
21 | default_value_callback: ''
22 | settings:
23 | handler: 'default:file'
24 | handler_settings: { }
25 | file_directory: '[date:custom:Y]-[date:custom:m]'
26 | file_extensions: 'png gif jpg jpeg webp'
27 | max_filesize: ''
28 | max_resolution: ''
29 | min_resolution: ''
30 | alt_field: true
31 | alt_field_required: true
32 | title_field: false
33 | title_field_required: false
34 | default_image:
35 | uuid: null
36 | alt: ''
37 | title: ''
38 | width: null
39 | height: null
40 | field_type: image
41 |
--------------------------------------------------------------------------------
/drupal/config/field.field.user.user.user_picture.yml:
--------------------------------------------------------------------------------
1 | uuid: 9b68a291-d067-456f-a457-86a9a9e96980
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - field.storage.user.user_picture
7 | module:
8 | - image
9 | - user
10 | _core:
11 | default_config_hash: TE3gYVzd6g0deXqUl8SEu2azHwVG-SdXm3kwbrz0kHw
12 | id: user.user.user_picture
13 | field_name: user_picture
14 | entity_type: user
15 | bundle: user
16 | label: Picture
17 | description: 'Your virtual face or picture.'
18 | required: false
19 | translatable: true
20 | default_value: { }
21 | default_value_callback: ''
22 | settings:
23 | handler: 'default:file'
24 | handler_settings: { }
25 | file_directory: 'pictures/[date:custom:Y]-[date:custom:m]'
26 | file_extensions: 'png gif jpg jpeg webp'
27 | max_filesize: ''
28 | max_resolution: ''
29 | min_resolution: ''
30 | alt_field: false
31 | alt_field_required: false
32 | title_field: false
33 | title_field_required: false
34 | default_image:
35 | uuid: null
36 | alt: ''
37 | title: ''
38 | width: null
39 | height: null
40 | field_type: image
41 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/utils/fetch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Helper function wraps a normal fetch call with a fetch request that first
3 | * retrieves a CSRF token and then adds the token as an X-CSRF-Token header
4 | * to the options for desired fetch call.
5 | *
6 | * @param {string} csrfUrl
7 | * URL where we can retrieve a CSRF token for the current user.
8 | * @param {string} fetchUrl
9 | * URL to fetch with X-CSRF-Token header included.
10 | * @param {object} fetchOptions
11 | * Options to pass to fetch for the call to fetchUrl.
12 | */
13 | export const fetchWithCSRFToken = (csrfUrl, fetchUrl, fetchOptions) => {
14 |
15 | if (!fetchOptions.headers.get('X-CSRF-Token')) {
16 | return fetch(`${drupalSettings.path.baseUrl}${csrfUrl}`)
17 | .then(response => response.text())
18 | .then((csrfToken) => {
19 | fetchOptions.headers.append('X-CSRF-Token', csrfToken);
20 | return fetch(`${drupalSettings.path.baseUrl}${fetchUrl}`, fetchOptions);
21 | });
22 | }
23 | else {
24 | return fetch(`${drupalSettings.path.baseUrl}${fetchUrl}`, fetchOptions);
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/drupal/config/user.role.content_editor.yml:
--------------------------------------------------------------------------------
1 | uuid: d9ab4139-38dd-491f-8cec-88ab6b706e39
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - node.type.article
7 | - node.type.page
8 | - taxonomy.vocabulary.tags
9 | module:
10 | - comment
11 | - contextual
12 | - file
13 | - node
14 | - path
15 | - system
16 | - taxonomy
17 | - toolbar
18 | _core:
19 | default_config_hash: e_wG1u7SrGaJbY2-Szgbgl-c8T3kdirU3uTNq2c_QJQ
20 | id: content_editor
21 | label: 'Content editor'
22 | weight: 2
23 | is_admin: false
24 | permissions:
25 | - 'access administration pages'
26 | - 'access content overview'
27 | - 'access contextual links'
28 | - 'access files overview'
29 | - 'access toolbar'
30 | - 'administer url aliases'
31 | - 'create article content'
32 | - 'create page content'
33 | - 'create terms in tags'
34 | - 'create url aliases'
35 | - 'delete article revisions'
36 | - 'delete own article content'
37 | - 'delete own files'
38 | - 'delete own page content'
39 | - 'delete page revisions'
40 | - 'edit own article content'
41 | - 'edit own comments'
42 | - 'edit own page content'
43 | - 'edit terms in tags'
44 | - 'revert all revisions'
45 | - 'view all revisions'
46 | - 'view own unpublished content'
47 | - 'view the administration theme'
48 |
--------------------------------------------------------------------------------
/react-decoupled/src/components/NodeDelete.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { fetchWithAuthentication } from '../utils/oauth';
3 |
4 | const NodeDelete = ({ id, title, onSuccess }) => {
5 | function doConfirm() {
6 | return window.confirm(`Are you sure you want to delete ${title}?`);
7 | }
8 |
9 | function doDelete() {
10 | const fetchUrl = `/jsonapi/node/article/${id}`;
11 | const fetchOptions = {
12 | method: 'DELETE',
13 | credentials: 'same-origin',
14 | headers: new Headers({
15 | 'Accept': 'application/vnd.api+json',
16 | 'Content-Type': 'application/vnd.api+json',
17 | 'Cache': 'no-cache'
18 | }),
19 | };
20 |
21 | try {
22 | fetchWithAuthentication(fetchUrl, fetchOptions)
23 | .then((response) => {
24 | // Should be 204. If so, call the onSuccess callback.
25 | if (response.status === 204) {
26 | // Do any additional work here.
27 | }
28 | });
29 | } catch (error) {
30 | console.log('API error', error);
31 | }
32 |
33 | if (typeof onSuccess === 'function') {
34 | onSuccess(id);
35 | }
36 | }
37 |
38 | return (
39 |
42 | );
43 | };
44 |
45 | export default NodeDelete;
46 |
--------------------------------------------------------------------------------
/react-decoupled-vite/src/components/NodeDelete.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { fetchWithAuthentication } from '../utils/oauth';
3 |
4 | const NodeDelete = ({ id, title, onSuccess }) => {
5 | function doConfirm() {
6 | return window.confirm(`Are you sure you want to delete ${title}?`);
7 | }
8 |
9 | function doDelete() {
10 | const fetchUrl = `/jsonapi/node/article/${id}`;
11 | const fetchOptions = {
12 | method: 'DELETE',
13 | credentials: 'same-origin',
14 | headers: new Headers({
15 | 'Accept': 'application/vnd.api+json',
16 | 'Content-Type': 'application/vnd.api+json',
17 | 'Cache': 'no-cache'
18 | }),
19 | };
20 |
21 | try {
22 | fetchWithAuthentication(fetchUrl, fetchOptions)
23 | .then((response) => {
24 | // Should be 204. If so, call the onSuccess callback.
25 | if (response.status === 204) {
26 | // Do any additional work here.
27 | }
28 | });
29 | } catch (error) {
30 | console.log('API error', error);
31 | }
32 |
33 | if (typeof onSuccess === 'function') {
34 | onSuccess(id);
35 | }
36 | }
37 |
38 | return (
39 |
42 | );
43 | };
44 |
45 | export default NodeDelete;
46 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/react_example_theme.libraries.yml:
--------------------------------------------------------------------------------
1 | # Include React app assets compiled by a JavaScript toolchain.
2 | react_app:
3 | version: VERSION
4 | js:
5 | js/dist/main.min.js: {minified: true}
6 |
7 | # Include development mode React app assets compiled by a JavaScript toolchain.
8 | # @see react_example_theme_page_attachments_alter() in
9 | # react_example_theme.theme.
10 | react_app_dev:
11 | version: VERSION
12 | js:
13 | js/dist_dev/main.min.js: {minified: true}
14 |
15 | # Include the React, ReactDOM, and Babel runtime scripts from a CDN.
16 | # Use this if you're not using a toolchain to bundle your custom JS code. The
17 | # best way is to include this as a dependency of another library.
18 | react_external:
19 | version: 16.x
20 | header: true
21 | js:
22 | https://unpkg.com/react@16/umd/react.production.min.js: { external: true, minified: true }
23 | https://unpkg.com/react-dom@16/umd/react-dom.production.min.js: { external: true, minified: true }
24 | https://unpkg.com/babel-standalone@6/babel.min.js: { external: true, minified: true }
25 |
26 | # Include a React/JavaScript application without using a JavaScript toolchain.
27 | react_app_simple:
28 | version: VERSION
29 | footer: true
30 | js:
31 | js/index.js: { attributes: { type: text/babel } }
32 | dependencies:
33 | - react_example_theme/react_external
34 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/components/NodeDelete.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { fetchWithCSRFToken } from "../utils/fetch";
3 |
4 | const NodeDelete = ({ id, title, onSuccess }) => {
5 | function doConfirm() {
6 | return window.confirm(`Are you sure you want to delete ${title}?`);
7 | }
8 |
9 | function doDelete() {
10 | const csrfUrl = `/session/token?_format=json`;
11 | const fetchUrl = `/jsonapi/node/article/${id}`;
12 | const fetchOptions = {
13 | method: 'DELETE',
14 | credentials: 'same-origin',
15 | headers: new Headers({
16 | 'Accept': 'application/vnd.api+json',
17 | 'Content-Type': 'application/vnd.api+json',
18 | 'Cache': 'no-cache'
19 | }),
20 | };
21 |
22 | try {
23 | fetchWithCSRFToken(csrfUrl, fetchUrl, fetchOptions)
24 | .then((response) => {
25 | // Should be 204. If so, call the onSuccess callback.
26 | if (response.status === 204) {
27 | // Do any additional work here.
28 | }
29 | });
30 | } catch (error) {
31 | console.log('API error', error);
32 | }
33 |
34 | if (typeof onSuccess === 'function') {
35 | onSuccess(id);
36 | }
37 | }
38 |
39 | return (
40 |
43 | );
44 | };
45 |
46 | export default NodeDelete;
47 |
--------------------------------------------------------------------------------
/drupal/config/core.entity_view_display.node.article.teaser.yml:
--------------------------------------------------------------------------------
1 | uuid: 54c594e8-6461-4835-beb3-4824a814092d
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - core.entity_view_mode.node.teaser
7 | - field.field.node.article.body
8 | - field.field.node.article.comment
9 | - field.field.node.article.field_image
10 | - field.field.node.article.field_tags
11 | - image.style.medium
12 | - node.type.article
13 | module:
14 | - image
15 | - text
16 | - user
17 | _core:
18 | default_config_hash: O8PxzfG8DOHHRu6M23kwR6TDPq_MNfYQ10Mp367ICUQ
19 | id: node.article.teaser
20 | targetEntityType: node
21 | bundle: article
22 | mode: teaser
23 | content:
24 | body:
25 | type: text_summary_or_trimmed
26 | label: hidden
27 | settings:
28 | trim_length: 600
29 | third_party_settings: { }
30 | weight: 0
31 | region: content
32 | field_image:
33 | type: image
34 | label: hidden
35 | settings:
36 | image_link: content
37 | image_style: medium
38 | image_loading:
39 | attribute: lazy
40 | third_party_settings: { }
41 | weight: -1
42 | region: content
43 | field_tags:
44 | type: entity_reference_label
45 | label: above
46 | settings:
47 | link: true
48 | third_party_settings: { }
49 | weight: 10
50 | region: content
51 | links:
52 | weight: 100
53 | region: content
54 | hidden:
55 | comment: true
56 |
--------------------------------------------------------------------------------
/react-decoupled-vite/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/drupal/config/filter.format.basic_html.yml:
--------------------------------------------------------------------------------
1 | uuid: 3cdfdd97-a3c1-43c5-b9c2-36349e5e2a50
2 | langcode: en
3 | status: true
4 | dependencies:
5 | module:
6 | - editor
7 | _core:
8 | default_config_hash: mclCbTlJwWJORez4Y1eX2MqA0aGjSMAoJb3TaBABcK8
9 | name: 'Basic HTML'
10 | format: basic_html
11 | weight: 0
12 | filters:
13 | editor_file_reference:
14 | id: editor_file_reference
15 | provider: editor
16 | status: true
17 | weight: 11
18 | settings: { }
19 | filter_align:
20 | id: filter_align
21 | provider: filter
22 | status: true
23 | weight: 7
24 | settings: { }
25 | filter_caption:
26 | id: filter_caption
27 | provider: filter
28 | status: true
29 | weight: 8
30 | settings: { }
31 | filter_html:
32 | id: filter_html
33 | provider: filter
34 | status: true
35 | weight: -10
36 | settings:
37 | allowed_html: '
-
-
-
'
38 | filter_html_help: false
39 | filter_html_nofollow: false
40 | filter_html_image_secure:
41 | id: filter_html_image_secure
42 | provider: filter
43 | status: true
44 | weight: 9
45 | settings: { }
46 | filter_image_lazy_load:
47 | id: filter_image_lazy_load
48 | provider: filter
49 | status: true
50 | weight: 15
51 | settings: { }
52 |
--------------------------------------------------------------------------------
/drupal/config/editor.editor.basic_html.yml:
--------------------------------------------------------------------------------
1 | uuid: 86703ef5-67bb-4dc4-8d0a-c3e77e7f4491
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - filter.format.basic_html
7 | module:
8 | - ckeditor5
9 | _core:
10 | default_config_hash: Qi2tIe-L97EutlMmhEvhsNxZOpOoA-RH82c4BQb5n4A
11 | format: basic_html
12 | editor: ckeditor5
13 | settings:
14 | toolbar:
15 | items:
16 | - bold
17 | - italic
18 | - '|'
19 | - link
20 | - '|'
21 | - bulletedList
22 | - numberedList
23 | - '|'
24 | - blockQuote
25 | - drupalInsertImage
26 | - '|'
27 | - heading
28 | - code
29 | - '|'
30 | - sourceEditing
31 | plugins:
32 | ckeditor5_heading:
33 | enabled_headings:
34 | - heading2
35 | - heading3
36 | - heading4
37 | - heading5
38 | - heading6
39 | ckeditor5_imageResize:
40 | allow_resize: true
41 | ckeditor5_list:
42 | properties:
43 | reversed: false
44 | startIndex: true
45 | multiBlock: true
46 | ckeditor5_sourceEditing:
47 | allowed_tags:
48 | - ''
49 | - ''
50 | - '- '
51 | - '
- '
52 | - ''
53 | - '
'
54 | - ''
55 | - ''
56 | - ''
57 | - ''
58 | - ''
59 | - ''
60 | - ''
61 | image_upload:
62 | status: true
63 | scheme: public
64 | directory: inline-images
65 | max_size: null
66 | max_dimensions:
67 | width: null
68 | height: null
69 |
--------------------------------------------------------------------------------
/drupal/config/core.entity_view_display.node.article.default.yml:
--------------------------------------------------------------------------------
1 | uuid: a9e39129-3157-4ab2-9524-0a66a82378b7
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - core.entity_view_display.comment.comment.default
7 | - field.field.node.article.body
8 | - field.field.node.article.comment
9 | - field.field.node.article.field_image
10 | - field.field.node.article.field_tags
11 | - image.style.wide
12 | - node.type.article
13 | module:
14 | - comment
15 | - image
16 | - text
17 | - user
18 | _core:
19 | default_config_hash: br6izr-iGEu--JvNbCJNtOBpRnxpFLXfoV5y61U9Nqc
20 | id: node.article.default
21 | targetEntityType: node
22 | bundle: article
23 | mode: default
24 | content:
25 | body:
26 | type: text_default
27 | label: hidden
28 | settings: { }
29 | third_party_settings: { }
30 | weight: 0
31 | region: content
32 | comment:
33 | type: comment_default
34 | label: above
35 | settings:
36 | view_mode: default
37 | pager_id: 0
38 | third_party_settings: { }
39 | weight: 110
40 | region: content
41 | field_image:
42 | type: image
43 | label: hidden
44 | settings:
45 | image_link: ''
46 | image_style: wide
47 | image_loading:
48 | attribute: eager
49 | third_party_settings: { }
50 | weight: -1
51 | region: content
52 | field_tags:
53 | type: entity_reference_label
54 | label: above
55 | settings:
56 | link: true
57 | third_party_settings: { }
58 | weight: 10
59 | region: content
60 | links:
61 | settings: { }
62 | third_party_settings: { }
63 | weight: 100
64 | region: content
65 | hidden: { }
66 |
--------------------------------------------------------------------------------
/react-decoupled/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/drupal/config/core.entity_form_display.node.page.default.yml:
--------------------------------------------------------------------------------
1 | uuid: 9009298d-d006-45c8-bd2c-f709022fa6b0
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - field.field.node.page.body
7 | - node.type.page
8 | module:
9 | - path
10 | - text
11 | _core:
12 | default_config_hash: SfpLhPExzvR0MgFp0Wp7CrmgEnhcqQ-fXIWFhbf4ue0
13 | id: node.page.default
14 | targetEntityType: node
15 | bundle: page
16 | mode: default
17 | content:
18 | body:
19 | type: text_textarea_with_summary
20 | weight: 31
21 | region: content
22 | settings:
23 | rows: 9
24 | summary_rows: 3
25 | placeholder: ''
26 | show_summary: false
27 | third_party_settings: { }
28 | created:
29 | type: datetime_timestamp
30 | weight: 10
31 | region: content
32 | settings: { }
33 | third_party_settings: { }
34 | path:
35 | type: path
36 | weight: 30
37 | region: content
38 | settings: { }
39 | third_party_settings: { }
40 | promote:
41 | type: boolean_checkbox
42 | weight: 15
43 | region: content
44 | settings:
45 | display_label: true
46 | third_party_settings: { }
47 | status:
48 | type: boolean_checkbox
49 | weight: 120
50 | region: content
51 | settings:
52 | display_label: true
53 | third_party_settings: { }
54 | sticky:
55 | type: boolean_checkbox
56 | weight: 16
57 | region: content
58 | settings:
59 | display_label: true
60 | third_party_settings: { }
61 | title:
62 | type: string_textfield
63 | weight: -5
64 | region: content
65 | settings:
66 | size: 60
67 | placeholder: ''
68 | third_party_settings: { }
69 | uid:
70 | type: entity_reference_autocomplete
71 | weight: 5
72 | region: content
73 | settings:
74 | match_operator: CONTAINS
75 | match_limit: 10
76 | size: 60
77 | placeholder: ''
78 | third_party_settings: { }
79 | hidden: { }
80 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3 | const isDevMode = process.env.NODE_ENV !== 'production';
4 |
5 | const PROXY = 'https://react-tutorials-2.ddev.site/';
6 | const PUBLIC_PATH = '/themes/react_example_theme/js/dist_dev/';
7 |
8 | const config = {
9 | entry: {
10 | main: [
11 | "./js/src/index.jsx"
12 | ]
13 | },
14 | devtool: (isDevMode) ? 'source-map' : false,
15 | mode: (isDevMode) ? 'development' : 'production',
16 | output: {
17 | path: isDevMode ? path.resolve(__dirname, "js/dist_dev") : path.resolve(__dirname, "js/dist"),
18 | filename: '[name].min.js',
19 | publicPath: PUBLIC_PATH
20 | },
21 | resolve: {
22 | extensions: ['.js', '.jsx'],
23 | },
24 | module: {
25 | rules: [
26 | {
27 | test: /\.jsx?$/,
28 | loader: 'babel-loader',
29 | exclude: /node_modules/,
30 | include: path.join(__dirname, 'js/src'),
31 | options: {
32 | // This is a feature of `babel-loader` for webpack (not Babel itself).
33 | // It enables caching results in ./node_modules/.cache/babel-loader/
34 | // directory for faster rebuilds.
35 | cacheDirectory: true,
36 | plugins: [
37 | isDevMode && require.resolve('react-refresh/babel')
38 | ].filter(Boolean),
39 | },
40 | }
41 | ],
42 | },
43 | plugins: [
44 | isDevMode && new ReactRefreshWebpackPlugin(),
45 | ].filter(Boolean),
46 | devServer: {
47 | port: 8181,
48 | hot: true,
49 | headers: { 'Access-Control-Allow-Origin': '*' },
50 | devMiddleware: {
51 | writeToDisk: true,
52 | },
53 | // Settings for http-proxy-middleware.
54 | proxy: [
55 | {
56 | index: '',
57 | context: ['/'],
58 | target: PROXY,
59 | publicPath: PUBLIC_PATH,
60 | secure: false,
61 | // These settings allow Drupal authentication to work, so you can sign
62 | // in to your Drupal site via the proxy. They require some corresponding
63 | // configuration in Drupal's settings.php.
64 | changeOrigin: true,
65 | xfwd: true
66 | }
67 | ]
68 | },
69 | };
70 |
71 | module.exports = config;
72 |
--------------------------------------------------------------------------------
/react-decoupled/src/components/DrupalProjectStats.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const DrupalProjectStats = ({ projectName }) => {
5 | const [project, setProject] = useState(projectName);
6 | const [usage, setUsage] = useState(null);
7 |
8 | useEffect(() => {
9 | setUsage(false);
10 | const data = fetch(
11 | `https://www.drupal.org/api-d7/node.json?field_project_machine_name=${project}`
12 | )
13 | .then(response => response.json())
14 | .then(result => {
15 | if (result.list[0].project_usage) {
16 | setUsage(result.list[0].project_usage);
17 | }
18 | })
19 | .catch(error => console.log("error", error));
20 | }, [project]);
21 |
22 | return (
23 |
24 |
25 | Choose a project:
26 |
27 |
28 |
29 |
30 |
31 |
32 | Usage stats for {project} by version:
33 | {usage ? (
34 |
35 | {Object.keys(usage).map(key => (
36 |
37 | ))}
38 |
39 | ) : (
40 | fetching data ...
41 | )}
42 |
43 |
44 | );
45 | };
46 |
47 | // Provide type checking for props. Think of this as documentation for what
48 | // props a component accepts.
49 | // https://reactjs.org/docs/typechecking-with-proptypes.html
50 | DrupalProjectStats.propTypes = {
51 | projectName: PropTypes.string.required
52 | };
53 |
54 | // Set a default value for any required props.
55 | DrupalProjectStats.defaultProps = {
56 | projectName: 'drupal',
57 | };
58 |
59 | /**
60 | * Another component; This one displays usage statistics for a specific version
61 | * of Drupal. It's not exported, so it can only be used in this files scope.
62 | * Breaking things up like this can help make your code easier to maintain.
63 | */
64 | const StatsItem = ({ count, version }) => (
65 |
-
66 | {version}: {count}
67 |
68 | );
69 |
70 | StatsItem.propTypes = {
71 | count: PropTypes.number,
72 | version: PropTypes.string,
73 | };
74 |
75 | export default DrupalProjectStats;
76 |
--------------------------------------------------------------------------------
/react-decoupled-vite/src/components/DrupalProjectStats.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const DrupalProjectStats = ({ projectName }) => {
5 | const [project, setProject] = useState(projectName);
6 | const [usage, setUsage] = useState(null);
7 |
8 | useEffect(() => {
9 | setUsage(false);
10 | const data = fetch(
11 | `https://www.drupal.org/api-d7/node.json?field_project_machine_name=${project}`
12 | )
13 | .then(response => response.json())
14 | .then(result => {
15 | if (result.list[0].project_usage) {
16 | setUsage(result.list[0].project_usage);
17 | }
18 | })
19 | .catch(error => console.log("error", error));
20 | }, [project]);
21 |
22 | return (
23 |
24 |
25 | Choose a project:
26 |
27 |
28 |
29 |
30 |
31 |
32 | Usage stats for {project} by version:
33 | {usage ? (
34 |
35 | {Object.keys(usage).map(key => (
36 |
37 | ))}
38 |
39 | ) : (
40 | fetching data ...
41 | )}
42 |
43 |
44 | );
45 | };
46 |
47 | // Provide type checking for props. Think of this as documentation for what
48 | // props a component accepts.
49 | // https://reactjs.org/docs/typechecking-with-proptypes.html
50 | DrupalProjectStats.propTypes = {
51 | projectName: PropTypes.string.required
52 | };
53 |
54 | // Set a default value for any required props.
55 | DrupalProjectStats.defaultProps = {
56 | projectName: 'drupal',
57 | };
58 |
59 | /**
60 | * Another component; This one displays usage statistics for a specific version
61 | * of Drupal. It's not exported, so it can only be used in this files scope.
62 | * Breaking things up like this can help make your code easier to maintain.
63 | */
64 | const StatsItem = ({ count, version }) => (
65 | -
66 | {version}: {count}
67 |
68 | );
69 |
70 | StatsItem.propTypes = {
71 | count: PropTypes.number,
72 | version: PropTypes.string,
73 | };
74 |
75 | export default DrupalProjectStats;
76 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/components/DrupalProjectStats.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const DrupalProjectStats = ({ projectName }) => {
5 | const [project, setProject] = useState(projectName);
6 | const [usage, setUsage] = useState(null);
7 |
8 | useEffect(() => {
9 | setUsage(false);
10 | const data = fetch(
11 | `https://www.drupal.org/api-d7/node.json?field_project_machine_name=${project}`
12 | )
13 | .then(response => response.json())
14 | .then(result => {
15 | if (result.list[0].project_usage) {
16 | setUsage(result.list[0].project_usage);
17 | }
18 | })
19 | .catch(error => console.log("error", error));
20 | }, [project]);
21 |
22 | return (
23 |
24 |
25 | Choose a project:
26 |
27 |
28 |
29 |
30 |
31 |
32 | Usage stats for {project} by version:
33 | {usage ? (
34 |
35 | {Object.keys(usage).map(key => (
36 |
37 | ))}
38 |
39 | ) : (
40 | fetching data ...
41 | )}
42 |
43 |
44 | );
45 | };
46 |
47 | // Provide type checking for props. Think of this as documentation for what
48 | // props a component accepts.
49 | // https://reactjs.org/docs/typechecking-with-proptypes.html
50 | DrupalProjectStats.propTypes = {
51 | projectName: PropTypes.string.required
52 | };
53 |
54 | // Set a default value for any required props.
55 | DrupalProjectStats.defaultProps = {
56 | projectName: 'drupal',
57 | };
58 |
59 | /**
60 | * Another component; This one displays usage statistics for a specific version
61 | * of Drupal. It's not exported, so it can only be used in this files scope.
62 | * Breaking things up like this can help make your code easier to maintain.
63 | */
64 | const StatsItem = ({ count, version }) => (
65 | -
66 | {version}: {count}
67 |
68 | );
69 |
70 | StatsItem.propTypes = {
71 | count: PropTypes.number,
72 | version: PropTypes.string,
73 | };
74 |
75 | export default DrupalProjectStats;
76 |
--------------------------------------------------------------------------------
/react-decoupled/src/components/NodeListOnly.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 |
3 | function isValidData(data) {
4 | if (data === null) {
5 | return false;
6 | }
7 | if (data.data === undefined ||
8 | data.data === null ||
9 | data.data.length === 0 ) {
10 | return false;
11 | }
12 | return true;
13 | }
14 |
15 | const NodeItem = ({drupal_internal__nid, title}) => (
16 |
17 | {title}
18 |
19 | );
20 |
21 | const NoData = () => (
22 | No articles found.
23 | );
24 |
25 | const NodeListOnly = () => {
26 | const [content, setContent] = useState(false);
27 | const [filter, setFilter] = useState(null);
28 |
29 | useEffect(() => {
30 | // This should point to your local Drupal instance. Alternatively, for React
31 | // applications embedded in a Drupal theme or module this could also be set
32 | // to a relative path.
33 | const API_ROOT = '/jsonapi/';
34 | const url = `${API_ROOT}node/article?fields[node--article]=id,drupal_internal__nid,title,body&sort=-created&page[limit]=10`;
35 |
36 | const headers = new Headers({
37 | Accept: 'application/vnd.api+json',
38 | });
39 |
40 | fetch(url, {headers})
41 | .then((response) => response.json())
42 | .then((data) => {
43 | if (isValidData(data)) {
44 | setContent(data.data)
45 | }
46 | })
47 | .catch(err => console.log('There was an error accessing the API', err));
48 | }, []);
49 |
50 | return (
51 |
52 | Site content
53 | {content ? (
54 | <>
55 |
56 | setFilter(event.target.value.toLowerCase()))}
61 | />
62 |
63 | {
64 | content.filter((item) => {
65 | if (!filter) {
66 | return item;
67 | }
68 |
69 | if (filter && (item.attributes.title.toLowerCase().includes(filter) || item.attributes.body.value.toLowerCase().includes(filter))) {
70 | return item;
71 | }
72 | }).map((item) => )
73 | }
74 | >
75 | ) : (
76 |
77 | )}
78 |
79 | );
80 | };
81 |
82 | export default NodeListOnly;
83 |
--------------------------------------------------------------------------------
/react-decoupled-vite/src/components/NodeListOnly.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 |
3 | function isValidData(data) {
4 | if (data === null) {
5 | return false;
6 | }
7 | if (data.data === undefined ||
8 | data.data === null ||
9 | data.data.length === 0 ) {
10 | return false;
11 | }
12 | return true;
13 | }
14 |
15 | const NodeItem = ({drupal_internal__nid, title}) => (
16 |
17 | {title}
18 |
19 | );
20 |
21 | const NoData = () => (
22 | No articles found.
23 | );
24 |
25 | const NodeListOnly = () => {
26 | const [content, setContent] = useState(false);
27 | const [filter, setFilter] = useState(null);
28 |
29 | useEffect(() => {
30 | // This should point to your local Drupal instance. Alternatively, for React
31 | // applications embedded in a Drupal theme or module this could also be set
32 | // to a relative path.
33 | const API_ROOT = '/jsonapi/';
34 | const url = `${API_ROOT}node/article?fields[node--article]=id,drupal_internal__nid,title,body&sort=-created&page[limit]=10`;
35 |
36 | const headers = new Headers({
37 | Accept: 'application/vnd.api+json',
38 | });
39 |
40 | fetch(url, {headers})
41 | .then((response) => response.json())
42 | .then((data) => {
43 | if (isValidData(data)) {
44 | setContent(data.data)
45 | }
46 | })
47 | .catch(err => console.log('There was an error accessing the API', err));
48 | }, []);
49 |
50 | return (
51 |
52 | Site content
53 | {content ? (
54 | <>
55 |
56 | setFilter(event.target.value.toLowerCase()))}
61 | />
62 |
63 | {
64 | content.filter((item) => {
65 | if (!filter) {
66 | return item;
67 | }
68 |
69 | if (filter && (item.attributes.title.toLowerCase().includes(filter) || item.attributes.body.value.toLowerCase().includes(filter))) {
70 | return item;
71 | }
72 | }).map((item) => )
73 | }
74 | >
75 | ) : (
76 |
77 | )}
78 |
79 | );
80 | };
81 |
82 | export default NodeListOnly;
83 |
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/components/NodeListOnly.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 |
3 | function isValidData(data) {
4 | if (data === null) {
5 | return false;
6 | }
7 | if (data.data === undefined ||
8 | data.data === null ||
9 | data.data.length === 0 ) {
10 | return false;
11 | }
12 | return true;
13 | }
14 |
15 | const NodeItem = ({drupal_internal__nid, title}) => (
16 |
17 | {title}
18 |
19 | );
20 |
21 | const NoData = () => (
22 | No articles found.
23 | );
24 |
25 | const NodeListOnly = () => {
26 | const [content, setContent] = useState(false);
27 | const [filter, setFilter] = useState(null);
28 |
29 | useEffect(() => {
30 | // This should point to your local Drupal instance. Alternatively, for React
31 | // applications embedded in a Drupal theme or module this could also be set
32 | // to a relative path.
33 | const API_ROOT = '/jsonapi/';
34 | const url = `${API_ROOT}node/article?fields[node--article]=id,drupal_internal__nid,title,body&sort=-created&page[limit]=10`;
35 |
36 | const headers = new Headers({
37 | Accept: 'application/vnd.api+json',
38 | });
39 |
40 | fetch(url, {headers})
41 | .then((response) => response.json())
42 | .then((data) => {
43 | if (isValidData(data)) {
44 | setContent(data.data)
45 | }
46 | })
47 | .catch(err => console.log('There was an error accessing the API', err));
48 | }, []);
49 |
50 | return (
51 |
52 | Site content
53 | {content ? (
54 | <>
55 |
56 | setFilter(event.target.value.toLowerCase()))}
61 | />
62 |
63 | {
64 | content.filter((item) => {
65 | if (!filter) {
66 | return item;
67 | }
68 |
69 | if (filter && (item.attributes.title.toLowerCase().includes(filter) || item.attributes.body.value.toLowerCase().includes(filter))) {
70 | return item;
71 | }
72 | }).map((item) => )
73 | }
74 | >
75 | ) : (
76 |
77 | )}
78 |
79 | );
80 | };
81 |
82 | export default NodeListOnly;
83 |
--------------------------------------------------------------------------------
/drupal/config/editor.editor.full_html.yml:
--------------------------------------------------------------------------------
1 | uuid: fd5e9c64-be1d-4b8d-a011-28b9de20f587
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - filter.format.full_html
7 | module:
8 | - ckeditor5
9 | _core:
10 | default_config_hash: lurcn8yf33g3Dkzdc55cTfJa9salGp9ytSYyG1s7QMI
11 | format: full_html
12 | editor: ckeditor5
13 | settings:
14 | toolbar:
15 | items:
16 | - bold
17 | - italic
18 | - strikethrough
19 | - superscript
20 | - subscript
21 | - removeFormat
22 | - '|'
23 | - link
24 | - '|'
25 | - bulletedList
26 | - numberedList
27 | - '|'
28 | - blockQuote
29 | - drupalInsertImage
30 | - insertTable
31 | - horizontalLine
32 | - '|'
33 | - heading
34 | - codeBlock
35 | - '|'
36 | - sourceEditing
37 | plugins:
38 | ckeditor5_codeBlock:
39 | languages:
40 | -
41 | label: 'Plain text'
42 | language: plaintext
43 | -
44 | label: C
45 | language: c
46 | -
47 | label: 'C#'
48 | language: cs
49 | -
50 | label: C++
51 | language: cpp
52 | -
53 | label: CSS
54 | language: css
55 | -
56 | label: Diff
57 | language: diff
58 | -
59 | label: HTML
60 | language: html
61 | -
62 | label: Java
63 | language: java
64 | -
65 | label: JavaScript
66 | language: javascript
67 | -
68 | label: PHP
69 | language: php
70 | -
71 | label: Python
72 | language: python
73 | -
74 | label: Ruby
75 | language: ruby
76 | -
77 | label: TypeScript
78 | language: typescript
79 | -
80 | label: XML
81 | language: xml
82 | ckeditor5_heading:
83 | enabled_headings:
84 | - heading2
85 | - heading3
86 | - heading4
87 | - heading5
88 | - heading6
89 | ckeditor5_imageResize:
90 | allow_resize: true
91 | ckeditor5_list:
92 | properties:
93 | reversed: true
94 | startIndex: true
95 | multiBlock: true
96 | ckeditor5_sourceEditing:
97 | allowed_tags: { }
98 | image_upload:
99 | status: true
100 | scheme: public
101 | directory: inline-images
102 | max_size: null
103 | max_dimensions:
104 | width: null
105 | height: null
106 |
--------------------------------------------------------------------------------
/react-decoupled/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/drupal/config/core.entity_form_display.node.article.default.yml:
--------------------------------------------------------------------------------
1 | uuid: 0335668a-152e-445a-9215-cd69d06944fc
2 | langcode: en
3 | status: true
4 | dependencies:
5 | config:
6 | - field.field.node.article.body
7 | - field.field.node.article.comment
8 | - field.field.node.article.field_image
9 | - field.field.node.article.field_tags
10 | - image.style.thumbnail
11 | - node.type.article
12 | module:
13 | - comment
14 | - image
15 | - path
16 | - text
17 | _core:
18 | default_config_hash: ewbd6G2uX456-bgwseM2Q-KQG3RkASoyHmTh-XR3oLU
19 | id: node.article.default
20 | targetEntityType: node
21 | bundle: article
22 | mode: default
23 | content:
24 | body:
25 | type: text_textarea_with_summary
26 | weight: 2
27 | region: content
28 | settings:
29 | rows: 9
30 | summary_rows: 3
31 | placeholder: ''
32 | show_summary: false
33 | third_party_settings: { }
34 | comment:
35 | type: comment_default
36 | weight: 20
37 | region: content
38 | settings: { }
39 | third_party_settings: { }
40 | created:
41 | type: datetime_timestamp
42 | weight: 10
43 | region: content
44 | settings: { }
45 | third_party_settings: { }
46 | field_image:
47 | type: image_image
48 | weight: 1
49 | region: content
50 | settings:
51 | progress_indicator: throbber
52 | preview_image_style: thumbnail
53 | third_party_settings: { }
54 | field_tags:
55 | type: entity_reference_autocomplete_tags
56 | weight: 3
57 | region: content
58 | settings:
59 | match_operator: CONTAINS
60 | match_limit: 10
61 | size: 60
62 | placeholder: ''
63 | third_party_settings: { }
64 | path:
65 | type: path
66 | weight: 30
67 | region: content
68 | settings: { }
69 | third_party_settings: { }
70 | promote:
71 | type: boolean_checkbox
72 | weight: 15
73 | region: content
74 | settings:
75 | display_label: true
76 | third_party_settings: { }
77 | status:
78 | type: boolean_checkbox
79 | weight: 120
80 | region: content
81 | settings:
82 | display_label: true
83 | third_party_settings: { }
84 | sticky:
85 | type: boolean_checkbox
86 | weight: 16
87 | region: content
88 | settings:
89 | display_label: true
90 | third_party_settings: { }
91 | title:
92 | type: string_textfield
93 | weight: 0
94 | region: content
95 | settings:
96 | size: 60
97 | placeholder: ''
98 | third_party_settings: { }
99 | uid:
100 | type: entity_reference_autocomplete
101 | weight: 5
102 | region: content
103 | settings:
104 | match_operator: CONTAINS
105 | match_limit: 10
106 | size: 60
107 | placeholder: ''
108 | third_party_settings: { }
109 | hidden: { }
110 |
--------------------------------------------------------------------------------
/react-decoupled/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm run start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm run test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `yarn build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Drupalize.Me React & Drupal Code Examples
2 |
3 | This repo contains the example code used in the https://drupalize.me/series/drupal-8-and-reactjs series.
4 |
5 | **Note:** The `hello-world` branch contains a trimmed down version of the code that reflects the application as it would be after completing this tutorial covering how to [Connect React to a Drupal Theme or Module](https://drupalize.me/tutorial/connect-react-drupal-theme-or-module?p=3253).
6 |
7 | ## Drupal
8 |
9 | The /drupal directory contains a Drupal 10 project with basic configuration for JSON:API, and to demonstrate embedding a React application inside a Drupal theme or module.
10 |
11 | See the `drupal-9` branch for older examples including Drupal 8/9 and React 16.
12 |
13 | Install all the Drupal dependencies:
14 |
15 | ```bash
16 | cd drupal
17 | composer install
18 | ```
19 |
20 | Then import the database snapshot in _drupal/backup.sql.gz_.
21 |
22 | The default admin account is admin/admin. You can change this with `drush upwd admin {NEW_PASSWORD}`.
23 |
24 | If you're using [ddev](https://ddev.readthedocs.io/en/stable/) this contains ddev configuration and can be started with:
25 |
26 | ```bash
27 | cd drupal
28 | # Start ddev.
29 | ddev start
30 | # Install/update composer dependencies.
31 | ddev composer install
32 | # Import the database snapshot.
33 | ddev import-db --src=./backup.sql.gz
34 | # Run any necessary database updates, and re-import config.
35 | ddev exec "drush updb -y && drush cim -y && drush cr -y"
36 | ```
37 |
38 | The _/drupal/web/themes/react\_example\_theme/_ contains a custom theme with a React application embedded via the theme. This demonstrates using Webpack to bundle and transpile React/JavaScript files.
39 |
40 | ```bash
41 | cd /drupal/web/themes/react_example_theme;
42 | npm install
43 | # Build the production JS files:
44 | npm run build
45 | # Build development JS files
46 | npm run build:dev
47 | # Start webpack in --watch mode while doing development
48 | npm run start
49 | # Start webpack in --watch mode with hot module reloading
50 | # Requires some config to proxy requests to Drupal see .proxyrc
51 | npm run start:hmr
52 | ```
53 |
54 | ## Decoupled React Application
55 |
56 | The _/react-decoupled-vite_ directory contains an example decoupled React application built with Vite. It is built to interact with the API provided by Drupal installed in _/drupal_. This is the recommended approach.
57 |
58 | To download dependencies and start the local development server run:
59 |
60 | ```bash
61 | cd react-decoupled-vite
62 | npm install
63 | npm run dev
64 | ```
65 |
66 | You might need to update some configuration to make sure it points to your local Drupal installation. See _src/utils/oath.js_.
67 |
68 | For an older create-react-app based example see the _/react-decoupled_ directory which contains an example of a decoupled React application built with create-react-app.
69 |
70 | To download dependencies and start the local development server run:
71 |
72 | ```bash
73 | cd react-decoupled
74 | npm install
75 | npm run start
76 | ```
77 |
78 | You might need to update some configuration to make sure it points to your local Drupal installation.
79 |
--------------------------------------------------------------------------------
/drupal/web/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Drupal is an open source content management platform supporting a variety of
4 | websites ranging from personal weblogs to large community-driven websites. For
5 | more information, visit the Drupal website, [Drupal.org][Drupal.org], and join
6 | the [Drupal community][Drupal community].
7 |
8 | ## Contributing
9 |
10 | Drupal is developed on [Drupal.org][Drupal.org], the home of the international
11 | Drupal community since 2001!
12 |
13 | [Drupal.org][Drupal.org] hosts Drupal's [GitLab repository][GitLab repository],
14 | its [issue queue][issue queue], and its [documentation][documentation]. Before
15 | you start working on code, be sure to search the [issue queue][issue queue] and
16 | create an issue if your aren't able to find an existing issue.
17 |
18 | Every issue on Drupal.org automatically creates a new community-accessible fork
19 | that you can contribute to. Learn more about the code contribution process on
20 | the [Issue forks & merge requests page][issue forks].
21 |
22 | ## Usage
23 |
24 | For a brief introduction, see [USAGE.txt](/core/USAGE.txt). You can also find
25 | guides, API references, and more by visiting Drupal's [documentation
26 | page][documentation].
27 |
28 | You can quickly extend Drupal's core feature set by installing any of its
29 | [thousands of free and open source modules][modules]. With Drupal and its
30 | module ecosystem, you can often build most or all of what your project needs
31 | before writing a single line of code.
32 |
33 | ## Changelog
34 |
35 | Drupal keeps detailed [change records][changelog]. You can search Drupal's
36 | changes for a record of every notable breaking change and new feature since
37 | 2011.
38 |
39 | ## Security
40 |
41 | For a list of security announcements, see the [Security advisories
42 | page][Security advisories] (available as [an RSS feed][security RSS]). This
43 | page also describes how to subscribe to these announcements via email.
44 |
45 | For information about the Drupal security process, or to find out how to report
46 | a potential security issue to the Drupal security team, see the [Security team
47 | page][security team].
48 |
49 | ## Need a helping hand?
50 |
51 | Visit the [Support page][support] or browse [over a thousand Drupal
52 | providers][service providers] offering design, strategy, development, and
53 | hosting services.
54 |
55 | ## Legal matters
56 |
57 | Know your rights when using Drupal by reading Drupal core's
58 | [license](/core/LICENSE.txt).
59 |
60 | Learn about the [Drupal trademark and logo policy here][trademark].
61 |
62 | [Drupal.org]: https://www.drupal.org
63 | [Drupal community]: https://www.drupal.org/community
64 | [GitLab repository]: https://git.drupalcode.org/project/drupal
65 | [issue queue]: https://www.drupal.org/project/issues/drupal
66 | [issue forks]: https://www.drupal.org/drupalorg/docs/gitlab-integration/issue-forks-merge-requests
67 | [documentation]: https://www.drupal.org/documentation
68 | [changelog]: https://www.drupal.org/list-changes/drupal
69 | [modules]: https://www.drupal.org/project/project_module
70 | [security advisories]: https://www.drupal.org/security
71 | [security RSS]: https://www.drupal.org/security/rss.xml
72 | [security team]: https://www.drupal.org/drupal-security-team
73 | [service providers]: https://www.drupal.org/drupal-services
74 | [support]: https://www.drupal.org/support
75 | [trademark]: https://www.drupal.com/trademark
76 |
--------------------------------------------------------------------------------
/drupal/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "drupal/recommended-project",
3 | "description": "Project template for Drupal 8 projects with a relocated document root",
4 | "type": "project",
5 | "license": "GPL-2.0-or-later",
6 | "homepage": "https://www.drupal.org/project/drupal",
7 | "support": {
8 | "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
9 | "chat": "https://www.drupal.org/node/314178"
10 | },
11 | "repositories": [
12 | {
13 | "type": "composer",
14 | "url": "https://packages.drupal.org/8"
15 | }
16 | ],
17 | "require": {
18 | "composer/installers": "^1.2",
19 | "drupal/core-composer-scaffold": "^10",
20 | "drupal/core-project-message": "^10",
21 | "drupal/core-recommended": "^10",
22 | "drupal/devel": "^5.4",
23 | "drupal/simple_oauth": "6.0.x-dev@dev",
24 | "drush/drush": "^12.4"
25 | },
26 | "conflict": {
27 | "drupal/drupal": "*"
28 | },
29 | "minimum-stability": "dev",
30 | "prefer-stable": true,
31 | "config": {
32 | "sort-packages": true,
33 | "allow-plugins": {
34 | "composer/installers": true,
35 | "dealerdirect/phpcodesniffer-composer-installer": true,
36 | "drupal/core-composer-scaffold": true,
37 | "drupal/core-project-message": true,
38 | "php-http/discovery": true,
39 | "phpstan/extension-installer": true,
40 | "tbachert/spi": true
41 | }
42 | },
43 | "extra": {
44 | "drupal-scaffold": {
45 | "locations": {
46 | "web-root": "web/"
47 | }
48 | },
49 | "installer-paths": {
50 | "web/core": ["type:drupal-core"],
51 | "web/libraries/{$name}": ["type:drupal-library"],
52 | "web/modules/contrib/{$name}": ["type:drupal-module"],
53 | "web/profiles/contrib/{$name}": ["type:drupal-profile"],
54 | "web/themes/contrib/{$name}": ["type:drupal-theme"],
55 | "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
56 | "web/modules/custom/{$name}": ["type:drupal-custom-module"],
57 | "web/themes/custom/{$name}": ["type:drupal-custom-theme"]
58 | },
59 | "drupal-core-project-message": {
60 | "include-keys": ["homepage", "support"],
61 | "post-create-project-cmd-message": [
62 | " >",
63 | " Congratulations, you’ve installed the Drupal codebase >",
64 | " from the drupal/recommended-project template! >",
65 | " >",
66 | "",
67 | "Next steps>:",
68 |
69 | " * Install the site: https://www.drupal.org/docs/8/install",
70 | " * Read the user guide: https://www.drupal.org/docs/user_guide/en/index.html",
71 | " * Get support: https://www.drupal.org/support",
72 | " * Get involved with the Drupal community:",
73 | " https://www.drupal.org/getting-involved",
74 | " * Remove the plugin that prints this message:",
75 | " composer remove drupal/core-project-message"
76 | ]
77 | }
78 | },
79 | "require-dev": {
80 | "drupal/core-dev": "^10"
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/react-decoupled-vite/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Editor config
2 | .idea
3 |
4 | # Oauth keys
5 | /drupal/keys/private.key
6 | /drupal/keys/public.key
7 |
8 | # Ignore configuration files that may contain sensitive information
9 | /drupal/web/sites/*/*settings.*.php
10 | /drupal/web/sites/*/*services*.yml
11 |
12 | # Ignore paths that may contain user-generated content
13 | /drupal/web/sites/*/files
14 | /drupal/web/sites/*/public
15 | /drupal/web/sites/*/private
16 | /drupal/web/sites/*/files-public
17 | /drupal/web/sites/*/files-private
18 |
19 | # Ignore paths that may contain temporary files
20 | /drupal/web/sites/*/translations
21 | /drupal/web/sites/*/tmp
22 | /drupal/web/sites/*/cache
23 | /drupal/web/themes/react_example_theme/js/dist_dev
24 |
25 | # Ignore testing related files
26 | /drupal/web/sites/simpletest
27 |
28 | # Ignore drupal core (if not versioning drupal sources)
29 | /drupal/web/core
30 | /drupal/web/modules/README.txt
31 | /drupal/web/profiles/README.txt
32 | /drupal/web/sites/README.txt
33 | /drupal/web/sites/example.sites.php
34 | /drupal/web/sites/example.settings.local.php
35 | /drupal/web/sites/development.services.yml
36 | /drupal/web/themes/README.txt
37 | /drupal/web/vendor
38 | /drupal/web/.csslintrc
39 | /drupal/web/.editorconfig
40 | /drupal/web/.eslintignore
41 | /drupal/web/.eslintrc.json
42 | /drupal/web/.gitattributes
43 | /drupal/web/.htaccess
44 | /drupal/web/.ht.router.php
45 | /drupal/web/autoload.php
46 | /drupal/web/composer.json
47 | /drupal/web/composer.lock
48 | /drupal/web/example.gitignore
49 | /drupal/web/index.php
50 | /drupal/web/INSTALL.txt
51 | /drupal/web/LICENSE.txt
52 | /drupal/web/README.txt
53 | /drupal/web/robots.txt
54 | /drupal/web/update.php
55 | /drupal/web/web.config
56 |
57 | # Ignore files included by composer
58 | /drupal/vendor
59 | /drupal/web/modules/contrib
60 |
61 | # Node.js / JavaScript stuff
62 | # Logs
63 | logs
64 | *.log
65 | npm-debug.log*
66 | yarn-debug.log*
67 | yarn-error.log*
68 | lerna-debug.log*
69 |
70 | # Diagnostic reports (https://nodejs.org/api/report.html)
71 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
72 |
73 | # Runtime data
74 | pids
75 | *.pid
76 | *.seed
77 | *.pid.lock
78 |
79 | # Directory for instrumented libs generated by jscoverage/JSCover
80 | lib-cov
81 |
82 | # Coverage directory used by tools like istanbul
83 | coverage
84 | *.lcov
85 |
86 | # nyc test coverage
87 | .nyc_output
88 |
89 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
90 | .grunt
91 |
92 | # Bower dependency directory (https://bower.io/)
93 | bower_components
94 |
95 | # node-waf configuration
96 | .lock-wscript
97 |
98 | # Compiled binary addons (https://nodejs.org/api/addons.html)
99 | build/Release
100 |
101 | # Dependency directories
102 | node_modules/
103 | jspm_packages/
104 |
105 | # TypeScript v1 declaration files
106 | typings/
107 |
108 | # TypeScript cache
109 | *.tsbuildinfo
110 |
111 | # Optional npm cache directory
112 | .npm
113 |
114 | # Optional eslint cache
115 | .eslintcache
116 |
117 | # Microbundle cache
118 | .rpt2_cache/
119 | .rts2_cache_cjs/
120 | .rts2_cache_es/
121 | .rts2_cache_umd/
122 |
123 | # Optional REPL history
124 | .node_repl_history
125 |
126 | # Output of 'npm pack'
127 | *.tgz
128 |
129 | # Yarn Integrity file
130 | .yarn-integrity
131 |
132 | # dotenv environment variables file
133 | .env
134 | .env.test
135 |
136 | # parcel-bundler cache (https://parceljs.org/)
137 | .cache
138 | .parcel-cache
139 |
140 | # Next.js build output
141 | .next
142 |
143 | # Nuxt.js build / generate output
144 | .nuxt
145 | dist
146 |
147 | # Gatsby files
148 | .cache/
149 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
150 | # https://nextjs.org/blog/next-9-1#public-directory-support
151 | # public
152 |
153 | # vuepress build output
154 | .vuepress/dist
155 |
156 | # Serverless directories
157 | .serverless/
158 |
159 | # FuseBox cache
160 | .fusebox/
161 |
162 | # DynamoDB Local files
163 | .dynamodb/
164 |
165 | # TernJS port file
166 | .tern-port
167 |
--------------------------------------------------------------------------------
/drupal/.gitattributes:
--------------------------------------------------------------------------------
1 | # Drupal git normalization
2 | # @see https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
3 | # @see https://www.drupal.org/node/1542048
4 |
5 | # Normally these settings would be done with macro attributes for improved
6 | # readability and easier maintenance. However macros can only be defined at the
7 | # repository root directory. Drupal avoids making any assumptions about where it
8 | # is installed.
9 |
10 | # Define text file attributes.
11 | # - Treat them as text.
12 | # - Ensure no CRLF line-endings, neither on checkout nor on checkin.
13 | # - Detect whitespace errors.
14 | # - Exposed by default in `git diff --color` on the CLI.
15 | # - Validate with `git diff --check`.
16 | # - Deny applying with `git apply --whitespace=error-all`.
17 | # - Fix automatically with `git apply --whitespace=fix`.
18 |
19 | *.config text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
20 | *.css text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
21 | *.dist text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
22 | *.engine text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
23 | *.html text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=html
24 | *.inc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
25 | *.install text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
26 | *.js text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
27 | *.json text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
28 | *.lock text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
29 | *.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
30 | *.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
31 | *.module text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
32 | *.php text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
33 | *.po text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
34 | *.profile text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
35 | *.script text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
36 | *.sh text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
37 | *.sql text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
38 | *.svg text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
39 | *.theme text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php
40 | *.twig text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
41 | *.txt text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
42 | *.xml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
43 | *.yml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
44 |
45 | # Define binary file attributes.
46 | # - Do not treat them as text.
47 | # - Include binary diff in patches instead of "binary files differ."
48 | *.eot -text diff
49 | *.exe -text diff
50 | *.gif -text diff
51 | *.gz -text diff
52 | *.ico -text diff
53 | *.jpeg -text diff
54 | *.jpg -text diff
55 | *.otf -text diff
56 | *.phar -text diff
57 | *.png -text diff
58 | *.svgz -text diff
59 | *.ttf -text diff
60 | *.woff -text diff
61 | *.woff2 -text diff
62 |
--------------------------------------------------------------------------------
/drupal/config/user.mail.yml:
--------------------------------------------------------------------------------
1 | _core:
2 | default_config_hash: 6CZIzFifRq3qbdq3n3nDpEOO4hWIQtKOAQNPvGNGKeM
3 | langcode: en
4 | cancel_confirm:
5 | subject: 'Account cancellation request for [user:display-name] at [site:name]'
6 | body: |-
7 | [user:display-name]
8 |
9 | A request to cancel your account has been made at [site:name].
10 |
11 | You may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:
12 |
13 | [user:cancel-url]
14 |
15 | NOTE: The cancellation of your account is not reversible.
16 |
17 | This link expires in one day and nothing will happen if it is not used.
18 |
19 | -- [site:name] team
20 | password_reset:
21 | subject: 'Replacement login information for [user:display-name] at [site:name]'
22 | body: |-
23 | [user:display-name],
24 |
25 | A request to reset the password for your account has been made at [site:name].
26 |
27 | You may now log in by clicking this link or copying and pasting it into your browser:
28 |
29 | [user:one-time-login-url]
30 |
31 | This link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.
32 |
33 | -- [site:name] team
34 | register_admin_created:
35 | subject: 'An administrator created an account for you at [site:name]'
36 | body: |-
37 | [user:display-name],
38 |
39 | A site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it into your browser:
40 |
41 | [user:one-time-login-url]
42 |
43 | This link can only be used once to log in and will lead you to a page where you can set your password.
44 |
45 | After setting your password, you will be able to log in at [site:login-url] in the future using:
46 |
47 | username: [user:name]
48 | password: Your password
49 |
50 | -- [site:name] team
51 | register_no_approval_required:
52 | subject: 'Account details for [user:display-name] at [site:name]'
53 | body: |-
54 | [user:display-name],
55 |
56 | Thank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it into your browser:
57 |
58 | [user:one-time-login-url]
59 |
60 | This link can only be used once to log in and will lead you to a page where you can set your password.
61 |
62 | After setting your password, you will be able to log in at [site:login-url] in the future using:
63 |
64 | username: [user:name]
65 | password: Your password
66 |
67 | -- [site:name] team
68 | register_pending_approval:
69 | subject: 'Account details for [user:display-name] at [site:name] (pending admin approval)'
70 | body: |-
71 | [user:display-name],
72 |
73 | Thank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.
74 |
75 | -- [site:name] team
76 | register_pending_approval_admin:
77 | subject: 'Account details for [user:display-name] at [site:name] (pending admin approval)'
78 | body: |-
79 | [user:display-name] has applied for an account.
80 |
81 | [user:edit-url]
82 | status_activated:
83 | subject: 'Account details for [user:display-name] at [site:name] (approved)'
84 | body: |-
85 | [user:display-name],
86 |
87 | Your account at [site:name] has been activated.
88 |
89 | You may now log in by clicking this link or copying and pasting it into your browser:
90 |
91 | [user:one-time-login-url]
92 |
93 | This link can only be used once to log in and will lead you to a page where you can set your password.
94 |
95 | After setting your password, you will be able to log in at [site:login-url] in the future using:
96 |
97 | username: [user:account-name]
98 | password: Your password
99 |
100 | -- [site:name] team
101 | status_blocked:
102 | subject: 'Account details for [user:display-name] at [site:name] (blocked)'
103 | body: |-
104 | [user:display-name],
105 |
106 | Your account on [site:name] has been blocked.
107 |
108 | -- [site:name] team
109 | status_canceled:
110 | subject: 'Account details for [user:display-name] at [site:name] (canceled)'
111 | body: |-
112 | [user:display-name],
113 |
114 | Your account on [site:name] has been canceled.
115 |
116 | -- [site:name] team
--------------------------------------------------------------------------------
/drupal/web/themes/react_example_theme/js/src/components/NodeForm.jsx:
--------------------------------------------------------------------------------
1 | import React, {useState} from "react";
2 | import {fetchWithCSRFToken} from "../utils/fetch";
3 |
4 | const NodeForm = ({id, title, body, onSuccess}) => {
5 | const [isSubmitting, setSubmitting] = useState(false);
6 |
7 | const [result, setResult] = useState({
8 | success: null,
9 | error: null,
10 | message: '',
11 | });
12 |
13 | const defaultValues = {
14 | title: title ? title : '',
15 | body: body ? body : '',
16 | };
17 | const [values, setValues] = useState(defaultValues);
18 |
19 | const handleInputChange = (event) => {
20 | const {name, value} = event.target;
21 | setValues({...values, [name]: value});
22 | };
23 |
24 | const handleSubmit = (event) => {
25 | setSubmitting(true);
26 | event.preventDefault();
27 |
28 | const csrfUrl = `/session/token?_format=json`;
29 | const fetchUrl = id ? `/jsonapi/node/article/${id}` : `/jsonapi/node/article`;
30 |
31 | let data = {
32 | "data": {
33 | "type": "node--article",
34 | "attributes": {
35 | "title": `${values.title}`,
36 | "body": {
37 | "value": `${values.body}`,
38 | "format": 'plain_text',
39 | }
40 | }
41 | }
42 | };
43 |
44 | // If we have an ID that means we're editing an existing node and not
45 | // creating a new one.
46 | if (id) {
47 | // Set the ID in the data we'll send to the API.
48 | data.data.id = id;
49 | }
50 |
51 | const fetchOptions = {
52 | // Use HTTP PATCH for edits, and HTTP POST to create new articles.
53 | method: id ? 'PATCH' : 'POST',
54 | credentials: 'same-origin',
55 | headers: new Headers({
56 | 'Accept': 'application/vnd.api+json',
57 | 'Content-Type': 'application/vnd.api+json',
58 | 'Cache': 'no-cache'
59 | }),
60 | body: JSON.stringify(data),
61 | };
62 |
63 | try {
64 | fetchWithCSRFToken(csrfUrl, fetchUrl, fetchOptions)
65 | .then((response) => response.json())
66 | .then((data) => {
67 | // We're done processing.
68 | setSubmitting(false);
69 |
70 | // If there are any errors display the error and return early.
71 | if (data.errors && data.errors.length > 0) {
72 | setResult({
73 | success: false,
74 | error: true,
75 | message: {data.errors[0].title}: {data.errors[0].detail},
76 | });
77 | return false;
78 | }
79 |
80 | // If the request was successful, remove existing form values and
81 | // display a success message.
82 | setValues(defaultValues);
83 | if (data.data.id) {
84 | setResult({
85 | success: true,
86 | message: {(id ? 'Updated' : 'Added')}: {data.data.attributes.title},
87 | });
88 |
89 | if (typeof onSuccess === 'function') {
90 | onSuccess(data.data);
91 | }
92 | }
93 | });
94 | } catch (error) {
95 | console.log('Error while contacting API', error);
96 | setSubmitting(false);
97 | }
98 | };
99 |
100 | // If the form is currently being processed display a spinner.
101 | if (isSubmitting) {
102 | return (
103 |
104 | Processing ...
105 |
106 | )
107 | }
108 |
109 | return (
110 |
111 | {(result.success || result.error) &&
112 |
113 | {(result.success ? 'Success!' : 'Error')}:
114 | {result.message}
115 |
116 | }
117 |
141 |
142 | )
143 | };
144 |
145 | export default NodeForm;
146 |
--------------------------------------------------------------------------------