├── .github
├── CODEOWNERS
├── actions
│ └── install-sql-localdb
│ │ └── action.yml
└── workflows
│ ├── ci.yml
│ ├── npm-publish.yml
│ └── nuget-release.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── samples
├── music-festival-vue-coupled
│ ├── .editorConfig
│ ├── App_Data
│ │ ├── DefaultSiteContent.episerverdata
│ │ └── db.mdf
│ ├── ClientApp
│ │ ├── .gitignore
│ │ ├── app.vue
│ │ ├── assets
│ │ │ ├── images
│ │ │ │ ├── back.svg
│ │ │ │ ├── pattern-wave.svg
│ │ │ │ └── wave.svg
│ │ │ └── styles
│ │ │ │ ├── common
│ │ │ │ ├── _grid.less
│ │ │ │ ├── _normalize.less
│ │ │ │ ├── _typography.less
│ │ │ │ ├── base.less
│ │ │ │ └── variables.less
│ │ │ │ ├── components
│ │ │ │ ├── atoms
│ │ │ │ │ ├── Button.less
│ │ │ │ │ ├── Heading.less
│ │ │ │ │ ├── Icon.less
│ │ │ │ │ └── Link.less
│ │ │ │ ├── molecules
│ │ │ │ │ ├── Content.less
│ │ │ │ │ ├── ContentBlock.less
│ │ │ │ │ ├── Grid.less
│ │ │ │ │ └── Hero.less
│ │ │ │ └── organisms
│ │ │ │ │ ├── Footer.less
│ │ │ │ │ ├── Header.less
│ │ │ │ │ ├── NavBar.less
│ │ │ │ │ └── Page.less
│ │ │ │ ├── main.less
│ │ │ │ └── utils
│ │ │ │ ├── utils-align.less
│ │ │ │ ├── utils-display.less
│ │ │ │ ├── utils-layout.less
│ │ │ │ ├── utils-link.less
│ │ │ │ ├── utils-offset.less
│ │ │ │ ├── utils-position.less
│ │ │ │ ├── utils-size.less
│ │ │ │ ├── utils-state.less
│ │ │ │ ├── utils-text.less
│ │ │ │ └── utils.less
│ │ ├── components
│ │ │ ├── ArtistImage.vue
│ │ │ ├── BackButton.vue
│ │ │ ├── Card.vue
│ │ │ ├── ConditionalImage.vue
│ │ │ ├── Hero.vue
│ │ │ ├── LanguageSelector.vue
│ │ │ ├── Modal.vue
│ │ │ └── episerver
│ │ │ │ ├── Block.vue
│ │ │ │ ├── ContentArea.vue
│ │ │ │ ├── Link.vue
│ │ │ │ ├── Page.vue
│ │ │ │ ├── Property.vue
│ │ │ │ └── ViewModeLink.vue
│ │ ├── composables
│ │ │ ├── useDisplayOptions.ts
│ │ │ ├── useModal.ts
│ │ │ └── useResolvedContent.ts
│ │ ├── nuxt.config.ts
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── pages
│ │ │ ├── AccessDenied.vue
│ │ │ ├── NotFound.vue
│ │ │ └── [...path].vue
│ │ ├── plugins
│ │ │ ├── epiEdit.ts
│ │ │ └── updateOnContentSaved.ts
│ │ ├── tsconfig.json
│ │ └── views
│ │ │ ├── blocks
│ │ │ ├── BuyTicketBlock.vue
│ │ │ ├── ContentBlock.vue
│ │ │ └── GenericBlock.vue
│ │ │ ├── media
│ │ │ └── ImageFile.vue
│ │ │ └── pages
│ │ │ ├── ArtistContainerPage.vue
│ │ │ ├── ArtistDetailsPage.vue
│ │ │ ├── BlockPreview.vue
│ │ │ └── LandingPage.vue
│ ├── GlobalUsings.cs
│ ├── Models
│ │ ├── Blocks
│ │ │ ├── BuyTicketBlock.cs
│ │ │ └── ContentBlock.cs
│ │ ├── Media
│ │ │ └── ImageFile.cs
│ │ └── Pages
│ │ │ ├── ArtistContainerPage.cs
│ │ │ ├── ArtistDetailsPage.cs
│ │ │ ├── BasePage.cs
│ │ │ └── LandingPage.cs
│ ├── MusicFestival.csproj
│ ├── MusicFestival.sln
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── README.md
│ ├── Startup.cs
│ ├── appsettings.json
│ ├── diagram.jpg
│ ├── nuget.config
│ ├── setup.cmd
│ └── setup.sh
└── music-festival-vue-decoupled
│ ├── MusicFestivalDecoupled.sln
│ ├── README.md
│ ├── backend
│ ├── .editorConfig
│ ├── App_Data
│ │ ├── DefaultSiteContent.episerverdata
│ │ └── db.mdf
│ ├── MusicFestival.Backend.csproj
│ ├── Program.cs
│ ├── ProvisionDatabase.cs
│ ├── Startup.cs
│ ├── appsettings.json
│ ├── properties
│ │ └── launchSettings.json
│ └── wwwroot
│ │ └── favicon.ico
│ ├── frontend
│ ├── .editorconfig
│ ├── .env.development
│ ├── .env.test
│ ├── MusicFestival.Frontend.csproj
│ ├── babel.config.js
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── App.vue
│ │ ├── assets
│ │ │ ├── images
│ │ │ │ ├── back.svg
│ │ │ │ ├── pattern-wave.svg
│ │ │ │ └── wave.svg
│ │ │ └── styles
│ │ │ │ ├── common
│ │ │ │ ├── _grid.less
│ │ │ │ ├── _normalize.less
│ │ │ │ ├── _typography.less
│ │ │ │ ├── base.less
│ │ │ │ └── variables.less
│ │ │ │ ├── components
│ │ │ │ ├── atoms
│ │ │ │ │ ├── Button.less
│ │ │ │ │ ├── Heading.less
│ │ │ │ │ ├── Icon.less
│ │ │ │ │ └── Link.less
│ │ │ │ ├── molecules
│ │ │ │ │ ├── Content.less
│ │ │ │ │ ├── ContentBlock.less
│ │ │ │ │ ├── Grid.less
│ │ │ │ │ └── Hero.less
│ │ │ │ └── organisms
│ │ │ │ │ ├── Footer.less
│ │ │ │ │ ├── Header.less
│ │ │ │ │ ├── NavBar.less
│ │ │ │ │ └── Page.less
│ │ │ │ ├── main.less
│ │ │ │ └── utils
│ │ │ │ ├── utils-align.less
│ │ │ │ ├── utils-display.less
│ │ │ │ ├── utils-layout.less
│ │ │ │ ├── utils-link.less
│ │ │ │ ├── utils-offset.less
│ │ │ │ ├── utils-position.less
│ │ │ │ ├── utils-size.less
│ │ │ │ ├── utils-state.less
│ │ │ │ ├── utils-text.less
│ │ │ │ └── utils.less
│ │ ├── components
│ │ │ ├── EpiBlockComponentSelector.vue
│ │ │ ├── EpiContentArea.vue
│ │ │ ├── EpiLink.vue
│ │ │ ├── EpiPageComponentSelector.vue
│ │ │ ├── EpiProperty.vue
│ │ │ ├── EpiViewModeLink.vue
│ │ │ └── widgets
│ │ │ │ ├── ArtistImage.vue
│ │ │ │ ├── BackButton.vue
│ │ │ │ ├── Card.vue
│ │ │ │ ├── ConditionalImage.vue
│ │ │ │ ├── Hero.vue
│ │ │ │ ├── LanguageSelector.vue
│ │ │ │ └── Modal.vue
│ │ ├── constants.js
│ │ ├── directives
│ │ │ └── epiEdit.js
│ │ ├── epiBootstrap.js
│ │ ├── main.js
│ │ ├── router
│ │ │ └── index.js
│ │ ├── store
│ │ │ ├── index.js
│ │ │ └── modules
│ │ │ │ ├── appContext.js
│ │ │ │ ├── epiContext.js
│ │ │ │ └── epiDataModel.js
│ │ ├── urlHelpers.js
│ │ └── views
│ │ │ ├── 403.vue
│ │ │ ├── 404.vue
│ │ │ ├── BlockPreview.vue
│ │ │ ├── blocks
│ │ │ ├── BuyTicketBlock.vue
│ │ │ ├── ContentBlock.vue
│ │ │ └── GenericBlock.vue
│ │ │ ├── media
│ │ │ └── ImageFile.vue
│ │ │ └── pages
│ │ │ ├── ArtistContainerPage.vue
│ │ │ ├── ArtistDetailsPage.vue
│ │ │ └── LandingPage.vue
│ └── vue.config.js
│ ├── nuget.config
│ └── setup.cmd
└── src
├── @episerver
├── .editorconfig
├── .eslintrc.js
├── content-definitions
│ ├── README.md
│ ├── bin
│ │ └── cli.mjs
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── authService.mjs
│ │ ├── manifest.mjs
│ │ └── utils.mjs
│ └── test
│ │ ├── cli.tests.mjs
│ │ ├── cmd.mjs
│ │ ├── fixture.mjs
│ │ └── manifests
│ │ ├── empty-json.json
│ │ ├── major-downgrade.json
│ │ ├── major-update.json
│ │ ├── new.json
│ │ └── no-json.json
├── content-delivery
│ ├── README.md
│ ├── lib
│ │ ├── apiClient.d.mts
│ │ ├── apiClient.d.mts.map
│ │ ├── apiClient.mjs
│ │ ├── apiClient.mjs.map
│ │ ├── config.d.mts
│ │ ├── config.d.mts.map
│ │ ├── config.mjs
│ │ ├── config.mjs.map
│ │ ├── contentLoader.d.mts
│ │ ├── contentLoader.d.mts.map
│ │ ├── contentLoader.mjs
│ │ ├── contentLoader.mjs.map
│ │ ├── contentResolver.d.mts
│ │ ├── contentResolver.d.mts.map
│ │ ├── contentResolver.mjs
│ │ ├── contentResolver.mjs.map
│ │ ├── main.d.mts
│ │ ├── main.d.mts.map
│ │ ├── main.mjs
│ │ ├── main.mjs.map
│ │ ├── models.d.mts
│ │ ├── models.d.mts.map
│ │ ├── models.mjs
│ │ ├── models.mjs.map
│ │ ├── siteLoader.d.mts
│ │ ├── siteLoader.d.mts.map
│ │ ├── siteLoader.mjs
│ │ └── siteLoader.mjs.map
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── apiClient.mts
│ │ ├── config.mts
│ │ ├── contentLoader.mts
│ │ ├── contentResolver.mts
│ │ ├── main.mts
│ │ ├── models.mts
│ │ └── siteLoader.mts
│ ├── test
│ │ ├── contentLoader.tests.mjs
│ │ ├── contentResolver.tests.mjs
│ │ ├── fixture.mjs
│ │ └── siteLoader.tests.mjs
│ └── tsconfig.json
└── test-setup
│ ├── backend
│ ├── Backend.csproj
│ ├── CreateTestContentFirstRequestInitializer.cs
│ ├── CustomHeaderContentApiFilter.cs
│ ├── DatabaseHelper.cs
│ ├── Program.cs
│ ├── ProvisionDatabase.cs
│ ├── Startup.cs
│ ├── UsernameAuthenticationHandler.cs
│ ├── appsettings.json
│ ├── nuget.config
│ └── properties
│ │ └── launchSettings.json
│ ├── data
│ └── DefaultSiteContent.episerverdata
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ └── index.mjs
└── EPiServer.ContentDelivery.NodeProxy
├── DependencyInjection
├── NodeJsEndpointRouteBuilderExtensions.cs
└── NodeJsServiceCollectionExtensions.cs
├── EPiServer.ContentDelivery.NodeProxy.csproj
├── NodeJsForwarder.cs
├── NodeJsOptions.cs
├── NodeJsProcess.cs
├── icon.png
└── nuget.config
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Users referenced in this file will automatically be requested as reviewers for PRs that modify the given paths.
2 |
3 | # See https://help.github.com/articles/about-code-owners/
4 |
5 | # Default owners for everything in this repo
6 |
7 | - @episerver/cms-delivery
8 |
--------------------------------------------------------------------------------
/.github/actions/install-sql-localdb/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Install SQL LocalDB'
2 | description: 'Installs Microsoft SQL Server Express LocalDB 2016 SP2'
3 | runs:
4 | using: "composite"
5 | steps:
6 | - run: |
7 | Write-Host "Downloading"
8 | Import-Module BitsTransfer
9 | Start-BitsTransfer -Source https://download.microsoft.com/download/4/1/A/41AD6EDE-9794-44E3-B3D5-A1AF62CD7A6F/sql16_sp2_dlc/en-us/SqlLocalDB.msi -Destination SqlLocalDB.msi
10 | Write-Host "Installing"
11 | Start-Process -FilePath "SqlLocalDB.msi" -Wait -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES";
12 | Write-Host "Checking"
13 | sqlcmd -l 60 -S "(localdb)\MSSQLLocalDB" -Q "SELECT @@VERSION;"
14 | shell: pwsh
15 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous integration
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - develop
7 | - master
8 | - main
9 |
10 | jobs:
11 | build:
12 | name: Build & test SDK
13 | runs-on: windows-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: ./.github/actions/install-sql-localdb
17 | - uses: actions/setup-node@v2
18 | with:
19 | node-version: 18
20 | - name: Build & test Content Definitions
21 | working-directory: ./src/@episerver/content-definitions
22 | run: |
23 | npm ci
24 | npm test
25 | - name: Build & test Content Delivery
26 | working-directory: ./src/@episerver/content-delivery
27 | run: |
28 | npm ci
29 | npm test
30 |
31 | build_test_pack_proxy:
32 | name: Build, test & pack Node.js proxy
33 | runs-on: windows-latest
34 | env:
35 | buildConfiguration: release
36 | versionSuffix: ci-${{github.RUN_NUMBER }}
37 | DOTNET_NOLOGO: 1
38 | steps:
39 | - uses: actions/checkout@v2
40 | - name: Restore Node.js proxy
41 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
42 | run: dotnet restore
43 | - name: Build Node.js proxy
44 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
45 | run: dotnet build --no-restore --configuration $env:buildConfiguration --version-suffix $env:versionSuffix
46 | - name: Test Node.js proxy
47 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
48 | run: dotnet test --no-build --configuration $env:buildConfiguration
49 | - name: Pack Node.js proxy
50 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
51 | run: dotnet pack --no-build --configuration $env:buildConfiguration --version-suffix $env:versionSuffix
52 | - name: Archive packages
53 | uses: actions/upload-artifact@v4
54 | with:
55 | name: packages
56 | path: artifacts/**/*.nupkg
57 | retention-days: 2
58 |
59 | publish:
60 | name: Publish packages
61 | needs: build_test_pack_proxy
62 | runs-on: windows-latest
63 | env:
64 | nugetSource: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
65 | DOTNET_NOLOGO: 1
66 | steps:
67 | - name: Download artifacts
68 | uses: actions/download-artifact@v4
69 | with:
70 | name: packages
71 | - name: Publish to Github Packages
72 | run: dotnet nuget push "**/*.nupkg" --source $env:nugetSource --skip-duplicate --api-key ${{ secrets.GITHUB_TOKEN }}
73 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: Npm publish
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build:
9 | name: Build & test
10 | runs-on: windows-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: ./.github/actions/install-sql-localdb
14 | - uses: actions/setup-node@v2
15 | with:
16 | node-version: 18
17 | - name: Build & test Content Definitions
18 | working-directory: ./src/@episerver/content-definitions
19 | run: |
20 | npm ci
21 | npm test
22 | - name: Build & test Content Delivery
23 | working-directory: ./src/@episerver/content-delivery
24 | run: |
25 | npm ci
26 | npm test
27 |
28 | publish:
29 | name: Publish packages
30 | needs: build
31 | runs-on: ubuntu-latest
32 | steps:
33 | - uses: actions/checkout@v2
34 | - uses: actions/setup-node@v2
35 | with:
36 | node-version: 14
37 | registry-url: https://registry.npmjs.org/
38 | - name: Publish Content Definitions
39 | working-directory: ./src/@episerver/content-definitions
40 | env:
41 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
42 | run: npm publish --access public
43 | - name: Publish Content Delivery
44 | working-directory: ./src/@episerver/content-delivery
45 | env:
46 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
47 | run: npm publish --access public
48 |
--------------------------------------------------------------------------------
/.github/workflows/nuget-release.yml:
--------------------------------------------------------------------------------
1 | name: NuGet release
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build_test_pack:
9 | name: Build, test & pack Node.js proxy
10 | runs-on: windows-latest
11 | env:
12 | buildConfiguration: release
13 | DOTNET_NOLOGO: 1
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Restore Node.js proxy
17 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
18 | run: dotnet restore
19 | - name: Build Node.js proxy
20 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
21 | run: dotnet build --no-restore --configuration $env:buildConfiguration
22 | - name: Test Node.js proxy
23 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
24 | run: dotnet test --no-build --configuration $env:buildConfiguration
25 | - name: Pack Node.js proxy
26 | working-directory: ./src/EPiServer.ContentDelivery.NodeProxy
27 | run: dotnet pack --no-build --configuration $env:buildConfiguration
28 | - name: Archive packages
29 | uses: actions/upload-artifact@v4
30 | with:
31 | name: packages
32 | path: artifacts/**/*.nupkg
33 | retention-days: 2
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | app_data
5 | packages
6 | bin
7 | obj
8 | modules
9 | artifacts
10 |
11 | # local env files
12 | .env.local
13 | .env.*.local
14 |
15 | # Log files
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 | pnpm-debug.log*
20 |
21 | # Editor directories and files
22 | .idea
23 | .vs
24 | .vscode
25 | *.suo
26 | *.ntvs*
27 | *.njsproj
28 | *.sw?
29 | *.user
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | ## Did you find a bug?
4 |
5 | * Do not open up a GitHub issue if the bug is a security vulnerability, instead contact support@optimizely.com.
6 |
7 | * Ensure the bug was not already reported by searching on GitHub under [Issues](https://github.com/episerver/content-delivery-js-sdk/issues).
8 |
9 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/episerver/content-delivery-js-sdk/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
10 |
11 | ## Did you write a patch that fixes a bug?
12 |
13 | * Open a new GitHub pull request with the patch.
14 |
15 | * Ensure the pull request description clearly describes the problem and solution. Include the relevant issue number if applicable.
16 |
17 | ## Do you intend to add a new feature or change an existing one?
18 |
19 | * Don't surprise us with a large pull requests. Instead, file an issue and start a discussion so we can agree on a direction before you invest a large amount of time.
20 |
21 | ## Do you have questions about the source code?
22 |
23 | * Ask any question about how to use this code on the [Optimizely World forum](https://world.optimizely.com/forum/).
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Optimizely Content Delivery JavaScript SDKs and samples
2 |
3 | [](https://github.com/episerver/content-delivery-js-sdk/actions/workflows/npm-publish.yml) [](https://github.com/episerver/content-delivery-js-sdk/actions/workflows/ci.yml)
4 |
5 |
6 | This repository contains the source code for the Content Delivery JavaScript SDKs and samples.
7 |
8 | ## Content Definitions
9 |
10 | * [Source code and documentation](https://github.com/episerver/content-delivery-js-sdk/tree/master/src/%40episerver/content-definitions)
11 | * [REST API documentation](https://world.optimizely.com/documentation/developer-guides/content-definitions-api/)
12 |
13 | ## Content Delivery
14 |
15 | * [Source code and documentation](https://github.com/episerver/content-delivery-js-sdk/tree/master/src/%40episerver/content-delivery)
16 | * [REST API documentation](https://world.optimizely.com/documentation/developer-guides/content-delivery-api/)
17 |
18 | ## Samples
19 |
20 | The samples are using Nuxt and Vue CLI v5.
21 |
22 | * [Nuxt - Coupled](samples/music-festival-vue-coupled)
23 | * [Vue.js - Decoupled](samples/music-festival-vue-decoupled)
24 |
25 | ## Prerequisites for building and testing the SDKs
26 |
27 | This project uses:
28 | * Node.js 18+
29 | * .NET SDK 6+
30 | * SQL Server 2016 Express LocalDB ([download here](https://www.microsoft.com/en-us/sql-server/sql-server-downloads))
31 |
32 | ## Create a release
33 |
34 | 1. Update the version in all modules with `npm version 1.2.3`.
35 | 2. Run `npm install` in all samples to update their package-lock.json files.
36 | 3. Commit, push, and create PR to `master` branch.
37 | 4. Create new Github release based of `master` branch.
38 | 5. Tag version with following format `v1.2.3`.
39 | 6. Give the release a title and write an optional description.
40 | 7. Publish.
41 |
42 | ## Contributing
43 |
44 | The easiest way to contribute is to join in with the discussions on Github issues or create new issues with questions, suggestions or any other feedback. If you want to contribute code or documentation, you are more than welcome to create pull-requests, but make sure that you read the contribution page first.
45 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/App_Data/DefaultSiteContent.episerverdata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-coupled/App_Data/DefaultSiteContent.episerverdata
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/App_Data/db.mdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-coupled/App_Data/db.mdf
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log*
3 | .nuxt
4 | .nitro
5 | .cache
6 | .output
7 | .env
8 | dist
9 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/app.vue:
--------------------------------------------------------------------------------
1 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/images/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/common/variables.less:
--------------------------------------------------------------------------------
1 | @basePath: '/';
2 |
3 | // - Fonts -
4 | @baseFontSize: 16;
5 | @fontFamilyPrimary: "barlow", "Helvetica", "Arial", sans-serif;
6 | @fontFamilySecondary: "ubuntu", "Helvetica", "Arial", serif;
7 | @fontHeading: "ubuntu-bold";
8 | @fontSubHeading: "barlow-bold";
9 |
10 | // - Container Width -
11 | @widthContainer: 1204px;
12 |
13 | // - Text Sizes -
14 | @textSizeXSmall: unit((12 / @baseFontSize), em); // 12px
15 | @textSizeSmall: unit((14 / @baseFontSize), em); // 14px
16 | @textSizeMedium: unit((16 / @baseFontSize), em); // 16px
17 | @textSizeLarge: unit((18 / @baseFontSize), em); // 18px
18 | @textSizeXLarge: unit((20 / @baseFontSize), em); // 20px
19 |
20 | // - Colors -
21 | @backgroundColor: @colorBlue;
22 | @backgroundGradient: linear-gradient(to bottom, rgba(255,0,0,0) 0%, rgba(255,0,0,.35) 100%);
23 | @colorBlack: #000;
24 | @colorWhite: #fff;
25 | @colorBlue: #1A237E;
26 | @colorTurquoise: #41FFF5;
27 | @colorPink: #EC407A;
28 | //# Text
29 | @colorText: #fff;
30 | @colorTextLight: #aaa;
31 |
32 | //# Links
33 | @colorLink: #fff;
34 | @colorLinkHover: #ccc;
35 |
36 | // - Space -
37 | @spaceDefault: unit((20 / @baseFontSize), em);
38 | @spaceXSmall: @spaceDefault / 2;
39 | @spaceSmall: @spaceDefault / 1.5;
40 | @spaceMedium: @spaceDefault;
41 | @spaceLarge: @spaceDefault * 1.5;
42 | @spaceXLarge: @spaceDefault * 2;
43 |
44 | @space: 1em;
45 | @space-and-half: @space*1.5;
46 | @space-double: @space*2;
47 | @space-triple: @space*3;
48 | @space-quad: @space*4;
49 | @space-half: @space/2;
50 | @space-quarter: @space/4;
51 |
52 |
53 | // - Breakpoint Widths -
54 | @widthXSmall: (420 / @baseFontSize);
55 | @widthSmall: (544 / @baseFontSize);
56 | @widthMedium: (768 / @baseFontSize);
57 | @widthLarge: (920 / @baseFontSize);
58 | @widthXLarge: (1200 / @baseFontSize);
59 |
60 | // - Breakpoint Media Queries -
61 | @bpXSmall: ~ "only screen and (min-width: @{widthXSmall}em), print";
62 | @bpSmall: ~ "only screen and (min-width: @{widthSmall}em), print";
63 | @bpMedium: ~ "only screen and (min-width: @{widthMedium}em), print";
64 | @bpLarge: ~ "only screen and (min-width: @{widthLarge}em), print";
65 | @bpXLarge: ~ "only screen and (min-width: @{widthXLarge}em)";
66 |
67 | @bpMediumMax: ~ "only screen and (max-width: @{widthMedium}em), print";
68 |
69 |
70 | // - Size Prefixes -
71 | @prefixXSmall: ~ "xsm-";
72 | @prefixSmall: ~ "sm-";
73 | @prefixMedium: ~ "md-";
74 | @prefixLarge: ~ "lg-";
75 | @prefixXLarge: ~ "xlg-";
76 |
77 | // menu
78 | @menuTransition: left .4s ease-in-out;
79 |
80 | // Grid
81 | @gutterSize: 24px; // needs to be in pixels
82 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/atoms/Button.less:
--------------------------------------------------------------------------------
1 | /*#
2 |
3 | The button classes are best applied to links and buttons.
4 | These components can be used in forms, as calls to action, or as part of the general UI of the site/app.
5 | */
6 |
7 | @import "../../common/variables.less";
8 |
9 | .Button, button {
10 | background: @colorTurquoise;
11 | border-radius: 2em;
12 | color: @colorBlue;
13 | text-transform: uppercase;
14 | font-size: 12px;
15 | font-weight: 700;
16 | letter-spacing: 1.2px;
17 | padding: 12px 26px;
18 | font-family: @fontFamilyPrimary;
19 | box-shadow: 0 0 12px 0 fade(@colorBlack, 36%);
20 | border: 0;
21 | transition: all 300ms;
22 | &:hover{
23 | text-decoration: none;
24 | background-color: @colorBlue;
25 | color: @colorTurquoise;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/atoms/Heading.less:
--------------------------------------------------------------------------------
1 | @import (reference) "../../common/variables.less";
2 |
3 | @Heading-color: @colorText;
4 | @Heading-size1: 80px;
5 | @Heading-size2: 80px;
6 | @Heading-size3: 48px;
7 | @Heading-size4: 32px;
8 | @Heading-size5: 22px;
9 | @Heading-size6: 14px;
10 |
11 | .Heading {
12 | display: block;
13 | margin-top: 0;
14 | margin-bottom: .5em;
15 | color: @Heading-color;
16 | font-weight: 700;
17 | line-height: 1em;
18 | text-transform: lowercase;
19 | font-family: @fontFamilySecondary;
20 |
21 | a:hover & {
22 | color: @colorLinkHover;
23 | }
24 | }
25 |
26 | .Heading--h1, h1 {
27 | &:extend(.Heading all);
28 | font: 48px/50px @fontHeading;
29 | margin: .25em 0;
30 |
31 | @media @bpMedium {
32 | font-size: unit((@Heading-size1 / @baseFontSize), em);
33 | }
34 | }
35 |
36 | .Heading--h2, h2 {
37 | &:extend(.Heading all);
38 | font-size: unit(((@Heading-size2*.65) / @baseFontSize), em);
39 | margin: 20px 0 10px;
40 |
41 | @media @bpMedium {
42 | font-size: unit((@Heading-size2 / @baseFontSize), em);
43 | }
44 | }
45 |
46 | .Heading--h3, h3 {
47 | &:extend(.Heading all);
48 | font-size: unit(((@Heading-size3*.75) / @baseFontSize), em);
49 | font-family: @fontSubHeading;
50 |
51 | @media @bpMedium {
52 | font-size: unit((@Heading-size3 / @baseFontSize), em);
53 | }
54 | }
55 |
56 | .Heading--h4, h4 {
57 | &:extend(.Heading all);
58 | font-weight: 300;
59 | font-size: 18px;
60 |
61 | @media @bpMedium {
62 | font-size: unit((@Heading-size4 / @baseFontSize), em);
63 | }
64 | }
65 |
66 | .Heading--h5, h5, .Heading--h6, h6 {
67 | &:extend(.Heading all);
68 | font-weight: 700;
69 | letter-spacing: 2.2px;
70 | font-family: @fontFamilyPrimary;
71 | text-transform: uppercase;
72 | font-size: 18px;
73 |
74 | @media @bpMedium {
75 | font-size: unit((@Heading-size5 / @baseFontSize), em);
76 | }
77 | }
78 |
79 | .Heading--h6, h6 {
80 | &:extend(.Heading all);
81 | text-transform: uppercase;
82 | margin-bottom: 0;
83 | font-size: 1em;
84 |
85 | @media @bpMedium {
86 | font-size: unit((@Heading-size6 / @baseFontSize), em);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/atoms/Icon.less:
--------------------------------------------------------------------------------
1 | /*#
2 |
3 | Icon component displaying icons using an Icomoon.io generated icon web font.
4 |
5 | To add or remove icons navigate to https://icomoon.io/app/ and import "selection.json" from the Styles/Fonts/Icons folder.
6 |
7 | **Usage**
8 | To use an icon in a different component start by importing the Icon.less file:
9 | @import (reference) "../Atoms/Icon.less";
10 |
11 | Then extend the CSS rule with .Icon and set "content" to the icon you're looking for:
12 | .MyComponent {
13 | &:after {
14 | &:extend(.Icon);
15 | content: @Icon--checkmark;
16 | }
17 | }
18 |
19 | Examples:
20 |
21 |
22 |
23 |
24 |
25 | */
26 |
27 | @import "../../common/variables.less";
28 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/atoms/Link.less:
--------------------------------------------------------------------------------
1 | .Link {}
2 |
3 | .Link--skipLink {
4 | border: 0;
5 | clip: rect(0 0 0 0);
6 | height: 1px;
7 | margin: -1px;
8 | overflow: hidden;
9 | padding: 0;
10 | position: absolute;
11 | width: 1px;
12 |
13 | &:focus {
14 | clip: auto;
15 | height: auto;
16 | margin: 0;
17 | width: auto;
18 | background: #fff;
19 | padding: .1875em .3125em;
20 | z-index: 1000;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/molecules/Content.less:
--------------------------------------------------------------------------------
1 | /*#
2 |
3 | Responsible for styling CMS content where it is not possible to add custom CSS-classes.
4 | For example adding styling for ,
etc.
5 | */
6 |
7 | @import "../../common/variables.less";
8 |
9 | .Content {
10 | }
11 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/molecules/ContentBlock.less:
--------------------------------------------------------------------------------
1 | @import "../../common/variables.less";
2 |
3 | .Preview {
4 | > .u-md-size1of2, > .u-md-size1of3 {
5 | .Grid .Grid-cell {
6 | width: 100% !important;
7 | }
8 | }
9 | }
10 |
11 | .ContentArea {
12 | > .u-md-size1of2,
13 | > .u-md-size1of3 {
14 | .ContentBlock {
15 | .Grid-cell {
16 | width: 100% !important;
17 | }
18 | }
19 | }
20 | }
21 |
22 | footer {
23 | background-color: fade(@colorPink, 24%);
24 | background-image: url('../../../images/pattern-wave.svg');
25 |
26 | .ContentBlock {
27 |
28 | .Page-container {
29 | max-width: 920px;
30 | padding: 3em 1em 0;
31 | }
32 |
33 | h2 {
34 | font-size: 48px;
35 | }
36 |
37 | .Grid-cell:first-child {
38 | order: 1;
39 | text-align: center;
40 |
41 | @media @bpMedium {
42 | order: 0;
43 | }
44 | }
45 | }
46 | }
47 |
48 | .ContentBlock {
49 | ol {
50 | list-style-type: none;
51 | padding: 0;
52 | line-height: 1.8;
53 | margin-bottom: 2em;
54 | font-size: 20px;
55 |
56 | li {
57 | display: inline-block;
58 | position: relative;
59 | padding-right: 20px;
60 |
61 | &:after {
62 | content: '';
63 | width: 4px;
64 | height: 4px;
65 | position: absolute;
66 | top: 50%;
67 | right: 6px;
68 | transform: translateY(-50%);
69 | background: @colorPink;
70 | border-radius: 50%;
71 | }
72 |
73 | &:last-child {
74 | &:after {
75 | content: none;
76 | }
77 | }
78 | }
79 | }
80 |
81 | a {
82 | background: @colorTurquoise;
83 | border-radius: 2em;
84 | color: @colorBlue;
85 | text-transform: uppercase;
86 | font-size: 12px;
87 | font-weight: 700;
88 | letter-spacing: 1.2px;
89 | padding: 12px 26px;
90 | font-family: @fontFamilyPrimary;
91 | box-shadow: 0 0 12px 0 fade(@colorBlack, 36%);
92 | border: 0;
93 | transition: all 300ms;
94 | &:hover{
95 | text-decoration: none;
96 | background-color: @colorBlue;
97 | color: @colorTurquoise;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/molecules/Hero.less:
--------------------------------------------------------------------------------
1 | @import "../../common/variables.less";
2 |
3 | .Hero {
4 | height: 500px;
5 |
6 | width: 100%;
7 | display: flex;
8 | align-items: center;
9 | justify-content: center;
10 | flex-direction: column;
11 | text-align: center;
12 | position: relative;
13 |
14 | h1, h2, h3, h4, h5, p {
15 | color: @colorWhite;
16 | text-align: center;
17 | }
18 |
19 | &-content {
20 | z-index: 10;
21 | }
22 |
23 | &-image {
24 | background-size: cover;
25 | position: absolute;
26 | top: 0;
27 | left: 0;
28 | width: 100%;
29 | height: 100%;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/organisms/Footer.less:
--------------------------------------------------------------------------------
1 | /*#
2 | Component for the footer section.
3 | */
4 |
5 | @import "../../common/variables.less";
6 |
7 | .FooterBottom {
8 | background: @backgroundColor;
9 | text-align: center;
10 | padding: 1em;
11 | p{
12 | margin: 0;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/organisms/Header.less:
--------------------------------------------------------------------------------
1 | /*#
2 | Component for the header section.
3 | */
4 |
5 | @import "../../common/variables.less";
6 |
7 | @Header-bg: #f4f4f4;
8 | @Header-border: darken(#f4f4f4, 5%);
9 |
10 | .Header {
11 | border-bottom: 1px solid @Header-border;
12 | width: 100%;
13 | }
14 |
15 | .Header-secondary {
16 | padding: 0 @spaceMedium;
17 | background: @Header-bg;
18 | text-align: right;
19 | }
20 |
21 | .Header-primary {
22 | padding: (@spaceLarge - .5em) @spaceMedium;
23 | }
24 |
25 | .Header-primaryContainer {
26 | display: flex;
27 | align-items: center;
28 | }
29 |
30 | .Header-logo {
31 | }
32 |
33 | .Header-primaryNav {
34 | flex: 1 1 auto;
35 | text-align: right;
36 | }
37 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/components/organisms/NavBar.less:
--------------------------------------------------------------------------------
1 | /*#
2 | Component for the navigation bar.
3 | */
4 |
5 | @import "../../common/variables.less";
6 |
7 | .NavBar {
8 | padding: 10px 5px 0;
9 |
10 | @media (min-width:425px) {
11 | padding: 20px 0 0;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/main.less:
--------------------------------------------------------------------------------
1 | @import "common/variables.less";
2 | @import "common/base.less";
3 |
4 | // Import all the common stuff, like mixins, normalize, etc
5 | @import "common/_normalize.less";
6 | @import "common/_typography.less";
7 | @import "common/_grid.less";
8 |
9 | // Utility classes
10 | @import "utils/utils-align.less";
11 | @import "utils/utils-display.less";
12 | @import "utils/utils-layout.less";
13 | @import "utils/utils-link.less";
14 | @import "utils/utils-offset.less";
15 | @import "utils/utils-position.less";
16 | @import "utils/utils-size.less";
17 | @import "utils/utils-state.less";
18 | @import "utils/utils-text.less";
19 | @import "utils/utils.less";
20 |
21 | // Atomic design
22 | @import "components/atoms/Button.less";
23 | @import "components/atoms/Heading.less";
24 | @import "components/atoms/Icon.less";
25 | @import "components/atoms/Link.less";
26 |
27 | @import "components/molecules/Content.less";
28 | @import "components/molecules/ContentBlock.less";
29 | @import "components/molecules/Grid.less";
30 | @import "components/molecules/Hero.less";
31 |
32 | @import "components/organisms/Footer.less";
33 | @import "components/organisms/Header.less";
34 | @import "components/organisms/NavBar.less";
35 | @import "components/organisms/Page.less";
36 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/utils/utils-align.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Vertical alignment utilities
3 | * Depends on an appropriate `display` value.
4 | */
5 |
6 | .u-alignBaseline {
7 | vertical-align: baseline !important;
8 | }
9 |
10 | .u-alignBottom {
11 | vertical-align: bottom !important;
12 | }
13 |
14 | .u-alignMiddle {
15 | vertical-align: middle !important;
16 | }
17 |
18 | .u-alignTop {
19 | vertical-align: top !important;
20 | }
21 |
22 | .u-alignCenter {
23 | margin-left: auto !important;
24 | margin-right: auto !important;
25 | }
26 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/utils/utils-layout.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Contain floats
3 | *
4 | * Make an element expand to contain floated children.
5 | * Uses pseudo-elements (micro clearfix).
6 | *
7 | * 1. The space content is one way to avoid an Opera bug when the
8 | * `contenteditable` attribute is included anywhere else in the document.
9 | * Otherwise it causes space to appear at the top and bottom of the
10 | * element.
11 | * 2. The use of `table` rather than `block` is only necessary if using
12 | * `:before` to contain the top-margins of child elements.
13 | */
14 |
15 | .u-cf:before,
16 | .u-cf:after {
17 | content: " "; /* 1 */
18 | display: table; /* 2 */
19 | }
20 |
21 | .u-cf:after {
22 | clear: both;
23 | }
24 |
25 | /**
26 | * New block formatting context
27 | *
28 | * This affords some useful properties to the element. It won't wrap under
29 | * floats. Will also contain any floated children.
30 |
31 | * N.B. This will clip overflow. Use the alternative method below if this is
32 | * problematic.
33 | */
34 |
35 | .u-nbfc {
36 | overflow: hidden !important;
37 | }
38 |
39 | /**
40 | * New block formatting context (alternative)
41 | *
42 | * Alternative method when overflow must not be clipped.
43 | *
44 | * 1. Create a new block formatting context (NBFC).
45 | * 2. Avoid shrink-wrap behaviour of table-cell.
46 | *
47 | * N.B. This breaks down in some browsers when elements within this element
48 | * exceed its width.
49 | */
50 |
51 | .u-nbfcAlt {
52 | display: table-cell !important; /* 1 */
53 | width: 10000px !important; /* 2 */
54 | }
55 |
56 | /**
57 | * Floats
58 | */
59 |
60 | .u-pullLeft {
61 | float: left !important;
62 | }
63 |
64 | .u-pullRight {
65 | float: right !important;
66 | }
67 |
68 | .u-clear {
69 | clear: both;
70 | }
71 |
72 | .u-noOverflow{
73 | overflow:hidden;
74 | }
75 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/utils/utils-link.less:
--------------------------------------------------------------------------------
1 | @import (reference) "../common/variables.less";
2 |
3 | /**
4 | * Clean link
5 | *
6 | * A link without any text-decoration at all.
7 | */
8 |
9 | .u-linkClean,
10 | .u-linkClean:hover,
11 | .u-linkClean:focus,
12 | .u-linkClean:active {
13 | text-decoration: none !important;
14 | }
15 |
16 | /**
17 | * Link complex
18 | *
19 | * A common pattern is to have a link with several pieces of text and/or an
20 | * icon, where only one piece of text should display the underline when the
21 | * link is the subject of user interaction.
22 | */
23 |
24 | .u-linkComplex,
25 | .u-linkComplex:hover,
26 | .u-linkComplex:focus,
27 | .u-linkComplex:active {
28 | text-decoration: none !important;
29 | }
30 |
31 | .u-linkComplex:hover .u-linkComplexTarget,
32 | .u-linkComplex:focus .u-linkComplexTarget,
33 | .u-linkComplex:active .u-linkComplexTarget {
34 | text-decoration: underline !important;
35 | }
36 |
37 | /**
38 | * Block-level link
39 | *
40 | * Combination of traits commonly used in vertical navigation lists.
41 | */
42 |
43 | .u-linkBlock,
44 | .u-linkBlock:hover,
45 | .u-linkBlock:focus,
46 | .u-linkBlock:active {
47 | display: block !important;
48 | text-decoration: none !important;
49 | }
50 |
51 | /**
52 | * Changes a link to look like plain text.
53 | */
54 | .u-linkText {
55 | color: @colorText;
56 | }
57 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/utils/utils-position.less:
--------------------------------------------------------------------------------
1 | .u-posAbsolute {
2 | position: absolute !important;
3 | }
4 |
5 | /**
6 | * Pins to all corners by default. But when a width and/or height are
7 | * provided, the element will be centered in its nearest relatively-positioned
8 | * ancestor.
9 | */
10 |
11 | .u-posAbsoluteCenter {
12 | bottom: 0 !important;
13 | left: 0 !important;
14 | margin: auto !important;
15 | position: absolute !important;
16 | right: 0 !important;
17 | top: 0 !important;
18 | }
19 |
20 | /**
21 | * 1. Make sure fixed elements are promoted into a new layer, for performance
22 | * reasons.
23 | */
24 |
25 | .u-posFixed {
26 | position: fixed !important;
27 | backface-visibility: hidden; /* 1 */
28 | }
29 |
30 | .u-posRelative {
31 | position: relative !important;
32 | }
33 |
34 | .u-posStatic {
35 | position: static !important;
36 | }
37 |
38 | .u-posBottomCenter{
39 | position: absolute;
40 | bottom: 0;
41 | left: 50%;
42 | transform: translateX(-50%);
43 | }
44 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/assets/styles/utils/utils-text.less:
--------------------------------------------------------------------------------
1 | @import (reference) "../common/variables.less";
2 |
3 | /**
4 | * Text font size.
5 | */
6 |
7 | .u-textXSmall {
8 | font-size: @textSizeXSmall !important;
9 | }
10 | .u-textSmall {
11 | font-size: @textSizeSmall !important;
12 | }
13 | .u-textMedium,
14 | .u-textNormal {
15 | font-size: @textSizeMedium !important;
16 | }
17 | .u-textLarge {
18 | font-size: @textSizeLarge !important;
19 | }
20 | .u-textXLarge {
21 | font-size: @textSizeXLarge !important;
22 | }
23 |
24 | /**
25 | * Text font weights
26 | */
27 |
28 | .u-textWeightThin {
29 | font-weight: 100 !important;
30 | }
31 | .u-textWeightLight {
32 | font-weight: 200 !important;
33 | }
34 | .u-textWeightNormal {
35 | font-weight: normal !important;
36 | }
37 | .u-textWeightMedium {
38 | font-weight: 500 !important;
39 | }
40 | .u-textWeightBold {
41 | font-weight: bold !important;
42 | }
43 |
44 | /**
45 | * Word breaking
46 | *
47 | * Break strings when their length exceeds the width of their container.
48 | */
49 |
50 | .u-textBreak {
51 | word-wrap: break-word !important;
52 | }
53 |
54 | /**
55 | * Horizontal text alignment
56 | */
57 |
58 | .u-textCenter {
59 | text-align: center !important;
60 | }
61 |
62 | .u-textLeft {
63 | text-align: left !important;
64 | }
65 |
66 | .u-textRight {
67 | text-align: right !important;
68 | }
69 |
70 | /**
71 | * Prevent whitespace wrapping
72 | */
73 |
74 | .u-textNoWrap {
75 | white-space: nowrap !important;
76 | }
77 |
78 | /**
79 | * Text truncation
80 | *
81 | * Prevent text from wrapping onto multiple lines, and truncate with an
82 | * ellipsis.
83 | *
84 | * 1. Ensure that the node has a maximum width after which truncation can
85 | * occur.
86 | * 2. Fix for IE 8/9 if `word-wrap: break-word` is in effect on ancestor
87 | * nodes.
88 | */
89 |
90 | .u-textTruncate {
91 | max-width: 100%; /* 1 */
92 | overflow: hidden !important;
93 | text-overflow: ellipsis !important;
94 | white-space: nowrap !important;
95 | word-wrap: normal !important; /* 2 */
96 | }
97 |
98 | /*
99 | * Inherit the ancestor's text color.
100 | */
101 | .u-textInheritColor {
102 | color: inherit !important;
103 | }
104 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/ArtistImage.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
11 |
12 |
13 |
33 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/BackButton.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
11 |
12 |
13 |
29 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/Card.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
55 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/ConditionalImage.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/Hero.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/LanguageSelector.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
12 | {{
13 | item.displayName
14 | }}
15 |
16 |
17 |
18 |
19 |
92 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/Modal.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | default content
12 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
78 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/episerver/Block.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/episerver/ContentArea.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
22 |
23 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/episerver/Link.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/episerver/Page.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/episerver/Property.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
15 | Edit property: {{ propertyName }}
16 |
17 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/components/episerver/ViewModeLink.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/composables/useDisplayOptions.ts:
--------------------------------------------------------------------------------
1 | export default () => {
2 | const displayOptions = {
3 | full: 'u-md-sizeFull',
4 | wide: 'u-md-size2of3',
5 | half: 'u-md-size1of2',
6 | narrow: 'u-md-size1of3',
7 | };
8 |
9 | const getDisplayOption = (value) => {
10 | const keys = Object.keys(displayOptions);
11 | const values = Object.values(displayOptions);
12 |
13 | for (let i = 0; i < keys.length; i += 1) {
14 | if (value === keys[i]) {
15 | return values[i];
16 | }
17 | }
18 | return null;
19 | }
20 |
21 | return {
22 | displayOptions,
23 | getDisplayOption
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/composables/useModal.ts:
--------------------------------------------------------------------------------
1 | const active = ref(false);
2 |
3 | export default () => {
4 | const showModal = () => {
5 | active.value = true;
6 | }
7 |
8 | const hideModal = () => {
9 | active.value = false;
10 | }
11 |
12 | return {
13 | active,
14 | showModal,
15 | hideModal,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/composables/useResolvedContent.ts:
--------------------------------------------------------------------------------
1 | import { ContentData, ContentResolver, ResolvedContent } from '@episerver/content-delivery';
2 |
3 | const pending = ref(false);
4 | const resolvedContent = reactive>({} as ResolvedContent);
5 |
6 | export default () => {
7 | const updateContentByUrl = async (url: string) => {
8 | pending.value = true;
9 |
10 | const contentResolver = new ContentResolver();
11 | const result = await contentResolver.resolveContent(url, true);
12 |
13 | Object.assign(resolvedContent, result);
14 |
15 | pending.value = false;
16 | };
17 |
18 | return {
19 | pending,
20 | resolvedContent: readonly(resolvedContent),
21 | updateContentByUrl
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | runtimeConfig: {
3 | public: {
4 | apiUrl: 'http://localhost:8080/api/episerver/v3.0/',
5 | websiteUrl: 'http://localhost:8080'
6 | }
7 | },
8 | components: {
9 | dirs: [
10 | {
11 | path: '~/components',
12 | global: false
13 | },
14 | {
15 | path: '~/views/pages',
16 | global: true
17 | },
18 | {
19 | path: '~/views/blocks',
20 | global: true
21 | },
22 | {
23 | path: '~/views/media',
24 | global: true
25 | }
26 | ]
27 | },
28 | css: ['@/assets/styles/main.less']
29 | })
30 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "build": "nuxt build",
5 | "dev": "nuxt dev",
6 | "generate": "nuxt generate",
7 | "preview": "nuxt preview"
8 | },
9 | "dependencies": {
10 | "@episerver/content-delivery": "file:../../../src/@episerver/content-delivery"
11 | },
12 | "devDependencies": {
13 | "less": "^4.1.3",
14 | "less-loader": "^11.0.0",
15 | "nuxt": "3.0.0-rc.10"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/pages/AccessDenied.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Access denied
4 |
5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio, rem nesciunt ad iure illum,
6 | repudiandae enim nostrum velit ipsum dignissimos molestiae architecto,
7 | officia nisi vel facilis quasi mollitia necessitatibus eum.
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/pages/NotFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Page not found
4 |
5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio, rem nesciunt ad iure illum,
6 | repudiandae enim nostrum velit ipsum dignissimos molestiae architecto,
7 | officia nisi vel facilis quasi mollitia necessitatibus eum.
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/pages/[...path].vue:
--------------------------------------------------------------------------------
1 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/plugins/epiEdit.ts:
--------------------------------------------------------------------------------
1 | // The v-epi-edit directive is responsible for adding the
2 | // necessary attributes on-page-edit needs to render its overlay.
3 |
4 | import { ContextMode } from '@episerver/content-delivery'
5 |
6 | const { resolvedContent } = useResolvedContent();
7 |
8 | function toggleEditAttributes(el, binding) {
9 | const isEditable = resolvedContent.mode === ContextMode.Edit;
10 | if (isEditable) {
11 | el.setAttribute('data-epi-property-name', binding.value);
12 | el.setAttribute('data-epi-property-render', 'none')
13 | } else {
14 | el.removeAttribute('data-epi-property-name');
15 | el.removeAttribute('data-epi-property-render')
16 | }
17 | }
18 |
19 | export default defineNuxtPlugin(nuxtApp => {
20 | nuxtApp.vueApp.directive('epi-edit', {
21 | beforeMount(el, binding) {
22 | toggleEditAttributes(el, binding);
23 | },
24 | updated(el, binding) {
25 | toggleEditAttributes(el, binding);
26 | }
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/plugins/updateOnContentSaved.ts:
--------------------------------------------------------------------------------
1 | // This plugin sets up the communication between on-page-edit
2 | // and our application. When content has changed, we need to
3 | // update our state.
4 |
5 | export default defineNuxtPlugin(nuxtApp => {
6 | nuxtApp.hook('page:finish', () => {
7 | if (process.server) {
8 | return;
9 | }
10 |
11 | const { updateContentByUrl } = useResolvedContent();
12 |
13 | window.addEventListener('message', async (event) => {
14 | if (event.data.id === 'contentSaved') {
15 | const previewUrl = new URL(event.data.data.previewUrl);
16 | await updateContentByUrl(previewUrl.pathname + previewUrl.search + previewUrl.hash);
17 | }
18 | }, false);
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // https://v3.nuxtjs.org/concepts/typescript
3 | "extends": "./.nuxt/tsconfig.json"
4 | }
5 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/blocks/BuyTicketBlock.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 | {{ heading || model.heading }}
15 |
16 |
17 |
18 | {{ message || model.message }}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/blocks/ContentBlock.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
23 |
{{ model.title }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/blocks/GenericBlock.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
Could not load {{ model.name }} vue component.
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/media/ImageFile.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/pages/ArtistContainerPage.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
31 |
38 |
39 |
40 |
41 |
{{ model.name }}
42 |
43 |
44 |
45 |
{{ key }}
46 |
53 |
54 |
55 |
56 |
57 |
62 |
63 |
64 |
65 |
84 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/pages/ArtistDetailsPage.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
30 |
31 |
{{ model.artistName }}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
46 |
52 |
53 |
54 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/pages/BlockPreview.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
Full
11 |
12 |
13 |
14 |
15 |
16 |
17 |
Wide
18 |
19 |
20 |
21 |
22 |
23 |
24 |
Half
25 |
26 |
27 |
28 |
29 |
30 |
31 |
Narrow
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
58 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/ClientApp/views/pages/LandingPage.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
18 |
19 |
24 |
25 |
29 | {{ model.artistsLink.expanded.name }}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
46 |
47 |
48 |
49 |
54 |
55 |
56 |
57 |
58 |
59 |
84 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using EPiServer;
2 | global using EPiServer.Core;
3 | global using EPiServer.Data;
4 | global using EPiServer.DataAbstraction;
5 | global using EPiServer.DataAnnotations;
6 | global using EPiServer.Framework.DataAnnotations;
7 | global using EPiServer.Shell.ObjectEditing;
8 | global using EPiServer.Web;
9 | global using EPiServer.Web.Routing;
10 | global using MusicFestival;
11 | global using MusicFestival.Models.Blocks;
12 | global using MusicFestival.Models.Media;
13 | global using System.ComponentModel.DataAnnotations;
14 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Blocks/BuyTicketBlock.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Blocks;
2 |
3 | [ContentType(
4 | DisplayName = "Buy Ticket Block",
5 | GUID = "ac096c4f-56ab-4396-9f5c-cfa923875c18",
6 | Description = "Allow visitors to buy a ticket",
7 | AvailableInEditMode = false)]
8 | public class BuyTicketBlock : BlockData
9 | {
10 | [CultureSpecific]
11 | [Required]
12 | public virtual string? Heading { get; set; }
13 |
14 | [CultureSpecific]
15 | [Required]
16 | public virtual string? Message { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Blocks/ContentBlock.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Blocks;
2 |
3 | [ContentType(
4 | DisplayName = "Content Block",
5 | GUID = "ed70e2a6-1d80-4a51-9aa7-bb91609ccf1b",
6 | Description = "Generic content block with text and image")]
7 | public class ContentBlock : BlockData
8 | {
9 | [CultureSpecific]
10 | [Display(
11 | Name = "Title",
12 | GroupName = SystemTabNames.Content,
13 | Order = 10)]
14 | public virtual string? Title { get; set; }
15 |
16 | [Display(
17 | Name = "Image",
18 | GroupName = SystemTabNames.Content,
19 | Order = 10)]
20 | [UIHint(UIHint.Image)]
21 | public virtual Url? Image { get; set; }
22 |
23 | [Display(
24 | Name = "Image Alignment",
25 | GroupName = SystemTabNames.Content,
26 | Order = 10)]
27 | [SelectOne(SelectionFactoryType = typeof(ImageAlignmentSelectionFactory))]
28 | [Required]
29 | public virtual string? ImageAlignment { get; set; }
30 |
31 | [CultureSpecific]
32 | [Display(
33 | Name = "Content",
34 | GroupName = SystemTabNames.Content,
35 | Order = 10)]
36 | public virtual XhtmlString? Content { get; set; }
37 | }
38 |
39 | public class ImageAlignmentSelectionFactory : ISelectionFactory
40 | {
41 | public IEnumerable GetSelections(ExtendedMetadata metadata)
42 | {
43 | return new ISelectItem[]
44 | {
45 | new SelectItem() { Text = "Left", Value = "Left" },
46 | new SelectItem() { Text = "Right", Value = "Right" }
47 | };
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Media/ImageFile.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Media;
2 |
3 | [ContentType(
4 | DisplayName = "Image File",
5 | GUID = "a736bc13-d17c-46e2-ad5d-e37bd3af086b",
6 | AvailableInEditMode = false)]
7 | [MediaDescriptor(ExtensionString = "jpg,jpeg,jpe,ico,gif,bmp,png")]
8 | public class ImageFile : ImageData
9 | { }
10 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Pages/ArtistContainerPage.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Pages;
2 |
3 | [ContentType(
4 | DisplayName = "Artist Container Page",
5 | GUID = "0a5b7b88-d0ec-4a2a-83d4-13a66d6d581d",
6 | Description = "Container page for artists")]
7 | [AvailableContentTypes
8 | (Availability.Specific,
9 | Include = new[]
10 | {
11 | typeof(ArtistDetailsPage)
12 | })]
13 | public class ArtistContainerPage : BasePage
14 | { }
15 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Pages/ArtistDetailsPage.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Pages;
2 |
3 | [ContentType(
4 | DisplayName = "Artist Page",
5 | GUID = "9e98d26a-6f06-4c35-bfbd-8b850a0fa433",
6 | Description = "Page with details about an artist")]
7 | [AvailableContentTypes(Availability.None)]
8 | public class ArtistDetailsPage : BasePage
9 | {
10 | [Display(
11 | Name = "Artist Name",
12 | GroupName = SystemTabNames.Content,
13 | Order = 10)]
14 | public virtual string? ArtistName { get; set; }
15 |
16 | [Display(
17 | Name = "Artist Photo",
18 | GroupName = SystemTabNames.Content,
19 | Order = 20)]
20 | [UIHint(UIHint.Image)]
21 | public virtual Url? ArtistPhoto { get; set; }
22 |
23 | [CultureSpecific]
24 | [Display(
25 | Name = "Artist Description",
26 | Description = "Description to appear on the artist detail page.",
27 | GroupName = SystemTabNames.Content,
28 | Order = 30)]
29 | public virtual XhtmlString? ArtistDescription { get; set; }
30 |
31 | [Display(
32 | Name = "Artist Genre",
33 | GroupName = SystemTabNames.Content,
34 | Order = 40)]
35 | public virtual string? ArtistGenre { get; set; }
36 |
37 | [Display(
38 | Name = "Performance Start Time",
39 | GroupName = SystemTabNames.Content,
40 | Order = 50)]
41 | public virtual DateTime PerformanceStartTime { get; set; }
42 |
43 | [Display(
44 | Name = "Performance End Time",
45 | GroupName = SystemTabNames.Content,
46 | Order = 55)]
47 | public virtual DateTime PerformanceEndTime { get; set; }
48 |
49 | [Display(
50 | Name = "Stage Name",
51 | GroupName = SystemTabNames.Content,
52 | Order = 60)]
53 | public virtual string? StageName { get; set; }
54 |
55 | [Display(
56 | Name = "Headliner",
57 | GroupName = SystemTabNames.Content,
58 | Order = 70)]
59 | public virtual bool ArtistIsHeadliner { get; set; }
60 | }
61 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Pages/BasePage.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Pages;
2 |
3 | public abstract class BasePage : PageData
4 | { }
5 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Models/Pages/LandingPage.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Models.Pages;
2 |
3 | [ContentType(
4 | DisplayName = "Landing Page",
5 | GUID = "46278700-3173-4945-b143-befe071f0f71",
6 | Description = "Starting page for the site")]
7 | [AvailableContentTypes(
8 | Availability.Specific,
9 | Include = new[]
10 | {
11 | typeof(ArtistContainerPage)
12 | })]
13 | public class LandingPage : BasePage
14 | {
15 | [Display(
16 | Name = "Buy Ticket",
17 | GroupName = SystemTabNames.Content,
18 | Order = 5)]
19 | public virtual BuyTicketBlock? BuyTicketBlock { get; set; }
20 |
21 | [CultureSpecific]
22 | [Display(
23 | Name = "Hero Image",
24 | GroupName = SystemTabNames.Content,
25 | Order = 10)]
26 | [UIHint(UIHint.Image)]
27 | public virtual Url? HeroImage { get; set; }
28 |
29 | [CultureSpecific]
30 | [Display(
31 | Name = "Title",
32 | GroupName = SystemTabNames.Content,
33 | Order = 20)]
34 | public virtual string? Title { get; set; }
35 |
36 |
37 | [CultureSpecific]
38 | [Display(
39 | Name = "Subtitle",
40 | GroupName = SystemTabNames.Content,
41 | Order = 30)]
42 | public virtual string? Subtitle { get; set; }
43 |
44 | [Required]
45 | [Display(
46 | Name = "Link to Artists list",
47 | GroupName = SystemTabNames.Content,
48 | Order = 35)]
49 | public virtual PageReference? ArtistsLink { get; set; }
50 |
51 | [CultureSpecific]
52 | [Display(
53 | Name = "Main Content Area",
54 | GroupName = SystemTabNames.Content,
55 | Order = 40)]
56 | [AllowedTypes(typeof(ContentBlock), typeof(ImageFile))]
57 | public virtual ContentArea? MainContentArea { get; set; }
58 |
59 | [CultureSpecific]
60 | [Display(
61 | Name = "Footer Content Area",
62 | GroupName = SystemTabNames.Content,
63 | Order = 50)]
64 | [AllowedTypes(typeof(ContentBlock), typeof(ImageFile))]
65 | public virtual ContentArea? FooterContentArea { get; set; }
66 | }
67 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/MusicFestival.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | MusicFestival
6 | enable
7 | enable
8 | false
9 |
10 | ClientApp\
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
38 |
39 |
40 |
41 |
46 |
47 |
48 |
49 | wwwroot\%(RecursiveDir)%(FileName)%(Extension)
50 | PreserveNewest
51 | true
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/MusicFestival.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31912.275
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicFestival", "MusicFestival.csproj", "{9ECF0E04-7795-46AF-9966-A2F41D99BA51}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EPiServer.ContentDelivery.NodeProxy", "..\..\src\EPiServer.ContentDelivery.NodeProxy\EPiServer.ContentDelivery.NodeProxy.csproj", "{E184BDC0-EA5C-4A46-A491-DCF2B8758233}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {9ECF0E04-7795-46AF-9966-A2F41D99BA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {9ECF0E04-7795-46AF-9966-A2F41D99BA51}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {9ECF0E04-7795-46AF-9966-A2F41D99BA51}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {9ECF0E04-7795-46AF-9966-A2F41D99BA51}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {E184BDC0-EA5C-4A46-A491-DCF2B8758233}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {E184BDC0-EA5C-4A46-A491-DCF2B8758233}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {E184BDC0-EA5C-4A46-A491-DCF2B8758233}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {E184BDC0-EA5C-4A46-A491-DCF2B8758233}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {EB31D6D4-8C83-4CFB-A557-2D5426EB3316}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Program.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival;
2 |
3 | public class Program
4 | {
5 | public static void Main(string[] args)
6 | {
7 | CreateHostBuilder(args).Build().Run();
8 | }
9 |
10 | public static IHostBuilder CreateHostBuilder(string[] args) =>
11 | Host.CreateDefaultBuilder(args)
12 | .ConfigureCmsDefaults()
13 | .ConfigureWebHostDefaults(webBuilder =>
14 | {
15 | webBuilder.UseStartup();
16 | });
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "MusicFestival": {
4 | "commandName": "Project",
5 | "launchBrowser": true,
6 | "applicationUrl": "http://localhost:8080",
7 | "environmentVariables": {
8 | "ASPNETCORE_ENVIRONMENT": "Development"
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.AspNetCore.SpaProxy": "Information",
7 | "Microsoft.Hosting.Lifetime": "Information",
8 | "EPiServer": "Warning",
9 | "EPiServer.Framework.Cache": "Error",
10 | "Yarp": "Warning"
11 | }
12 | },
13 | "AllowedHosts": "*",
14 | "ConnectionStrings": {
15 | "EPiServerDB": "Data Source=(LocalDb)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\\musicfestival.mdf;Initial Catalog=musicfestival-coupled;Integrated Security=True;Connect Timeout=30"
16 | },
17 | "Episerver": {
18 | "CmsUI": {
19 | "UI": {
20 | "WebSocketEnabled": false
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/diagram.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-coupled/diagram.jpg
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/setup.cmd:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | SETLOCAL
3 |
4 | SET BASE=.\App_Data
5 |
6 | ECHO Removed all files from the App_Data folder
7 | IF EXIST %BASE%\blobs\ RMDIR %BASE%\blobs\ /S/Q || EXIT /B 1
8 | IF EXIST %BASE%\musicfestival.mdf DEL %BASE%\musicfestival.mdf /F/Q || EXIT /B 1
9 | IF EXIST %BASE%\musicfestival_log.ldf DEL %BASE%\musicfestival_log.ldf /F/Q || EXIT /B 1
10 |
11 | ECHO Created new database
12 | XCOPY %BASE%\db.mdf %BASE%\musicfestival.mdf* /Y/C || EXIT /B 1
13 |
14 | EXIT /B %ERRORLEVEL%
15 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-coupled/setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | BASE=./App_Data
4 |
5 | echo "Removed all files from the App_Data folder"
6 | if [ -d $BASE/blobs ]
7 | then
8 | rm -rf $BASE/blobs
9 | fi
10 | if [ -d $BASE/musicfestival.mdf ]
11 | then
12 | rm -rf $BASE/musicfestival.mdf
13 | fi
14 | if [ -d $BASE/musicfestival_log.ldf ]
15 | then
16 | rm -rf $BASE/musicfestival_log.ldf
17 | fi
18 |
19 | echo "Created new database"
20 | cp $BASE/db.mdf $BASE/musicfestival.mdf
21 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/MusicFestivalDecoupled.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30621.155
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicFestival.Backend", "backend\MusicFestival.Backend.csproj", "{92C1F4EC-FD97-4D27-8393-D0B115196A37}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicFestival.Frontend", "frontend\MusicFestival.Frontend.csproj", "{5EEA19AB-0C94-4A11-8094-C8D707F12F3B}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {92C1F4EC-FD97-4D27-8393-D0B115196A37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {92C1F4EC-FD97-4D27-8393-D0B115196A37}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {92C1F4EC-FD97-4D27-8393-D0B115196A37}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {92C1F4EC-FD97-4D27-8393-D0B115196A37}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {5EEA19AB-0C94-4A11-8094-C8D707F12F3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {5EEA19AB-0C94-4A11-8094-C8D707F12F3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {5EEA19AB-0C94-4A11-8094-C8D707F12F3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {5EEA19AB-0C94-4A11-8094-C8D707F12F3B}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {09D7AFCA-C728-4484-AEF3-3EE7AAD271B9}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/App_Data/DefaultSiteContent.episerverdata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-decoupled/backend/App_Data/DefaultSiteContent.episerverdata
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/App_Data/db.mdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-decoupled/backend/App_Data/db.mdf
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/MusicFestival.Backend.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | MusicFestival.Backend
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/Program.cs:
--------------------------------------------------------------------------------
1 | namespace MusicFestival.Backend;
2 |
3 | public class Program
4 | {
5 | public static void Main(string[] args)
6 | {
7 | CreateHostBuilder(args).Build().Run();
8 | }
9 |
10 | public static IHostBuilder CreateHostBuilder(string[] args) =>
11 | Host.CreateDefaultBuilder(args)
12 | .ConfigureCmsDefaults()
13 | .ConfigureWebHostDefaults(webBuilder =>
14 | {
15 | webBuilder.UseStartup();
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/ProvisionDatabase.cs:
--------------------------------------------------------------------------------
1 | using EPiServer.Web;
2 |
3 | namespace MusicFestival.Backend;
4 |
5 | public class ProvisionDatabase : IHostedService
6 | {
7 | private readonly ILogger _logger;
8 | private readonly ISiteDefinitionRepository _siteDefinitionRepository;
9 |
10 | public ProvisionDatabase(
11 | ILogger logger,
12 | ISiteDefinitionRepository siteDefinitionRepository)
13 | {
14 | _logger = logger;
15 | _siteDefinitionRepository = siteDefinitionRepository;
16 | }
17 |
18 | public async Task StartAsync(CancellationToken cancellationToken)
19 | {
20 | await AddPrimarySiteHost();
21 | }
22 |
23 | public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
24 |
25 | private Task AddPrimarySiteHost()
26 | {
27 | _logger.LogInformation("Provisioning primary site host.");
28 |
29 | var site = _siteDefinitionRepository
30 | .List()
31 | .FirstOrDefault();
32 |
33 | if (site is null)
34 | {
35 | _logger.LogInformation("Primary site host already exists.");
36 |
37 | return Task.CompletedTask;
38 | }
39 | else
40 | {
41 | site = site.CreateWritableClone();
42 | }
43 |
44 | if (!site.Hosts.Any(x => x.Type == HostDefinitionType.Primary))
45 | {
46 | var editHost = site.Hosts.First(x => x.Name != "*");
47 | editHost.Type = HostDefinitionType.Edit;
48 |
49 | site.Hosts.Add(new HostDefinition
50 | {
51 | Type = HostDefinitionType.Primary,
52 | Name = "localhost:8080"
53 | });
54 | }
55 |
56 | _siteDefinitionRepository.Save(site);
57 |
58 | return Task.CompletedTask;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "EPiServerDB": "Data Source=(LocalDb)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\\musicfestival.mdf;Initial Catalog=musicfestival-decoupled;Integrated Security=True;Connect Timeout=30"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "MusicFestival.Backend": {
4 | "commandName": "Project",
5 | "dotnetRunMessages": true,
6 | "launchBrowser": true,
7 | "applicationUrl": "http://localhost:8081",
8 | "launchUrl": "episerver/cms",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development"
11 | }
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/backend/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-decoupled/backend/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | charset = utf-8
3 | indent_style = space
4 | indent_size = 2
5 | end_of_line = crlf
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 | max_line_length = 200
9 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/.env.development:
--------------------------------------------------------------------------------
1 | VUE_APP_CONTENT_DELIVERY_API=http://localhost:8081
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/.env.test:
--------------------------------------------------------------------------------
1 | VUE_APP_CONTENT_DELIVERY_API=http://localhost:8081
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/MusicFestival.Frontend.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 | false
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset',
4 | ],
5 | };
6 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "music-festival-decoupled",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build"
8 | },
9 | "dependencies": {
10 | "@episerver/content-delivery": "file:../../../src/@episerver/content-delivery",
11 | "core-js": "^3.8.3",
12 | "lodash": "^4.17.20",
13 | "vue": "^3.2.13",
14 | "vue-router": "^4.0.3",
15 | "vuex": "^4.0.0"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.12.16",
19 | "@vue/cli-plugin-babel": "~5.0.0",
20 | "@vue/cli-plugin-router": "~5.0.0",
21 | "@vue/cli-plugin-vuex": "~5.0.0",
22 | "@vue/cli-service": "~5.0.0",
23 | "import-glob-loader": "^1.1.0",
24 | "less": "^4.1.3",
25 | "less-loader": "^11.0.0"
26 | },
27 | "browserslist": [
28 | "> 1%",
29 | "last 2 versions",
30 | "not dead",
31 | "not ie 11"
32 | ]
33 | }
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/samples/music-festival-vue-decoupled/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
36 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/images/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/common/variables.less:
--------------------------------------------------------------------------------
1 | @basePath: '/';
2 |
3 | // - Fonts -
4 | @baseFontSize: 16;
5 | @fontFamilyPrimary: "barlow", "Helvetica", "Arial", sans-serif;
6 | @fontFamilySecondary: "ubuntu", "Helvetica", "Arial", serif;
7 | @fontHeading: "ubuntu-bold";
8 | @fontSubHeading: "barlow-bold";
9 |
10 | // - Container Width -
11 | @widthContainer: 1204px;
12 |
13 | // - Text Sizes -
14 | @textSizeXSmall: unit((12 / @baseFontSize), em); // 12px
15 | @textSizeSmall: unit((14 / @baseFontSize), em); // 14px
16 | @textSizeMedium: unit((16 / @baseFontSize), em); // 16px
17 | @textSizeLarge: unit((18 / @baseFontSize), em); // 18px
18 | @textSizeXLarge: unit((20 / @baseFontSize), em); // 20px
19 |
20 | // - Colors -
21 | @backgroundColor: @colorBlue;
22 | @backgroundGradient: linear-gradient(to bottom, rgba(255,0,0,0) 0%, rgba(255,0,0,.35) 100%);
23 | @colorBlack: #000;
24 | @colorWhite: #fff;
25 | @colorBlue: #1A237E;
26 | @colorTurquoise: #41FFF5;
27 | @colorPink: #EC407A;
28 | //# Text
29 | @colorText: #fff;
30 | @colorTextLight: #aaa;
31 |
32 | //# Links
33 | @colorLink: #fff;
34 | @colorLinkHover: #ccc;
35 |
36 | // - Space -
37 | @spaceDefault: unit((20 / @baseFontSize), em);
38 | @spaceXSmall: @spaceDefault / 2;
39 | @spaceSmall: @spaceDefault / 1.5;
40 | @spaceMedium: @spaceDefault;
41 | @spaceLarge: @spaceDefault * 1.5;
42 | @spaceXLarge: @spaceDefault * 2;
43 |
44 | @space: 1em;
45 | @space-and-half: @space*1.5;
46 | @space-double: @space*2;
47 | @space-triple: @space*3;
48 | @space-quad: @space*4;
49 | @space-half: @space/2;
50 | @space-quarter: @space/4;
51 |
52 |
53 | // - Breakpoint Widths -
54 | @widthXSmall: (420 / @baseFontSize);
55 | @widthSmall: (544 / @baseFontSize);
56 | @widthMedium: (768 / @baseFontSize);
57 | @widthLarge: (920 / @baseFontSize);
58 | @widthXLarge: (1200 / @baseFontSize);
59 |
60 | // - Breakpoint Media Queries -
61 | @bpXSmall: ~ "only screen and (min-width: @{widthXSmall}em), print";
62 | @bpSmall: ~ "only screen and (min-width: @{widthSmall}em), print";
63 | @bpMedium: ~ "only screen and (min-width: @{widthMedium}em), print";
64 | @bpLarge: ~ "only screen and (min-width: @{widthLarge}em), print";
65 | @bpXLarge: ~ "only screen and (min-width: @{widthXLarge}em)";
66 |
67 | @bpMediumMax: ~ "only screen and (max-width: @{widthMedium}em), print";
68 |
69 |
70 | // - Size Prefixes -
71 | @prefixXSmall: ~ "xsm-";
72 | @prefixSmall: ~ "sm-";
73 | @prefixMedium: ~ "md-";
74 | @prefixLarge: ~ "lg-";
75 | @prefixXLarge: ~ "xlg-";
76 |
77 | // menu
78 | @menuTransition: left .4s ease-in-out;
79 |
80 | // Grid
81 | @gutterSize: 24px; // needs to be in pixels
82 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/atoms/Button.less:
--------------------------------------------------------------------------------
1 | /*#
2 |
3 | The button classes are best applied to links and buttons.
4 | These components can be used in forms, as calls to action, or as part of the general UI of the site/app.
5 | */
6 |
7 | @import "../../common/variables.less";
8 |
9 | .Button, button {
10 | background: @colorTurquoise;
11 | border-radius: 2em;
12 | color: @colorBlue;
13 | text-transform: uppercase;
14 | font-size: 12px;
15 | font-weight: 700;
16 | letter-spacing: 1.2px;
17 | padding: 12px 26px;
18 | font-family: @fontFamilyPrimary;
19 | box-shadow: 0 0 12px 0 fade(@colorBlack, 36%);
20 | border: 0;
21 | transition: all 300ms;
22 | &:hover{
23 | text-decoration: none;
24 | background-color: @colorBlue;
25 | color: @colorTurquoise;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/atoms/Heading.less:
--------------------------------------------------------------------------------
1 | @import (reference) "../../common/variables.less";
2 |
3 | @Heading-color: @colorText;
4 | @Heading-size1: 80px;
5 | @Heading-size2: 80px;
6 | @Heading-size3: 48px;
7 | @Heading-size4: 32px;
8 | @Heading-size5: 22px;
9 | @Heading-size6: 14px;
10 |
11 | .Heading {
12 | display: block;
13 | margin-top: 0;
14 | margin-bottom: .5em;
15 | color: @Heading-color;
16 | font-weight: 700;
17 | line-height: 1em;
18 | text-transform: lowercase;
19 | font-family: @fontFamilySecondary;
20 |
21 | a:hover & {
22 | color: @colorLinkHover;
23 | }
24 | }
25 |
26 | .Heading--h1, h1 {
27 | &:extend(.Heading all);
28 | font: 48px/50px @fontHeading;
29 | margin: .25em 0;
30 |
31 | @media @bpMedium {
32 | font-size: unit((@Heading-size1 / @baseFontSize), em);
33 | }
34 | }
35 |
36 | .Heading--h2, h2 {
37 | &:extend(.Heading all);
38 | font-size: unit(((@Heading-size2*.65) / @baseFontSize), em);
39 | margin: 20px 0 10px;
40 |
41 | @media @bpMedium {
42 | font-size: unit((@Heading-size2 / @baseFontSize), em);
43 | }
44 | }
45 |
46 | .Heading--h3, h3 {
47 | &:extend(.Heading all);
48 | font-size: unit(((@Heading-size3*.75) / @baseFontSize), em);
49 | font-family: @fontSubHeading;
50 |
51 | @media @bpMedium {
52 | font-size: unit((@Heading-size3 / @baseFontSize), em);
53 | }
54 | }
55 |
56 | .Heading--h4, h4 {
57 | &:extend(.Heading all);
58 | font-weight: 300;
59 | font-size: 18px;
60 |
61 | @media @bpMedium {
62 | font-size: unit((@Heading-size4 / @baseFontSize), em);
63 | }
64 | }
65 |
66 | .Heading--h5, h5, .Heading--h6, h6 {
67 | &:extend(.Heading all);
68 | font-weight: 700;
69 | letter-spacing: 2.2px;
70 | font-family: @fontFamilyPrimary;
71 | text-transform: uppercase;
72 | font-size: 18px;
73 |
74 | @media @bpMedium {
75 | font-size: unit((@Heading-size5 / @baseFontSize), em);
76 | }
77 | }
78 |
79 | .Heading--h6, h6 {
80 | &:extend(.Heading all);
81 | text-transform: uppercase;
82 | margin-bottom: 0;
83 | font-size: 1em;
84 |
85 | @media @bpMedium {
86 | font-size: unit((@Heading-size6 / @baseFontSize), em);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/atoms/Icon.less:
--------------------------------------------------------------------------------
1 | /*#
2 |
3 | Icon component displaying icons using an Icomoon.io generated icon web font.
4 |
5 | To add or remove icons navigate to https://icomoon.io/app/ and import "selection.json" from the Styles/Fonts/Icons folder.
6 |
7 | **Usage**
8 | To use an icon in a different component start by importing the Icon.less file:
9 | @import (reference) "../Atoms/Icon.less";
10 |
11 | Then extend the CSS rule with .Icon and set "content" to the icon you're looking for:
12 | .MyComponent {
13 | &:after {
14 | &:extend(.Icon);
15 | content: @Icon--checkmark;
16 | }
17 | }
18 |
19 | Examples:
20 |
21 |
22 |
23 |
24 |
25 | */
26 |
27 | @import "../../common/variables.less";
28 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/atoms/Link.less:
--------------------------------------------------------------------------------
1 | .Link {}
2 |
3 | .Link--skipLink {
4 | border: 0;
5 | clip: rect(0 0 0 0);
6 | height: 1px;
7 | margin: -1px;
8 | overflow: hidden;
9 | padding: 0;
10 | position: absolute;
11 | width: 1px;
12 |
13 | &:focus {
14 | clip: auto;
15 | height: auto;
16 | margin: 0;
17 | width: auto;
18 | background: #fff;
19 | padding: .1875em .3125em;
20 | z-index: 1000;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/molecules/Content.less:
--------------------------------------------------------------------------------
1 | /*#
2 |
3 | Responsible for styling CMS content where it is not possible to add custom CSS-classes.
4 | For example adding styling for ,
etc.
5 | */
6 |
7 | @import "../../common/variables.less";
8 |
9 | .Content {
10 | }
11 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/molecules/ContentBlock.less:
--------------------------------------------------------------------------------
1 | @import "../../common/variables.less";
2 |
3 | .Preview {
4 | > .u-md-size1of2, > .u-md-size1of3 {
5 | .Grid .Grid-cell {
6 | width: 100% !important;
7 | }
8 | }
9 | }
10 |
11 | .ContentArea {
12 | > .u-md-size1of2,
13 | > .u-md-size1of3 {
14 | .ContentBlock {
15 | .Grid-cell {
16 | width: 100% !important;
17 | }
18 | }
19 | }
20 | }
21 |
22 | footer {
23 | background-color: fade(@colorPink, 24%);
24 | background-image: url('../../../images/pattern-wave.svg');
25 |
26 | .ContentBlock {
27 |
28 | .Page-container {
29 | max-width: 920px;
30 | padding: 3em 1em 0;
31 | }
32 |
33 | h2 {
34 | font-size: 48px;
35 | }
36 |
37 | .Grid-cell:first-child {
38 | order: 1;
39 | text-align: center;
40 |
41 | @media @bpMedium {
42 | order: 0;
43 | }
44 | }
45 | }
46 | }
47 |
48 | .ContentBlock {
49 | ol {
50 | list-style-type: none;
51 | padding: 0;
52 | line-height: 1.8;
53 | margin-bottom: 2em;
54 | font-size: 20px;
55 |
56 | li {
57 | display: inline-block;
58 | position: relative;
59 | padding-right: 20px;
60 |
61 | &:after {
62 | content: '';
63 | width: 4px;
64 | height: 4px;
65 | position: absolute;
66 | top: 50%;
67 | right: 6px;
68 | transform: translateY(-50%);
69 | background: @colorPink;
70 | border-radius: 50%;
71 | }
72 |
73 | &:last-child {
74 | &:after {
75 | content: none;
76 | }
77 | }
78 | }
79 | }
80 |
81 | a {
82 | background: @colorTurquoise;
83 | border-radius: 2em;
84 | color: @colorBlue;
85 | text-transform: uppercase;
86 | font-size: 12px;
87 | font-weight: 700;
88 | letter-spacing: 1.2px;
89 | padding: 12px 26px;
90 | font-family: @fontFamilyPrimary;
91 | box-shadow: 0 0 12px 0 fade(@colorBlack, 36%);
92 | border: 0;
93 | transition: all 300ms;
94 | &:hover{
95 | text-decoration: none;
96 | background-color: @colorBlue;
97 | color: @colorTurquoise;
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/molecules/Hero.less:
--------------------------------------------------------------------------------
1 | @import "../../common/variables.less";
2 |
3 | .Hero {
4 | height: 500px;
5 |
6 | width: 100%;
7 | display: flex;
8 | align-items: center;
9 | justify-content: center;
10 | flex-direction: column;
11 | text-align: center;
12 | position: relative;
13 |
14 | h1, h2, h3, h4, h5, p {
15 | color: @colorWhite;
16 | text-align: center;
17 | }
18 |
19 | &-content {
20 | z-index: 10;
21 | }
22 |
23 | &-image {
24 | background-size: cover;
25 | position: absolute;
26 | top: 0;
27 | left: 0;
28 | width: 100%;
29 | height: 100%;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/organisms/Footer.less:
--------------------------------------------------------------------------------
1 | /*#
2 | Component for the footer section.
3 | */
4 |
5 | @import "../../common/variables.less";
6 |
7 | .FooterBottom {
8 | background: @backgroundColor;
9 | text-align: center;
10 | padding: 1em;
11 | p{
12 | margin: 0;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/organisms/Header.less:
--------------------------------------------------------------------------------
1 | /*#
2 | Component for the header section.
3 | */
4 |
5 | @import "../../common/variables.less";
6 |
7 | @Header-bg: #f4f4f4;
8 | @Header-border: darken(#f4f4f4, 5%);
9 |
10 | .Header {
11 | border-bottom: 1px solid @Header-border;
12 | width: 100%;
13 | }
14 |
15 | .Header-secondary {
16 | padding: 0 @spaceMedium;
17 | background: @Header-bg;
18 | text-align: right;
19 | }
20 |
21 | .Header-primary {
22 | padding: (@spaceLarge - .5em) @spaceMedium;
23 | }
24 |
25 | .Header-primaryContainer {
26 | display: flex;
27 | align-items: center;
28 | }
29 |
30 | .Header-logo {
31 | }
32 |
33 | .Header-primaryNav {
34 | flex: 1 1 auto;
35 | text-align: right;
36 | }
37 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/components/organisms/NavBar.less:
--------------------------------------------------------------------------------
1 | /*#
2 | Component for the navigation bar.
3 | */
4 |
5 | @import "../../common/variables.less";
6 |
7 | .NavBar {
8 | padding: 10px 5px 0;
9 |
10 | @media (min-width:425px) {
11 | padding: 20px 0 0;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/main.less:
--------------------------------------------------------------------------------
1 | @import "common/variables.less";
2 | @import "common/base.less";
3 |
4 | // Import all the common stuff, like mixins, normalize, etc
5 | @import "common/_normalize.less";
6 | @import "common/_typography.less";
7 | @import "common/_grid.less";
8 |
9 | // Utility classes
10 | @import "utils/**/*.less";
11 |
12 | // Atomic design
13 | @import "components/Atoms/**/*.less";
14 | @import "components/Molecules/**/*.less";
15 | @import "components/Organisms/**/*.less";
16 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/utils/utils-align.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Vertical alignment utilities
3 | * Depends on an appropriate `display` value.
4 | */
5 |
6 | .u-alignBaseline {
7 | vertical-align: baseline !important;
8 | }
9 |
10 | .u-alignBottom {
11 | vertical-align: bottom !important;
12 | }
13 |
14 | .u-alignMiddle {
15 | vertical-align: middle !important;
16 | }
17 |
18 | .u-alignTop {
19 | vertical-align: top !important;
20 | }
21 |
22 | .u-alignCenter {
23 | margin-left: auto !important;
24 | margin-right: auto !important;
25 | }
26 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/utils/utils-layout.less:
--------------------------------------------------------------------------------
1 | /**
2 | * Contain floats
3 | *
4 | * Make an element expand to contain floated children.
5 | * Uses pseudo-elements (micro clearfix).
6 | *
7 | * 1. The space content is one way to avoid an Opera bug when the
8 | * `contenteditable` attribute is included anywhere else in the document.
9 | * Otherwise it causes space to appear at the top and bottom of the
10 | * element.
11 | * 2. The use of `table` rather than `block` is only necessary if using
12 | * `:before` to contain the top-margins of child elements.
13 | */
14 |
15 | .u-cf:before,
16 | .u-cf:after {
17 | content: " "; /* 1 */
18 | display: table; /* 2 */
19 | }
20 |
21 | .u-cf:after {
22 | clear: both;
23 | }
24 |
25 | /**
26 | * New block formatting context
27 | *
28 | * This affords some useful properties to the element. It won't wrap under
29 | * floats. Will also contain any floated children.
30 |
31 | * N.B. This will clip overflow. Use the alternative method below if this is
32 | * problematic.
33 | */
34 |
35 | .u-nbfc {
36 | overflow: hidden !important;
37 | }
38 |
39 | /**
40 | * New block formatting context (alternative)
41 | *
42 | * Alternative method when overflow must not be clipped.
43 | *
44 | * 1. Create a new block formatting context (NBFC).
45 | * 2. Avoid shrink-wrap behaviour of table-cell.
46 | *
47 | * N.B. This breaks down in some browsers when elements within this element
48 | * exceed its width.
49 | */
50 |
51 | .u-nbfcAlt {
52 | display: table-cell !important; /* 1 */
53 | width: 10000px !important; /* 2 */
54 | }
55 |
56 | /**
57 | * Floats
58 | */
59 |
60 | .u-pullLeft {
61 | float: left !important;
62 | }
63 |
64 | .u-pullRight {
65 | float: right !important;
66 | }
67 |
68 | .u-clear {
69 | clear: both;
70 | }
71 |
72 | .u-noOverflow{
73 | overflow:hidden;
74 | }
75 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/utils/utils-link.less:
--------------------------------------------------------------------------------
1 | @import (reference) "../common/variables.less";
2 |
3 | /**
4 | * Clean link
5 | *
6 | * A link without any text-decoration at all.
7 | */
8 |
9 | .u-linkClean,
10 | .u-linkClean:hover,
11 | .u-linkClean:focus,
12 | .u-linkClean:active {
13 | text-decoration: none !important;
14 | }
15 |
16 | /**
17 | * Link complex
18 | *
19 | * A common pattern is to have a link with several pieces of text and/or an
20 | * icon, where only one piece of text should display the underline when the
21 | * link is the subject of user interaction.
22 | */
23 |
24 | .u-linkComplex,
25 | .u-linkComplex:hover,
26 | .u-linkComplex:focus,
27 | .u-linkComplex:active {
28 | text-decoration: none !important;
29 | }
30 |
31 | .u-linkComplex:hover .u-linkComplexTarget,
32 | .u-linkComplex:focus .u-linkComplexTarget,
33 | .u-linkComplex:active .u-linkComplexTarget {
34 | text-decoration: underline !important;
35 | }
36 |
37 | /**
38 | * Block-level link
39 | *
40 | * Combination of traits commonly used in vertical navigation lists.
41 | */
42 |
43 | .u-linkBlock,
44 | .u-linkBlock:hover,
45 | .u-linkBlock:focus,
46 | .u-linkBlock:active {
47 | display: block !important;
48 | text-decoration: none !important;
49 | }
50 |
51 | /**
52 | * Changes a link to look like plain text.
53 | */
54 | .u-linkText {
55 | color: @colorText;
56 | }
57 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/utils/utils-position.less:
--------------------------------------------------------------------------------
1 | .u-posAbsolute {
2 | position: absolute !important;
3 | }
4 |
5 | /**
6 | * Pins to all corners by default. But when a width and/or height are
7 | * provided, the element will be centered in its nearest relatively-positioned
8 | * ancestor.
9 | */
10 |
11 | .u-posAbsoluteCenter {
12 | bottom: 0 !important;
13 | left: 0 !important;
14 | margin: auto !important;
15 | position: absolute !important;
16 | right: 0 !important;
17 | top: 0 !important;
18 | }
19 |
20 | /**
21 | * 1. Make sure fixed elements are promoted into a new layer, for performance
22 | * reasons.
23 | */
24 |
25 | .u-posFixed {
26 | position: fixed !important;
27 | backface-visibility: hidden; /* 1 */
28 | }
29 |
30 | .u-posRelative {
31 | position: relative !important;
32 | }
33 |
34 | .u-posStatic {
35 | position: static !important;
36 | }
37 |
38 | .u-posBottomCenter{
39 | position: absolute;
40 | bottom: 0;
41 | left: 50%;
42 | transform: translateX(-50%);
43 | }
44 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/assets/styles/utils/utils-text.less:
--------------------------------------------------------------------------------
1 | @import (reference) "../common/variables.less";
2 |
3 | /**
4 | * Text font size.
5 | */
6 |
7 | .u-textXSmall {
8 | font-size: @textSizeXSmall !important;
9 | }
10 | .u-textSmall {
11 | font-size: @textSizeSmall !important;
12 | }
13 | .u-textMedium,
14 | .u-textNormal {
15 | font-size: @textSizeMedium !important;
16 | }
17 | .u-textLarge {
18 | font-size: @textSizeLarge !important;
19 | }
20 | .u-textXLarge {
21 | font-size: @textSizeXLarge !important;
22 | }
23 |
24 | /**
25 | * Text font weights
26 | */
27 |
28 | .u-textWeightThin {
29 | font-weight: 100 !important;
30 | }
31 | .u-textWeightLight {
32 | font-weight: 200 !important;
33 | }
34 | .u-textWeightNormal {
35 | font-weight: normal !important;
36 | }
37 | .u-textWeightMedium {
38 | font-weight: 500 !important;
39 | }
40 | .u-textWeightBold {
41 | font-weight: bold !important;
42 | }
43 |
44 | /**
45 | * Word breaking
46 | *
47 | * Break strings when their length exceeds the width of their container.
48 | */
49 |
50 | .u-textBreak {
51 | word-wrap: break-word !important;
52 | }
53 |
54 | /**
55 | * Horizontal text alignment
56 | */
57 |
58 | .u-textCenter {
59 | text-align: center !important;
60 | }
61 |
62 | .u-textLeft {
63 | text-align: left !important;
64 | }
65 |
66 | .u-textRight {
67 | text-align: right !important;
68 | }
69 |
70 | /**
71 | * Prevent whitespace wrapping
72 | */
73 |
74 | .u-textNoWrap {
75 | white-space: nowrap !important;
76 | }
77 |
78 | /**
79 | * Text truncation
80 | *
81 | * Prevent text from wrapping onto multiple lines, and truncate with an
82 | * ellipsis.
83 | *
84 | * 1. Ensure that the node has a maximum width after which truncation can
85 | * occur.
86 | * 2. Fix for IE 8/9 if `word-wrap: break-word` is in effect on ancestor
87 | * nodes.
88 | */
89 |
90 | .u-textTruncate {
91 | max-width: 100%; /* 1 */
92 | overflow: hidden !important;
93 | text-overflow: ellipsis !important;
94 | white-space: nowrap !important;
95 | word-wrap: normal !important; /* 2 */
96 | }
97 |
98 | /*
99 | * Inherit the ancestor's text color.
100 | */
101 | .u-textInheritColor {
102 | color: inherit !important;
103 | }
104 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/EpiBlockComponentSelector.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
35 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/EpiContentArea.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
23 |
24 |
25 |
52 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/EpiLink.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
60 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/EpiPageComponentSelector.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
51 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/EpiProperty.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | Edit property: {{propertyName}}
11 |
12 |
13 |
14 |
24 |
25 |
31 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/EpiViewModeLink.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
34 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/ArtistImage.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
11 |
12 |
13 |
23 |
24 |
40 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/BackButton.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
13 |
14 |
15 |
27 |
28 |
44 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/Card.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
16 |
17 |
18 |
19 |
31 |
32 |
69 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/ConditionalImage.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/Hero.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
34 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/LanguageSelector.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
10 | {{item.displayName}}
11 |
12 |
13 |
14 |
15 |
30 |
31 |
104 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/components/widgets/Modal.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | default content
15 |
16 |
17 |
18 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
44 |
45 |
97 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/constants.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Translates the different display options defined on the server to CSS
3 | * classes to use on the client.
4 | */
5 |
6 | export const DISPLAY_OPTIONS = {
7 | full: 'u-md-sizeFull',
8 | wide: 'u-md-size2of3',
9 | half: 'u-md-size1of2',
10 | narrow: 'u-md-size1of3',
11 | };
12 |
13 | export default {
14 | DISPLAY_OPTIONS,
15 | };
16 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/directives/epiEdit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The directive `v-epi-edit` is used similarly to @Html.EditAttributes() in
3 | * Razor views. It enables On-Page Editing on elements using the `data-epi-edit`
4 | * property (introduced in Episerver CMS UI 11.X.0) and disables the DOM
5 | * updating from the CMS so that Vue can keep the responsibility over the DOM.
6 | *
7 | * It's enabled by the `isEditable` value that is stored in the Vuex store, but
8 | * can be overwritten by a component having a property named
9 | * `epiDisableEditing` being true.
10 | *
11 | * Usage can be found on most Vue components, such as ArtistDetailsPage.vue.
12 | */
13 |
14 | import store from '@/store';
15 |
16 | function toggleEditAttributes(el, binding) {
17 | const siteIsEditable = store.state.epiContext.isEditable;
18 | const componentIsEditable = !binding.instance.epiDisableEditing;
19 |
20 | if (siteIsEditable && componentIsEditable) {
21 | el.setAttribute('data-epi-edit', binding.value);
22 | } else {
23 | el.removeAttribute('data-epi-edit');
24 | }
25 | }
26 |
27 | export default {
28 | beforeMount: toggleEditAttributes,
29 | updated: toggleEditAttributes,
30 | };
31 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/epiBootstrap.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Context flags that are useful to enable properly working On-Page Editing.
3 | * Sets the context in the vuex store to be used on every component that is
4 | * interested.
5 | *
6 | * These values are `false` by default and will be updated when the page has
7 | * finished loading. See the event handler at the bottom of the page.
8 | *
9 | * Also registers the `contentSaved` event that will update
10 | * the model in the store during editing.
11 | */
12 |
13 | import store from "@/store";
14 | import { parsePreviewToken } from "@/urlHelpers";
15 | import { UPDATE_CONTEXT, UPDATE_PREVIEW_TOKEN } from "@/store/modules/epiContext";
16 | import { UPDATE_MODEL_BY_URL } from "@/store/modules/epiDataModel";
17 |
18 | function setContext() {
19 | // Make the context available to all Vue components.
20 | store.commit(UPDATE_CONTEXT, window.epi.inEditMode);
21 |
22 | // If we're in an editable context we want to update the model on every change by the editor.
23 | if (window.epi.isEditable) {
24 | window.epi.subscribe("contentSaved", (message) => {
25 | var parsed = parsePreviewToken(message.previewUrl);
26 | store.commit(UPDATE_PREVIEW_TOKEN, parsed.previewToken);
27 | store.dispatch(UPDATE_MODEL_BY_URL, parsed.url);
28 | });
29 | }
30 | }
31 |
32 | window.addEventListener("load", () => {
33 | // Expect `epi` to be there after the `load` event. If it's not then we're
34 | // not in any editing context.
35 | if (!window.epi) {
36 | return;
37 | }
38 |
39 | if (window.epi.ready === true) {
40 | setContext();
41 | } else if (window.epi.subscribe) {
42 | window.epi.subscribe("epiReady", () => setContext());
43 | }
44 | });
45 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | import EpiEdit from './directives/epiEdit';
4 | import router from './router';
5 | import store from './store';
6 | import './epiBootstrap';
7 | import './assets/styles/main.less';
8 |
9 | const app = createApp(App)
10 | .directive('epi-edit', EpiEdit)
11 | .use(store)
12 | .use(router);
13 |
14 | // Register all Optimizely view components globally. This requires webpack!
15 | // Otherwise we need to register all components manually here in main.js.
16 | const requireComponent = require.context('./views', true, /.vue$/);
17 |
18 | requireComponent.keys().forEach((fileName) => {
19 | const componentConfig = requireComponent(fileName);
20 |
21 | // Gets the component name regardless folder depth
22 | const componentName = fileName
23 | .split('/')
24 | .pop()
25 | .replace(/\.\w+$/, '');
26 |
27 | // Look for the component options on `.default`, which will
28 | // exist if the component was exported with `export default`,
29 | // otherwise fall back to module's root.
30 | app.component(componentName, componentConfig.default || componentConfig);
31 | });
32 |
33 | app.mount('#app');
34 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from "vue-router";
2 | import { ResolvedContentStatus } from "@episerver/content-delivery";
3 | import { UPDATE_PREVIEW_TOKEN } from "@/store/modules/epiContext";
4 | import { UPDATE_MODEL_BY_URL } from "@/store/modules/epiDataModel";
5 | import { parsePreviewToken } from "@/urlHelpers";
6 | import store from "@/store";
7 | import PageComponentSelector from "@/components/EpiPageComponentSelector.vue";
8 | import AccessDenied from "@/views/403.vue";
9 | import NotFound from "@/views/404.vue";
10 |
11 | const router = createRouter({
12 | history: createWebHistory(),
13 | routes: [
14 | {
15 | path: "/access-denied",
16 | component: AccessDenied,
17 | },
18 | {
19 | path: "/not-found",
20 | component: NotFound,
21 | },
22 | {
23 | name: "page-component-selector",
24 | path: "/:pathMatch(.*)",
25 | component: PageComponentSelector,
26 | },
27 | ],
28 | });
29 |
30 | router.beforeEach((to, from, next) => {
31 | // URL is updated by vue-route-sync, and when time travelling with the
32 | // debugger we don't want to trigger a model commit as the model is already
33 | // part of the store holding the url update.
34 | if (to.name === "page-component-selector" && store.state.epiDataModel.model.url !== to.fullPath) {
35 | var parsed = parsePreviewToken(to.fullPath);
36 | store.commit(UPDATE_PREVIEW_TOKEN, parsed.previewToken);
37 | store.dispatch(UPDATE_MODEL_BY_URL, parsed.url).then(() => {
38 | switch (store.state.epiDataModel.status) {
39 | case ResolvedContentStatus.NotFound:
40 | router.replace("/not-found");
41 | break;
42 | case ResolvedContentStatus.Unauthorized:
43 | router.replace("/access-denied");
44 | break;
45 | case ResolvedContentStatus.AccessDenied:
46 | router.replace("/access-denied");
47 | break;
48 | default:
49 | }
50 | });
51 | }
52 |
53 | next();
54 | });
55 |
56 | export default router;
57 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/store/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The main vuex store. This holds the state of the URL and makes sure that
3 | * when the URL is updated, the model gets updated too.
4 | */
5 |
6 | import { createStore } from 'vuex';
7 | import appContext from './modules/appContext'; // Module handling app specific state
8 | import epiContext from './modules/epiContext'; // Module handling Optimizely specific state
9 | import epiDataModel from './modules/epiDataModel'; // Module handling model state
10 |
11 | export default createStore({
12 | state: {
13 | },
14 | mutations: {
15 | },
16 | actions: {
17 | },
18 | modules: {
19 | appContext,
20 | epiContext,
21 | epiDataModel,
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/store/modules/appContext.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The module responsible for handling app-wide context state that is
3 | * interesting for several components that otherwise doesn't share state.
4 | */
5 |
6 | // Mutations for the appContext module
7 | export const SHOW_MODAL = 'appContext/SHOW_MODAL';
8 | export const HIDE_MODAL = 'appContext/HIDE_MODAL';
9 |
10 | const state = {
11 | modalShowing: false,
12 | };
13 |
14 | const mutations = {
15 | [SHOW_MODAL](state) {
16 | state.modalShowing = true;
17 | },
18 | [HIDE_MODAL](state) {
19 | state.modalShowing = false;
20 | },
21 | };
22 |
23 | export default {
24 | state,
25 | mutations,
26 | };
27 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/store/modules/epiContext.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The module responsible to handling Episerver specific state that is relevant
3 | * when editing content in edit mode.
4 | */
5 |
6 | // Mutation for the epiContext module
7 | export const UPDATE_CONTEXT = "epiContext/UPDATE_CONTEXT";
8 | export const UPDATE_PREVIEW_TOKEN = "epiContext/UPDATE_PREVIEW_TOKEN";
9 |
10 | const state = {
11 | inEditMode: false,
12 | isEditable: false,
13 | previewToken: undefined,
14 | };
15 |
16 | const mutations = {
17 | [UPDATE_CONTEXT](state, isEditable) {
18 | state.isEditable = isEditable;
19 | state.inEditMode = isEditable;
20 | },
21 | [UPDATE_PREVIEW_TOKEN](state, previewToken) {
22 | state.previewToken = previewToken;
23 | },
24 | };
25 |
26 | export default {
27 | state,
28 | mutations,
29 | };
30 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/store/modules/epiDataModel.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The module that is responsible for handling the state of the current content
3 | * that is being either viewed or edited. This module will handle talking to
4 | * the API when the model needs to be updated when navigating or editing the
5 | * site.
6 | */
7 |
8 | import { ContentResolver, ResolvedContentStatus, ContextMode } from "@episerver/content-delivery";
9 | import { UPDATE_CONTEXT } from "./epiContext";
10 |
11 | export const UPDATE_MODEL_BY_URL = "epiDataModel/UPDATE_MODEL_BY_URL";
12 |
13 | const UPDATE_MODEL = "epiDataModel/UPDATE_MODEL";
14 |
15 | const state = {
16 | model: {},
17 | modelLoaded: false,
18 | status: ResolvedContentStatus.Unknown,
19 | };
20 |
21 | const mutations = {
22 | [UPDATE_MODEL](state, payload) {
23 | state.model = payload.model || {};
24 | state.modelLoaded = payload.status === ResolvedContentStatus.Resolved;
25 | state.status = payload.status;
26 | },
27 | };
28 |
29 | const actions = {
30 | async [UPDATE_MODEL_BY_URL]({ commit }, url) {
31 | const contentResolver = new ContentResolver();
32 |
33 | return contentResolver
34 | .resolveContent(url, true)
35 | .then((resolvedContent) => {
36 | commit(UPDATE_MODEL, { model: resolvedContent.content, status: resolvedContent.status });
37 | commit(UPDATE_CONTEXT, resolvedContent.mode === ContextMode.Edit);
38 | })
39 | .catch(() => commit(UPDATE_MODEL, { status: ResolvedContentStatus.Unknown }));
40 | },
41 | };
42 |
43 | export default {
44 | state,
45 | mutations,
46 | actions,
47 | };
48 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/urlHelpers.js:
--------------------------------------------------------------------------------
1 | export function parsePreviewToken(url) {
2 | var absolute = new URL(url, "http://temp");
3 |
4 | const previewToken = absolute.searchParams.get("preview_token");
5 | absolute.searchParams.delete("preview_token");
6 |
7 | return {
8 | url: absolute.host === "temp" ? absolute.pathname + absolute.search : absolute.href,
9 | previewToken: previewToken,
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/403.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Access denied
4 |
5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio, rem nesciunt ad iure illum,
6 | repudiandae enim nostrum velit ipsum dignissimos molestiae architecto,
7 | officia nisi vel facilis quasi mollitia necessitatibus eum.
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Page not found
4 |
5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio, rem nesciunt ad iure illum,
6 | repudiandae enim nostrum velit ipsum dignissimos molestiae architecto,
7 | officia nisi vel facilis quasi mollitia necessitatibus eum.
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/BlockPreview.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
Full
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Wide
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Half
27 |
28 |
29 |
30 |
31 |
32 |
33 |
Narrow
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
57 |
58 |
76 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/blocks/BuyTicketBlock.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
{{heading || model.heading}}
13 |
14 |
15 | {{message || model.message}}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
36 |
37 |
46 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/blocks/ContentBlock.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
12 |
13 |
16 |
17 |
18 |
{{model.title}}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
46 |
47 |
50 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/blocks/GenericBlock.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
Could not load {{model.Name}} vue component.
10 |
11 |
12 |
13 |
14 |
15 |
20 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/media/ImageFile.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
13 |
14 |
15 |
25 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/src/views/pages/ArtistContainerPage.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
{{ model.name }}
15 |
16 |
17 |
18 |
{{ key }}
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
70 |
71 |
90 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/frontend/vue.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('@vue/cli-service');
2 | const path = require('path');
3 |
4 | module.exports = defineConfig({
5 | transpileDependencies: true,
6 | chainWebpack: (config) => {
7 | config.module
8 | .rule('less')
9 | .oneOf('normal')
10 | .use('import-glob-loader')
11 | .loader('import-glob-loader');
12 | },
13 | devServer: {
14 | setupMiddlewares: (middlewares, devServer) => {
15 | // Make all requesets go to index so friendly URLs
16 | // are working. But only if no static file is already being served.
17 | middlewares.push({
18 | name: 'serve-app',
19 | path: '*',
20 | middleware: (req, res) => {
21 | res.sendFile(path.join(__dirname, 'public/index.html'));
22 | },
23 | });
24 |
25 | return middlewares;
26 | },
27 | },
28 | });
29 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/samples/music-festival-vue-decoupled/setup.cmd:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | SETLOCAL
3 |
4 | SET BASE=.\backend\App_Data
5 |
6 | ECHO Removed all files from the App_Data folder
7 | IF EXIST %BASE%\blobs\ RMDIR %BASE%\blobs\ /S/Q || EXIT /B 1
8 | IF EXIST %BASE%\musicfestival.mdf DEL %BASE%\musicfestival.mdf /F/Q || EXIT /B 1
9 | IF EXIST %BASE%\musicfestival_log.ldf DEL %BASE%\musicfestival_log.ldf /F/Q || EXIT /B 1
10 |
11 | ECHO Created new database
12 | XCOPY %BASE%\db.mdf %BASE%\musicfestival.mdf* /Y/C || EXIT /B 1
13 |
14 | EXIT /B %ERRORLEVEL%
15 |
--------------------------------------------------------------------------------
/src/@episerver/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | charset = utf-8
3 | indent_style = space
4 | indent_size = 2
5 | end_of_line = crlf
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 | max_line_length = 200
9 |
--------------------------------------------------------------------------------
/src/@episerver/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | es6: true,
6 | },
7 | parserOptions: {
8 | ecmaVersion: 2017,
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/bin/cli.mjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { Command } from 'commander';
4 | import { pushManifest, pullManifest } from '../src/manifest.mjs';
5 |
6 | const program = new Command();
7 |
8 | program
9 | .version('1.0.0')
10 | .usage(' [options]');
11 |
12 | program
13 | .command('push ')
14 | .description('Push a manifest with content definitions to a management application from the specified path.')
15 | .requiredOption('-s, --source ', 'URL to the management application.')
16 | .requiredOption('--authority ', 'Login authority.')
17 | .requiredOption('--client-id ', 'Login client ID.')
18 | .requiredOption('--client-secret ', 'Login client secret.')
19 | .option('--allowed-upgrades [allowedUpgrades]', 'Which semantic upgrades of definitions should be allowed. Allowed values are "none", "patch", "minor", and "major".')
20 | .option('-f, --force [force]', 'Should the push proceed even though there are warnings or the changes are not allowed.')
21 | .action(async (path, cmd) => {
22 | const options = {
23 | allowedUpgrades: cmd.allowedUpgrades,
24 | force: cmd.force
25 | };
26 |
27 | const login = {
28 | authority: cmd.authority,
29 | clientId: cmd.clientId,
30 | clientSecret: cmd.clientSecret
31 | };
32 |
33 | pushManifest(path, cmd.source, options, login)
34 | .then(result => {
35 | result.forEach(message => {
36 | console.log(message.severity, message.message);
37 | });
38 | })
39 | .catch(error => console.error(error))
40 | });
41 |
42 | program
43 | .command('pull [path]')
44 | .description('Pull a manifest with content definitions from a management application to the specified path.')
45 | .requiredOption('-s, --source ', 'URL to the management application.')
46 | .requiredOption('--authority ', 'Login authority.')
47 | .requiredOption('--client-id ', 'Login client ID.')
48 | .requiredOption('--client-secret ', 'Login client secret.')
49 | .action(async (path, cmd) => {
50 | const login = {
51 | authority: cmd.authority,
52 | clientId: cmd.clientId,
53 | clientSecret: cmd.clientSecret
54 | };
55 |
56 | pullManifest(path, cmd.source, login)
57 | .then(result => console.log(result))
58 | .catch(error => console.error(error))
59 | });
60 |
61 | await program.parseAsync(process.argv);
62 |
63 | if (!process.argv.slice(2).length) {
64 | program.outputHelp();
65 | }
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@episerver/content-definitions",
3 | "version": "2.0.0",
4 | "description": "CLI tool for managing content definitions manifest files.",
5 | "author": "Johan Petersson ",
6 | "license": "Apache-2.0",
7 | "keywords": [
8 | "episerver",
9 | "optimizely",
10 | "manifest",
11 | "definitions"
12 | ],
13 | "homepage": "https://www.optimizely.com",
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/episerver/content-delivery-js-sdk.git"
17 | },
18 | "type": "module",
19 | "main": "./bin/cli.mjs",
20 | "bin": {
21 | "content-definitions": "./bin/cli.mjs"
22 | },
23 | "scripts": {
24 | "test": "mocha --recursive --timeout 5000 --require ./test/fixture.mjs"
25 | },
26 | "dependencies": {
27 | "commander": "^9.4.0",
28 | "openid-client": "^5.1.9"
29 | },
30 | "devDependencies": {
31 | "@types/chai": "^4.3.3",
32 | "@types/mocha": "^9.1.1",
33 | "chai": "^4.3.6",
34 | "concat-stream": "^2.0.0",
35 | "mocha": "^10.0.0",
36 | "test-setup": "file:../test-setup"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/src/authService.mjs:
--------------------------------------------------------------------------------
1 | import { isLocal } from './utils.mjs';
2 | import { Issuer, custom } from 'openid-client';
3 |
4 | export async function getAccessToken(login) {
5 | const url = new URL(login.authority);
6 |
7 | custom.setHttpOptionsDefaults({
8 | https: {
9 | rejectUnauthorized: !isLocal(url.hostname),
10 | },
11 | });
12 |
13 | return new Promise(async (resolve, reject) => {
14 | const issuer = await Issuer.discover(login.authority)
15 | .catch(error => reject(error));
16 |
17 | if (!issuer) {
18 | return resolve();
19 | }
20 |
21 | const client = new issuer.Client({
22 | client_id: login.clientId,
23 | client_secret: login.clientSecret,
24 | });
25 |
26 | const grant = await client.grant({
27 | grant_type: 'client_credentials',
28 | scope: 'epi_content_definitions',
29 | }).catch(error => reject(error));
30 |
31 | resolve(grant?.access_token);
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/src/utils.mjs:
--------------------------------------------------------------------------------
1 | export function isLocal(hostname = window.location.hostname) {
2 | return (
3 | ['localhost', '127.0.0.1', '', '::1'].includes(hostname) ||
4 | hostname.startsWith('192.168.') ||
5 | hostname.startsWith('10.0.') ||
6 | hostname.endsWith('.local')
7 | )
8 | }
9 |
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/test/fixture.mjs:
--------------------------------------------------------------------------------
1 | import * as backend from 'test-setup';
2 |
3 | export async function mochaGlobalSetup() {
4 | backend.removeTempDirectory();
5 | backend.createTempDirectory();
6 |
7 | await backend.start();
8 | };
9 |
10 | export async function mochaGlobalTeardown() {
11 | backend.removeTempDirectory();
12 |
13 | await backend.stop();
14 | };
15 |
16 |
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/test/manifests/empty-json.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/test/manifests/major-downgrade.json:
--------------------------------------------------------------------------------
1 | {
2 | "contentTypes": [
3 | {
4 | "id": "ac0a6c4f-56ab-4596-9f5c-cfa923875c18",
5 | "name": "NewPage",
6 | "baseType": "Page",
7 | "version": "1.0.0"
8 | }
9 | ]
10 | }
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/test/manifests/major-update.json:
--------------------------------------------------------------------------------
1 | {
2 | "contentTypes": [
3 | {
4 | "id": "ac0a6c4f-56ab-4596-9f5c-cfa923875c18",
5 | "name": "NewPageName",
6 | "baseType": "Page",
7 | "version": "2.0.0",
8 | "properties": [
9 | {
10 | "name": "Heading",
11 | "dataType": "PropertyLongString"
12 | }
13 | ]
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/test/manifests/new.json:
--------------------------------------------------------------------------------
1 | {
2 | "contentTypes": [
3 | {
4 | "id": "ac0a6c4f-56ab-4596-9f5c-cfa923875c18",
5 | "name": "NewPage",
6 | "baseType": "Page",
7 | "properties": [
8 | {
9 | "name": "Heading",
10 | "dataType": "PropertyLongString"
11 | }
12 | ]
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/src/@episerver/content-definitions/test/manifests/no-json.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/src/@episerver/content-definitions/test/manifests/no-json.json
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/apiClient.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"apiClient.d.mts","sourceRoot":"","sources":["../src/apiClient.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;IAEX;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B;;OAEG;IACH,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACxD;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;MAEE;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;MAEE;IACF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAW,SAAQ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACrD;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,qBAAa,SAAS;;IAGpB;;;;OAIG;gBACS,MAAM,EAAE,qBAAqB;IAIzC;;;;;;;OAOG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,aAAkB,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAkCvG;;;;;;OAMG;IACH,oBAAoB,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,aAAa;IAOnF;;;;;OAKG;IACH,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU;CAK/C"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/config.d.mts:
--------------------------------------------------------------------------------
1 | /**
2 | * Type describing configuration to use when making
3 | * requests to the Content Delivery API.
4 | */
5 | export declare type ContentDeliveryConfig = {
6 | /**
7 | * URL to the Content Delivery API.
8 | */
9 | apiUrl: string;
10 | /**
11 | * Function to call to get an access token for authorizing
12 | * requests to the Content Delivery API.
13 | */
14 | getAccessToken?: (path?: string) => Promise;
15 | /**
16 | * Function to call to include custom headers in
17 | * requests to the Content Delivery API.
18 | */
19 | getHeaders?: (path?: string) => Promise>;
20 | /**
21 | * Function to call to change the URL before a
22 | * requests to the Content Delivery API.
23 | */
24 | getUrl?: (url: string) => Promise;
25 | /**
26 | * Select all properties by default, unless otherwise
27 | * specified in each request to the Content Delivery API.
28 | */
29 | selectAllProperties: boolean;
30 | /**
31 | * Expand all properties by default, unless otherwise
32 | * specified in each request to the Content Delivery API.
33 | */
34 | expandAllProperties: boolean;
35 | };
36 | /**
37 | * Default configuration to use when making requests to the
38 | * Content Delivery API.
39 | */
40 | export declare const defaultConfig: ContentDeliveryConfig;
41 | //# sourceMappingURL=config.d.mts.map
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/config.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"config.d.mts","sourceRoot":"","sources":["../src/config.mts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,oBAAY,qBAAqB,GAAG;IAClC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAE7D;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1C;;;OAGG;IACH,mBAAmB,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,qBAI3B,CAAA"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/config.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Default configuration to use when making requests to the
3 | * Content Delivery API.
4 | */
5 | export const defaultConfig = {
6 | apiUrl: '/',
7 | selectAllProperties: true,
8 | expandAllProperties: false,
9 | };
10 | //# sourceMappingURL=config.mjs.map
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/config.mjs.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"config.mjs","sourceRoot":"","sources":["../src/config.mts"],"names":[],"mappings":"AAyCA;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAA0B;IAClD,MAAM,EAAE,GAAG;IACX,mBAAmB,EAAE,IAAI;IACzB,mBAAmB,EAAE,KAAK;CAC3B,CAAA"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/contentLoader.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"contentLoader.d.mts","sourceRoot":"","sources":["../src/contentLoader.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAiB,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEvB;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,oBAAY,kBAAkB,GAAG;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAA;AAED;;;;GAIG;AACH,oBAAY,iBAAiB,CAAC,CAAC,SAAS,WAAW,IAAI;IACrD;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAA;AAED;;GAEG;AACH,qBAAa,aAAa;;IAGxB;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAInD;;;;;;OAMG;IACH,UAAU,CAAC,CAAC,SAAS,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAiBnF;;;;;;;OAOG;IACH,WAAW,CAAC,CAAC,SAAS,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAqC5H;;;;;;OAMG;IACH,YAAY,CAAC,CAAC,SAAS,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAgB7F"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/contentResolver.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"contentResolver.d.mts","sourceRoot":"","sources":["../src/contentResolver.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAiB,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEvB;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,oBAAY,qBAAqB;IAC/B;;;OAGG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,QAAQ,aAAa;IAErB;;OAEG;IACH,QAAQ,aAAa;IAErB;;;OAGG;IACH,YAAY,iBAAiB;IAE7B;;;OAGG;IACH,YAAY,iBAAiB;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,WAAW;IACpD;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,CAAC;IAEZ;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,qBAAqB,CAAA;IAE7B;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,oBAAY,oBAAoB,GAAG;IACjC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAA;AAED;;GAEG;AACH,qBAAa,eAAe;;IAG1B;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAInD;;;;;;;;;OASG;IACH,cAAc,CAAC,CAAC,SAAS,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CA4DtI"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/contentResolver.mjs.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"contentResolver.mjs","sourceRoot":"","sources":["../src/contentResolver.mts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAyB,aAAa,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAe,WAAW,EAAE,MAAM,cAAc,CAAC;AAoBxD;;GAEG;AACH,MAAM,CAAN,IAAY,qBA4BX;AA5BD,WAAY,qBAAqB;IAC/B;;;OAGG;IACH,4CAAmB,CAAA;IAEnB;;OAEG;IACH,8CAAqB,CAAA;IAErB;;OAEG;IACH,8CAAqB,CAAA;IAErB;;;OAGG;IACH,sDAA6B,CAAA;IAE7B;;;OAGG;IACH,sDAA6B,CAAA;AAC/B,CAAC,EA5BW,qBAAqB,KAArB,qBAAqB,QA4BhC;AAuDD;;GAEG;AACH,MAAM,OAAO,eAAe;IAG1B;;;;;OAKG;IACH,YAAY,MAAuC;QARnD,uCAAyB;QASvB,uBAAA,IAAI,wBAAQ,IAAI,SAAS,iCAAM,aAAa,GAAK,MAAM,EAAG,MAAA,CAAC;IAC7D,CAAC;IAED;;;;;;;;;OASG;IACH,cAAc,CAAwB,GAAW,EAAE,UAAmB,EAAE,OAA+B;QACrG,MAAM,UAAU,mBACd,YAAY,EAAE,GAAG,EACjB,YAAY,EAAE,UAAU,IACpB,uBAAA,IAAI,4BAAK,CAAC,oBAAoB,CAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,CAAC,CACrE,CAAC;QAEF,OAAO,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACzD,uBAAA,IAAI,4BAAK,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,QAAqB,EAAE,EAAE;gBAClE,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAgB,CAAC;gBAC9C,IAAI,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC;gBAC3C,IAAI,OAAsB,CAAC;gBAE3B,QAAQ,QAAQ,CAAC,MAAM,EAAE;oBACvB,KAAK,GAAG;wBACN,MAAM,GAAG,qBAAqB,CAAC,YAAY,CAAC;wBAC5C,MAAM;oBACR,KAAK,GAAG;wBACN,MAAM,GAAG,qBAAqB,CAAC,YAAY,CAAC;wBAC5C,MAAM;oBACR,KAAK,GAAG;wBACN,MAAM,GAAG,qBAAqB,CAAC,QAAQ,CAAC;wBACxC,MAAM;oBACR;wBACE,IAAI,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,IAAG,CAAC,EAAE;4BAC3B,MAAM,GAAG,qBAAqB,CAAC,QAAQ,CAAC;4BACxC,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;yBAC1B;6BAAM;4BACL,MAAM,GAAG,qBAAqB,CAAC,QAAQ,CAAC;yBACzC;wBACD,MAAM;iBACT;gBAAA,CAAC;gBAEF,SAAS,gBAAgB,CAAC,KAAc;oBACtC,QAAQ,KAAK,EAAE;wBACb,KAAK,MAAM;4BACT,OAAO,WAAW,CAAC,IAAI,CAAC;wBAC1B,KAAK,SAAS;4BACZ,OAAO,WAAW,CAAC,OAAO,CAAC;wBAC7B;4BACE,OAAO,WAAW,CAAC,OAAO,CAAC;qBAC9B;gBACH,CAAC;gBAED,MAAM,MAAM,GAAuB;oBACjC,OAAO,EAAE,OAAO;oBAChB,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;oBAC5C,MAAM,EAAE,MAAM;oBACd,IAAI,EAAE,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;oBACjE,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;oBAC3D,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;oBAC5C,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;iBACzD,CAAC;gBAEF,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAe,EAAE,EAAE;gBAC3B,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;;AAED,SAAS,UAAU,CAAC,KAAe;IACjC,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,UAAU;KAC/B,CAAA;AACH,CAAC"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/main.d.mts:
--------------------------------------------------------------------------------
1 | export * from './config.mjs';
2 | export * from './contentLoader.mjs';
3 | export * from './contentResolver.mjs';
4 | export * from './siteLoader.mjs';
5 | export * from './models.mjs';
6 | //# sourceMappingURL=main.d.mts.map
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/main.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.d.mts","sourceRoot":"","sources":["../src/main.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/main.mjs:
--------------------------------------------------------------------------------
1 | export * from './config.mjs';
2 | export * from './contentLoader.mjs';
3 | export * from './contentResolver.mjs';
4 | export * from './siteLoader.mjs';
5 | export * from './models.mjs';
6 | //# sourceMappingURL=main.mjs.map
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/main.mjs.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.mjs","sourceRoot":"","sources":["../src/main.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/models.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"models.d.mts","sourceRoot":"","sources":["../src/models.mts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,oBAAY,WAAW;IACrB;;OAEG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,IAAI,SAAS;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;IAEzB;;OAEG;IACH,UAAU,EAAE,WAAW,CAAC;IAExB;;OAEG;IACH,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAE3B;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAC;IAEnB;;OAEG;IACH,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEnC;;OAEG;IACH,OAAO,EAAE,IAAI,CAAC;IAEd;;OAEG;IACH,OAAO,EAAE,IAAI,CAAC;IAEd;;OAEG;IACH,KAAK,EAAE,IAAI,CAAC;IAEZ;;OAEG;IACH,YAAY,EAAE,IAAI,CAAC;IAEnB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,QAAS,SAAQ,WAAW;IAC3C;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;CAC3B;AAED;;GAEG;AACF,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAE1C;;OAEG;IACH,SAAS,EAAE,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAEzC;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,QAAQ;IACtD;;OAEG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACF,UAAU,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACF,GAAG,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,QAAQ,CAAC;CACpB"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/models.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * Enum describing in which mode the Content Delivery API
3 | * is being used.
4 | */
5 | export var ContextMode;
6 | (function (ContextMode) {
7 | /**
8 | * Default mode, also known as view mode.
9 | */
10 | ContextMode["Default"] = "DEFAULT";
11 | /**
12 | * Preview mode in the CMS shell.
13 | */
14 | ContextMode["Preview"] = "PREVIEW";
15 | /**
16 | * Edit mode in the CMS shell.
17 | */
18 | ContextMode["Edit"] = "EDIT";
19 | })(ContextMode || (ContextMode = {}));
20 | //# sourceMappingURL=models.mjs.map
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/models.mjs.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"models.mjs","sourceRoot":"","sources":["../src/models.mts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,WAeX;AAfD,WAAY,WAAW;IACrB;;OAEG;IACH,kCAAmB,CAAA;IAEnB;;OAEG;IACH,kCAAmB,CAAA;IAEnB;;OAEG;IACH,4BAAa,CAAA;AACf,CAAC,EAfW,WAAW,KAAX,WAAW,QAetB"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/siteLoader.d.mts:
--------------------------------------------------------------------------------
1 | import { ContentDeliveryConfig } from './config.mjs';
2 | import { SiteDefinition } from './models.mjs';
3 | /**
4 | * Type describing a site loader error.
5 | *
6 | * @typeparam T - Type of the additional error data.
7 | */
8 | export declare type SiteLoaderError = {
9 | /**
10 | * HTTP status code.
11 | */
12 | errorCode?: number;
13 | /**
14 | * Message describing the error.
15 | */
16 | errorMessage?: string;
17 | };
18 | /**
19 | * Class for loading sites.
20 | */
21 | export declare class SiteLoader {
22 | #private;
23 | /**
24 | * Constructs an instance of SiteLoader.
25 | *
26 | * @param config - Optional configuration to use. The configuration is
27 | * combined with the default configuration specified in defaultConfig.
28 | */
29 | constructor(config?: Partial);
30 | /**
31 | * Get site by an identifier.
32 | *
33 | * @param id - Identifier of the site.
34 | * @returns A promise with a SiteDefinition if the site was found, otherwise rejected with a SiteLoaderError.
35 | */
36 | getSite(id: string): Promise;
37 | /**
38 | * List all sites.
39 | *
40 | * @returns A promise with an array of SiteDefinition. Otherwise rejected with a SiteLoaderError.
41 | */
42 | getSites(): Promise>;
43 | }
44 | //# sourceMappingURL=siteLoader.d.mts.map
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/siteLoader.d.mts.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"siteLoader.d.mts","sourceRoot":"","sources":["../src/siteLoader.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAiB,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;GAIG;AACH,oBAAY,eAAe,GAAG;IAC5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAA;AAED;;GAEG;AACH,qBAAa,UAAU;;IAGrB;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAInD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAc5C;;;;KAIC;IACD,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;CAa3C"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/lib/siteLoader.mjs.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"siteLoader.mjs","sourceRoot":"","sources":["../src/siteLoader.mts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAyB,aAAa,EAAE,MAAM,cAAc,CAAC;AAoBpE;;GAEG;AACH,MAAM,OAAO,UAAU;IAGrB;;;;;OAKG;IACH,YAAY,MAAuC;QARnD,kCAAyB;QASvB,uBAAA,IAAI,mBAAQ,IAAI,SAAS,iCAAM,aAAa,GAAK,MAAM,EAAG,MAAA,CAAC;IAC7D,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,EAAU;QAChB,OAAO,IAAI,OAAO,CAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrD,uBAAA,IAAI,uBAAK,CAAC,GAAG,CAAC,QAAQ,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAqB,EAAE,EAAE;gBAC7E,IAAI,QAAQ,CAAC,EAAE,EAAE;oBACf,OAAO,CAAC,QAAQ,CAAC,IAAsB,CAAC,CAAC;iBAC1C;qBAAM;oBACL,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;iBACtC;YACH,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAe,EAAE,EAAE;gBAC3B,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;KAIC;IACD,QAAQ;QACN,OAAO,IAAI,OAAO,CAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC5D,uBAAA,IAAI,uBAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,QAAqB,EAAE,EAAE;gBACpD,IAAI,QAAQ,CAAC,EAAE,EAAE;oBACf,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;iBACxB;qBAAM;oBACL,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;iBACtC;YACH,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAe,EAAE,EAAE;gBAC3B,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;;AAED,SAAS,kBAAkB,CAAC,QAAqB;IAC/C,OAAO;QACL,SAAS,EAAE,QAAQ,CAAC,MAAM;QAC1B,YAAY,EAAE,QAAQ,CAAC,UAAU;KAClC,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAe;IACjC,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,UAAU;KAC/B,CAAA;AACH,CAAC"}
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@episerver/content-delivery",
3 | "version": "2.0.0",
4 | "description": "SDK for the Episerver Content Delivery API.",
5 | "author": "Johan Petersson ",
6 | "license": "Apache-2.0",
7 | "keywords": [
8 | "episerver",
9 | "optimizely",
10 | "content delivery",
11 | "headless",
12 | "sdk",
13 | "client"
14 | ],
15 | "homepage": "https://www.optimizely.com",
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/episerver/content-delivery-js-sdk.git"
19 | },
20 | "type": "module",
21 | "main": "./lib/main.mjs",
22 | "types": "./lib/main.d.mts",
23 | "scripts": {
24 | "build": "npx tsc",
25 | "watch": "npx tsc --watch",
26 | "test": "mocha --recursive --timeout 5000 --require ./test/fixture.mjs"
27 | },
28 | "devDependencies": {
29 | "@types/chai": "^4.3.3",
30 | "@types/mocha": "^9.1.1",
31 | "@types/node": "^14.14.25",
32 | "chai": "^4.3.6",
33 | "mocha": "^10.0.0",
34 | "test-setup": "file:../test-setup",
35 | "typescript": "^4.1.4"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/src/config.mts:
--------------------------------------------------------------------------------
1 | /**
2 | * Type describing configuration to use when making
3 | * requests to the Content Delivery API.
4 | */
5 | export type ContentDeliveryConfig = {
6 | /**
7 | * URL to the Content Delivery API.
8 | */
9 | apiUrl: string;
10 |
11 | /**
12 | * Function to call to get an access token for authorizing
13 | * requests to the Content Delivery API.
14 | */
15 | getAccessToken?: (path?: string) => Promise,
16 |
17 | /**
18 | * Function to call to include custom headers in
19 | * requests to the Content Delivery API.
20 | */
21 | getHeaders?: (path?: string) => Promise>,
22 |
23 | /**
24 | * Function to call to change the URL before a
25 | * requests to the Content Delivery API.
26 | */
27 | getUrl?: (url: string) => Promise,
28 |
29 | /**
30 | * Select all properties by default, unless otherwise
31 | * specified in each request to the Content Delivery API.
32 | */
33 | selectAllProperties: boolean,
34 |
35 | /**
36 | * Expand all properties by default, unless otherwise
37 | * specified in each request to the Content Delivery API.
38 | */
39 | expandAllProperties: boolean,
40 | };
41 |
42 | /**
43 | * Default configuration to use when making requests to the
44 | * Content Delivery API.
45 | */
46 | export const defaultConfig: ContentDeliveryConfig = {
47 | apiUrl: '/',
48 | selectAllProperties: true,
49 | expandAllProperties: false,
50 | }
51 |
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/src/main.mts:
--------------------------------------------------------------------------------
1 | export * from './config.mjs';
2 | export * from './contentLoader.mjs';
3 | export * from './contentResolver.mjs';
4 | export * from './siteLoader.mjs';
5 | export * from './models.mjs';
6 |
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/src/siteLoader.mts:
--------------------------------------------------------------------------------
1 | import { ApiClient, ApiResponse, ApiError } from './apiClient.mjs';
2 | import { ContentDeliveryConfig, defaultConfig } from './config.mjs';
3 | import { SiteDefinition } from './models.mjs';
4 |
5 | /**
6 | * Type describing a site loader error.
7 | *
8 | * @typeparam T - Type of the additional error data.
9 | */
10 | export type SiteLoaderError = {
11 | /**
12 | * HTTP status code.
13 | */
14 | errorCode?: number,
15 |
16 | /**
17 | * Message describing the error.
18 | */
19 | errorMessage?: string,
20 | }
21 |
22 | /**
23 | * Class for loading sites.
24 | */
25 | export class SiteLoader {
26 | readonly #api: ApiClient;
27 |
28 | /**
29 | * Constructs an instance of SiteLoader.
30 | *
31 | * @param config - Optional configuration to use. The configuration is
32 | * combined with the default configuration specified in defaultConfig.
33 | */
34 | constructor(config?: Partial) {
35 | this.#api = new ApiClient({ ...defaultConfig, ...config });
36 | }
37 |
38 | /**
39 | * Get site by an identifier.
40 | *
41 | * @param id - Identifier of the site.
42 | * @returns A promise with a SiteDefinition if the site was found, otherwise rejected with a SiteLoaderError.
43 | */
44 | getSite(id: string): Promise {
45 | return new Promise((resolve, reject) => {
46 | this.#api.get(`site/${encodeURIComponent(id)}`).then((response: ApiResponse) => {
47 | if (response.ok) {
48 | resolve(response.data as SiteDefinition);
49 | } else {
50 | reject(mapResponseToError(response));
51 | }
52 | }).catch((error: ApiError) => {
53 | reject(mapToError(error));
54 | });
55 | });
56 | }
57 |
58 | /**
59 | * List all sites.
60 | *
61 | * @returns A promise with an array of SiteDefinition. Otherwise rejected with a SiteLoaderError.
62 | */
63 | getSites(): Promise> {
64 | return new Promise>((resolve, reject) => {
65 | this.#api.get(`site/`).then((response: ApiResponse) => {
66 | if (response.ok) {
67 | resolve(response.data);
68 | } else {
69 | reject(mapResponseToError(response));
70 | }
71 | }).catch((error: ApiError) => {
72 | reject(mapToError(error));
73 | });
74 | });
75 | }
76 | }
77 |
78 | function mapResponseToError(response: ApiResponse): SiteLoaderError {
79 | return {
80 | errorCode: response.status,
81 | errorMessage: response.statusText,
82 | }
83 | }
84 |
85 | function mapToError(error: ApiError): SiteLoaderError {
86 | return {
87 | errorMessage: error.statusText,
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/test/fixture.mjs:
--------------------------------------------------------------------------------
1 | import * as backend from 'test-setup';
2 | import { defaultConfig } from "../lib/main.mjs";
3 |
4 | defaultConfig.apiUrl = backend.apiUrl;
5 |
6 | export async function mochaGlobalSetup() {
7 | await backend.start();
8 | };
9 |
10 | export async function mochaGlobalTeardown() {
11 | await backend.stop();
12 | };
--------------------------------------------------------------------------------
/src/@episerver/content-delivery/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "module": "ES6",
5 | "moduleResolution": "NodeNext",
6 | "esModuleInterop": false,
7 | "noEmitOnError": true,
8 | "noImplicitAny": true,
9 | "sourceMap": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "declaration": true,
13 | "declarationMap": true,
14 | "outDir": "lib/",
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/Backend.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | Backend
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/CreateTestContentFirstRequestInitializer.cs:
--------------------------------------------------------------------------------
1 | using EPiServer.Web;
2 | using EPiServer;
3 | using EPiServer.DataAnnotations;
4 | using EPiServer.Core;
5 | using EPiServer.DataAccess;
6 | using EPiServer.Security;
7 | using EPiServer.DataAbstraction;
8 |
9 | namespace Backend;
10 |
11 | public class CreateTestContentFirstRequestInitializer : IBlockingFirstRequestInitializer
12 | {
13 | private IContentSecurityRepository? _contentSecurityRepository;
14 | private IContentRepository? _contentRepository;
15 |
16 | public bool CanRunInParallel => false;
17 |
18 | public Task InitializeAsync(HttpContext httpContext)
19 | {
20 | _contentSecurityRepository = httpContext.RequestServices.GetService();
21 | _contentRepository = httpContext.RequestServices.GetService();
22 |
23 | var page = _contentRepository!.GetDefault(ContentReference.StartPage);
24 | page.Name = "Protected";
25 |
26 | var contentReference = _contentRepository.Save(page, SaveAction.Publish, AccessLevel.NoAccess);
27 |
28 | var permissions = (IContentSecurityDescriptor)_contentSecurityRepository!.Get(contentReference).CreateWritableClone();
29 | permissions.ToLocal(false);
30 | permissions.AddEntry(new AccessControlEntry("bob", AccessLevel.FullAccess, SecurityEntityType.User));
31 |
32 | _contentSecurityRepository.Save(contentReference, permissions, SecuritySaveType.Replace);
33 |
34 | return Task.CompletedTask;
35 | }
36 | }
37 |
38 | [ContentType]
39 | public class TestPage : PageData
40 | { }
41 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/CustomHeaderContentApiFilter.cs:
--------------------------------------------------------------------------------
1 | using EPiServer.ContentApi.Core.Serialization;
2 | using EPiServer.ContentApi.Core.Serialization.Internal;
3 | using EPiServer.ContentApi.Core.Serialization.Models;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.Primitives;
6 |
7 | namespace Backend
8 | {
9 | public class CustomHeaderContentApiFilter : ContentApiModelFilter
10 | {
11 | private readonly IHttpContextAccessor _httpContextAccessor;
12 |
13 | public CustomHeaderContentApiFilter(IHttpContextAccessor httpContextAccessor)
14 | {
15 | _httpContextAccessor = httpContextAccessor;
16 | }
17 |
18 | public override void Filter(ContentApiModel contentApiModel, ConverterContext converterContext)
19 | {
20 | if (_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("CustomHeaderName"))
21 | {
22 | contentApiModel.Properties.Add("customHeader", _httpContextAccessor.HttpContext.Request.Headers["CustomHeaderName"][0]);
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/Program.cs:
--------------------------------------------------------------------------------
1 | namespace Backend;
2 |
3 | public class Program
4 | {
5 | public static void Main(string[] args)
6 | {
7 | DatabaseHelper.Ensure(Startup.ConnectionString);
8 |
9 | CreateHostBuilder(args).Build().Run();
10 | }
11 |
12 | public static IHostBuilder CreateHostBuilder(string[] args) =>
13 | Host.CreateDefaultBuilder(args)
14 | .ConfigureCmsDefaults()
15 | .ConfigureWebHostDefaults(webBuilder =>
16 | {
17 | webBuilder.UseStartup();
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/UsernameAuthenticationHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using EPiServer.Cms.UI.AspNetIdentity;
3 | using Microsoft.AspNetCore.Authentication;
4 |
5 | namespace Backend;
6 |
7 | ///
8 | /// A workaround to pass a username in the bearer token directly.
9 | /// This makes testing easier as we don't have to obtain a real
10 | /// access token first.
11 | ///
12 | public class UsernameAuthenticationHandler : IAuthenticationHandler
13 | {
14 | public const string SchemeName = "UsernameAuthentication";
15 | public const string DisplayName = "Username Authentication";
16 |
17 | private ClaimsPrincipal? _principal;
18 |
19 | public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
20 | {
21 | var bearer = context.Request.Headers["Authorization"];
22 |
23 | if (string.IsNullOrEmpty(bearer))
24 | {
25 | return;
26 | }
27 |
28 | var username = bearer.ToString().Replace("Bearer ", string.Empty);
29 | var signInManager = context.RequestServices.GetService>();
30 | var user = await signInManager!.UserManager.FindByNameAsync(username);
31 |
32 | if (user is not null)
33 | {
34 | _principal = await signInManager.CreateUserPrincipalAsync(user);
35 | }
36 | }
37 |
38 | public Task AuthenticateAsync()
39 | {
40 | if (_principal is not null)
41 | {
42 | return Task.FromResult(AuthenticateResult.Success(
43 | new AuthenticationTicket(_principal, SchemeName)));
44 | }
45 | else
46 | {
47 | return Task.FromResult(AuthenticateResult.NoResult());
48 | }
49 | }
50 |
51 | public Task ChallengeAsync(AuthenticationProperties? properties) => Task.CompletedTask;
52 |
53 | public Task ForbidAsync(AuthenticationProperties? properties) => Task.CompletedTask;
54 | }
55 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "None",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/backend/properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "MusicFestival.Backend": {
4 | "commandName": "Project",
5 | "dotnetRunMessages": "true",
6 | "launchBrowser": false,
7 | "applicationUrl": "http://localhost:8080",
8 | "environmentVariables": {
9 | "ASPNETCORE_ENVIRONMENT": "Development"
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/data/DefaultSiteContent.episerverdata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/src/@episerver/test-setup/data/DefaultSiteContent.episerverdata
--------------------------------------------------------------------------------
/src/@episerver/test-setup/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-setup",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "test-setup",
9 | "version": "1.0.0"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-setup",
3 | "version": "1.0.0",
4 | "private": true,
5 | "type": "module",
6 | "main": "./src/index.mjs",
7 | "scripts": {
8 | "backend": "node ./src/index.mjs"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/@episerver/test-setup/src/index.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import http from 'http';
4 | import { spawn } from 'child_process';
5 |
6 | export const baseUrl = 'http://localhost:8080';
7 | export const apiUrl = `${baseUrl}/api/episerver/v3.0/`;
8 | export const tempPath = './temp';
9 |
10 | const basePath = '../test-setup/';
11 | const appDataPath = path.join(basePath, 'backend/App_Data/');
12 |
13 | let dotnet;
14 |
15 | export async function start() {
16 | cleanUpData();
17 | newData();
18 |
19 | return new Promise((resolve, reject) => {
20 | dotnet = spawn('dotnet', [ 'run', '--project', path.join(basePath, 'backend')], { stdio: 'inherit', })
21 | .on('error', (error) => reject(error))
22 | .on('exit', (code) => process.exit(code));
23 |
24 | waitForResponse()
25 | .then(resolve)
26 | .catch(reject);
27 | })
28 | }
29 |
30 | export async function stop() {
31 | dotnet.kill('SIGINT');
32 |
33 | cleanUpData();
34 |
35 | return Promise.resolve();
36 | }
37 |
38 | async function waitForResponse() {
39 | console.log('Waiting for server...')
40 |
41 | let started = false;
42 |
43 | while (!started) {
44 | await new Promise(resolve => setTimeout(resolve, 5000));
45 |
46 | http.get(baseUrl, () => {
47 | started = true;
48 | }).on('error', () => {});
49 |
50 | console.log('Still waiting for server...')
51 | }
52 |
53 | console.log('Server ready!')
54 |
55 | return Promise.resolve();
56 | }
57 |
58 | export function createTempDirectory() {
59 | console.log(`Creating temporary directory '${tempPath}'.`)
60 |
61 | fs.mkdirSync(tempPath);
62 | }
63 |
64 | export function removeTempDirectory() {
65 | console.log(`Removing temporary directory '${tempPath}'.`)
66 |
67 | fs.rmSync(tempPath, { recursive: true, force: true });
68 | }
69 |
70 | function cleanUpData() {
71 | console.log(`Removing directory '${appDataPath}'.`)
72 |
73 | fs.rmSync(appDataPath, { recursive: true, force: true });
74 | }
75 |
76 | function newData() {
77 | console.log(`Creating directory '${appDataPath}'.`)
78 |
79 | fs.mkdirSync(appDataPath);
80 |
81 | fs.copyFileSync(
82 | path.join(basePath, '/data/DefaultSiteContent.episerverdata'),
83 | path.join(appDataPath, 'DefaultSiteContent.episerverdata'));
84 | }
85 |
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/DependencyInjection/NodeJsEndpointRouteBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using EPiServer.ContentDelivery.NodeProxy;
2 | using Microsoft.AspNetCore.Routing;
3 | using Microsoft.Extensions.DependencyInjection;
4 |
5 | namespace Microsoft.AspNetCore.Builder;
6 |
7 | ///
8 | /// Extensions for .
9 | ///
10 | public static class NodeJsEndpointRouteBuilderExtensions
11 | {
12 | ///
13 | /// Adds an endpoint that proxies incoming requests to a Node.js based
14 | /// webserver running on the same machine, which is fully controlled by
15 | /// this application scope
16 | ///
17 | /// The to add the route to.
18 | /// An that can be used to further customize the endpoint.
19 | public static IEndpointRouteBuilder MapNodeJs(this IEndpointRouteBuilder endpoints)
20 | {
21 | endpoints.MapFallback("{*path}", async context =>
22 | {
23 | var process = endpoints.ServiceProvider.GetRequiredService();
24 | var ready = await process.EnsureProcessStarted();
25 |
26 | if (ready)
27 | {
28 | var forwarder = endpoints.ServiceProvider.GetRequiredService();
29 | await forwarder.ProxyRequest(context);
30 | }
31 | }).WithDisplayName("Node.js proxy");
32 |
33 | return endpoints;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/DependencyInjection/NodeJsServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Headers;
2 | using EPiServer.ContentDelivery.NodeProxy;
3 |
4 | namespace Microsoft.Extensions.DependencyInjection;
5 |
6 | ///
7 | /// Extensions for .
8 | ///
9 | public static class NodeJsServiceCollectionExtensions
10 | {
11 | ///
12 | /// Registers and configures services for the Node.js proxy.
13 | ///
14 | /// The to add services to.
15 | /// Registers an action used to configure .
16 | public static IServiceCollection AddNodeJs(
17 | this IServiceCollection services,
18 | Action? configureOptions = null)
19 | {
20 | var optionsBuilder = services.AddOptions();
21 | if (configureOptions is not null)
22 | {
23 | optionsBuilder.Configure(configureOptions);
24 | }
25 |
26 | services.AddHttpClient(NodeJsProcess.ClientName, options =>
27 | {
28 | options.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*", 0.1));
29 | });
30 |
31 | services
32 | .AddHttpForwarder()
33 | .AddSingleton()
34 | .AddSingleton();
35 |
36 | return services;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/EPiServer.ContentDelivery.NodeProxy.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0.1.0
5 |
6 | net6.0
7 | 10
8 | enable
9 | enable
10 | true
11 | embedded
12 | false
13 | true
14 | true
15 |
16 | Episerver Content Delivery Node.js proxy
17 | Optimizely Content Cloud Node.js proxy that proxies incoming requests to a Node.js based webserver running on the same machine, which is fully controlled by the dotnet process.
18 | Episerver AB
19 | Episerver AB
20 | © 2003-2022 by Episerver AB. All rights reserved
21 | https://github.com/episerver/content-delivery-js-sdk
22 |
23 | https://github.com/episerver/content-delivery-js-sdk
24 | ..\..\artifacts\
25 | Episerver, Content Delivery
26 | icon.png
27 | Apache-2.0
28 |
29 |
30 |
31 | true
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/NodeJsForwarder.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System.Diagnostics;
3 | using System.Net;
4 | using Yarp.ReverseProxy.Forwarder;
5 |
6 | namespace EPiServer.ContentDelivery.NodeProxy;
7 |
8 | ///
9 | /// This class acts as a simple wrapper around the from Yarp.
10 | ///
11 | internal class NodeJsForwarder
12 | {
13 | private readonly IHttpForwarder _forwarder;
14 | private readonly NodeJsOptions _options;
15 | private readonly HttpMessageInvoker _httpClient;
16 | private readonly ForwarderRequestConfig _requestConfig;
17 |
18 | public NodeJsForwarder(
19 | IHttpForwarder forwarder,
20 | NodeJsOptions options)
21 | {
22 | _forwarder = forwarder;
23 | _options = options;
24 |
25 | _httpClient = new HttpMessageInvoker(new SocketsHttpHandler()
26 | {
27 | UseProxy = false,
28 | AllowAutoRedirect = false,
29 | AutomaticDecompression = DecompressionMethods.None,
30 | UseCookies = false,
31 | ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current)
32 | });
33 |
34 | _requestConfig = new ForwarderRequestConfig
35 | {
36 | ActivityTimeout = TimeSpan.FromSeconds(_options.ProxyTimeout)
37 | };
38 | }
39 |
40 | public virtual ValueTask ProxyRequest(HttpContext context)
41 | => _forwarder.SendAsync(context, $"http://localhost:{_options.DestinationPort}", _httpClient, _requestConfig, HttpTransformer.Default);
42 | }
43 |
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/NodeJsOptions.cs:
--------------------------------------------------------------------------------
1 | using EPiServer.ServiceLocation;
2 |
3 | namespace EPiServer.ContentDelivery.NodeProxy;
4 |
5 | ///
6 | /// Options for Node.js proxy.
7 | ///
8 | [Options(ConfigurationSection = ConfigurationSectionConstants.Cms)]
9 | public class NodeJsOptions
10 | {
11 | ///
12 | /// Gets or sets the destination port where the requests
13 | /// should be proxied to. Only loopback is supported.
14 | ///
15 | ///
16 | /// Default is 3000.
17 | ///
18 | public int DestinationPort { get; set; } = 3000;
19 |
20 | ///
21 | /// Gets or sets the launch command.
22 | ///
23 | ///
24 | /// Example: node ./server.js
25 | ///
26 | public string LaunchCommand { get; set; } = string.Empty;
27 |
28 | ///
29 | /// Gets or sets the working directory.
30 | ///
31 | ///
32 | /// Example: ./clientApp/
33 | ///
34 | public string WorkingDirectory { get; set; } = string.Empty;
35 |
36 | ///
37 | /// Gets or sets the environment variables.
38 | ///
39 | ///
40 | /// Environment variables are not supported when output
41 | /// is not redirected. See .
42 | ///
43 | public IDictionary EnvironmentVariables { get; set; } = new Dictionary();
44 |
45 | ///
46 | /// Gets or sets whether the output should be redirected
47 | /// or open a separate terminal window. Only Windows supports
48 | /// open the process in a separate window.
49 | ///
50 | ///
51 | /// Default is true.
52 | ///
53 | public bool RedirectOutput { get; set; } = true;
54 |
55 | ///
56 | /// Gets or sets the timeout in seconds.
57 | ///
58 | ///
59 | /// Default is 10 seconds.
60 | ///
61 | public int ProxyTimeout { get; set; } = 10;
62 |
63 | ///
64 | /// Gets or sets whether the proxy should be disabled or not.
65 | ///
66 | public bool Disabled { get; set; }
67 | }
68 |
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/episerver/content-delivery-js-sdk/67bf8bd6c726cca97aa3ea6b1fe90f016fa8e6e2/src/EPiServer.ContentDelivery.NodeProxy/icon.png
--------------------------------------------------------------------------------
/src/EPiServer.ContentDelivery.NodeProxy/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------