├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── appveyor.yml
├── assets
├── logo.png
└── logo.svg
├── build-appveyor.cmd
├── build.cmd
├── build
├── package.nuspec
├── package.proj
├── readme.txt
└── tools
│ ├── AppVeyorUmbraco
│ └── AppVeyorUmbraco.Targets
│ ├── MSBuildCommunityTasks
│ ├── ICSharpCode.SharpZipLib.dll
│ ├── MSBuild.Community.Tasks.Targets
│ ├── MSBuild.Community.Tasks.chm
│ ├── MSBuild.Community.Tasks.dll
│ ├── MSBuild.Community.Tasks.xml
│ └── Sample.proj
│ └── MSBuildNugetTasks
│ ├── MSBuild.NuGet.Tasks.Targets
│ └── MSBuild.NuGet.Tasks.dll
└── src
├── .nuget
├── NuGet.Config
├── NuGet.exe
└── NuGet.targets
├── Our.Umbraco.HeadRest.sln
└── Our.Umbraco.HeadRest
├── Composing
├── HeadRestComponent.cs
└── HeadRestComposer.cs
├── HeadRest.cs
├── HeadRestConfig.cs
├── HeadRestEndpointMode.cs
├── HeadRestOptions.cs
├── Interfaces
├── IHeadRestConfig.cs
└── IHeadRestOptions.cs
├── Mapping
└── HeadRestMapDefinition.cs
├── Our.Umbraco.HeadRest.csproj
├── Properties
├── AssemblyInfo.cs
└── VersionInfo.cs
├── UmbracoMapperContextExtensions.cs
├── Web
├── Controllers
│ ├── AuthorizedHeadRestController.cs
│ └── HeadRestController.cs
├── HttpRequestBaseExtensions.cs
├── Mapping
│ ├── HeadRestMappingContext.cs
│ └── HeadRestViewModelMap.cs
├── Models
│ └── NotFoundPublishedContent.cs
├── Mvc
│ ├── HeadRestAuthorizeAttribute.cs
│ ├── HeadRestExceptionFilterAttribute.cs
│ └── HeadRestResult.cs
└── Routing
│ ├── HeadRestRouteHandler.cs
│ ├── HeadRestRouteMap.cs
│ ├── HeadRestRouteParamsCollection.cs
│ ├── HeadRestUrlProvider.cs
│ └── UmbracoRoutesConstraint.cs
├── app.config
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | # build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 | [Ii]mport/
25 |
26 | # Visual Studio 2015 cache/options directory
27 | .vs/
28 | # Uncomment if you have tasks that create the project's static files in wwwroot
29 | #wwwroot/
30 |
31 | # MSTest test Results
32 | [Tt]est[Rr]esult*/
33 | [Bb]uild[Ll]og.*
34 |
35 | # NUNIT
36 | *.VisualState.xml
37 | TestResult.xml
38 |
39 | # Build Results of an ATL Project
40 | [Dd]ebugPS/
41 | [Rr]eleasePS/
42 | dlldata.c
43 |
44 | # DNX
45 | project.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | # *.pubxml
144 | # *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Windows Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Windows Store app package directory
160 | AppPackages/
161 |
162 | # Visual Studio cache files
163 | # files ending in .cache can be ignored
164 | *.[Cc]ache
165 | # but keep track of directories ending in .cache
166 | !*.[Cc]ache/
167 |
168 | # Others
169 | ClientBin/
170 | [Ss]tyle[Cc]op.*
171 | ~$*
172 | *~
173 | *.dbmdl
174 | *.dbproj.schemaview
175 | *.pfx
176 | *.publishsettings
177 | node_modules/
178 | orleans.codegen.cs
179 |
180 | # RIA/Silverlight projects
181 | Generated_Code/
182 |
183 | # Backup & report files from converting an old project file
184 | # to a newer Visual Studio version. Backup files are not needed,
185 | # because we have git ;-)
186 | _UpgradeReport_Files/
187 | Backup*/
188 | UpgradeLog*.XML
189 | UpgradeLog*.htm
190 |
191 | # SQL Server files
192 | *.mdf
193 | *.ldf
194 |
195 | # Business Intelligence projects
196 | *.rdl.data
197 | *.bim.layout
198 | *.bim_*.settings
199 |
200 | # Microsoft Fakes
201 | FakesAssemblies/
202 |
203 | # GhostDoc plugin setting file
204 | *.GhostDoc.xml
205 |
206 | # Node.js Tools for Visual Studio
207 | .ntvs_analysis.dat
208 |
209 | # Visual Studio 6 build log
210 | *.plg
211 |
212 | # Visual Studio 6 workspace options file
213 | *.opt
214 |
215 | # Visual Studio LightSwitch build output
216 | **/*.HTMLClient/GeneratedArtifacts
217 | **/*.DesktopClient/GeneratedArtifacts
218 | **/*.DesktopClient/ModelManifest.xml
219 | **/*.Server/GeneratedArtifacts
220 | **/*.Server/ModelManifest.xml
221 | _Pvt_Extensions
222 |
223 | # Paket dependency manager
224 | .paket/paket.exe
225 |
226 | # FAKE - F# Make
227 | .fake/
228 |
229 | # Ignore unimportant folders generated by Umbraco
230 | **/App_Data/ClientDependency/
231 | **/App_Data/ExamineIndexes/
232 | **/App_Data/Logs/
233 | **/App_Data/[Pp]review/
234 | **/App_Data/TEMP/
235 | **/App_Data/cache/
236 | **/css/cache/
237 | **/scripts/cache/
238 | **/Media/
239 | Cached/
240 |
241 | # Ignore Umbraco content cache file
242 | **/App_Data/umbraco.config
243 |
244 | # Don't ignore Umbraco packages (VisualStudio.gitignore mistakes this for a NuGet packages folder)
245 | # Make sure to include details from VisualStudio.gitignore BEFORE this
246 | !**/App_Data/[Pp]ackages/
247 | !**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages
248 |
249 | # Custom
250 | build/_nuget
251 | src/Our.Umbraco.AuthU.TestHarness
252 | src/Our.Umbraco.AuthU.Web
253 | src/Our.Umbraco.AuthU.Matt.*
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this project
2 |
3 | Please take a moment to review this document in order to make the contribution
4 | process easy and effective for everyone involved.
5 |
6 | Following these guidelines helps to communicate that you respect the time of
7 | the developers managing and developing this open source project. In return,
8 | they should reciprocate that respect in addressing your issue or assessing
9 | patches and features.
10 |
11 |
12 | ## Using the issue tracker
13 |
14 | The issue tracker is the preferred channel for [bug reports](#bugs),
15 | [features requests](#features) and [submitting pull
16 | requests](#pull-requests), but please respect the following restrictions:
17 |
18 | * Please **do not** use the issue tracker for personal support requests (use
19 | [Our Umbraco](https://our.umbraco.org) or Twitter).
20 |
21 | * Please **do not** derail or troll issues. Keep the discussion on topic and
22 | respect the opinions of others.
23 |
24 |
25 |
26 | ## Bug reports
27 |
28 | A bug is a _demonstrable problem_ that is caused by the code in the repository.
29 | Good bug reports are extremely helpful - thank you!
30 |
31 | Guidelines for bug reports:
32 |
33 | 1. **Use the GitHub issue search** — check if the issue has already been
34 | reported.
35 |
36 | 2. **Check if the issue has been fixed** — try to reproduce it using the
37 | latest `master` or development branch in the repository.
38 |
39 | 3. **Isolate the problem** — create a reduced test case and a live example.
40 |
41 | A good bug report shouldn't leave others needing to chase you up for more
42 | information. Please try to be as detailed as possible in your report. What is
43 | your environment? What steps will reproduce the issue? What browser(s) and OS
44 | experience the problem? What would you expect to be the outcome? All these
45 | details will help people to fix any potential bugs.
46 |
47 | Example:
48 |
49 | > Short and descriptive example bug report title
50 | >
51 | > A summary of the issue and the browser/OS environment in which it occurs. If
52 | > suitable, include the steps required to reproduce the bug.
53 | >
54 | > 1. This is the first step
55 | > 2. This is the second step
56 | > 3. Further steps, etc.
57 | >
58 | > `` - a link to the reduced test case
59 | >
60 | > Any other information you want to share that is relevant to the issue being
61 | > reported. This might include the lines of code that you have identified as
62 | > causing the bug, and potential solutions (and your opinions on their
63 | > merits).
64 |
65 |
66 |
67 | ## Feature requests
68 |
69 | Feature requests are welcome. But take a moment to find out whether your idea
70 | fits with the scope and aims of the project. It's up to *you* to make a strong
71 | case to convince the project's developers of the merits of this feature. Please
72 | provide as much detail and context as possible.
73 |
74 |
75 |
76 | ## Pull requests
77 |
78 | Good pull requests - patches, improvements, new features - are a fantastic
79 | help. They should remain focused in scope and avoid containing unrelated
80 | commits.
81 |
82 | **Please ask first** before embarking on any significant pull request (e.g.
83 | implementing features, refactoring code, porting to a different language),
84 | otherwise you risk spending a lot of time working on something that the
85 | project's developers might not want to merge into the project.
86 |
87 | Please adhere to the coding conventions used throughout a project (indentation,
88 | accurate comments, etc.) and any other requirements (such as test coverage).
89 |
90 | Follow this process if you'd like your work considered for inclusion in the
91 | project:
92 |
93 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
94 | and configure the remotes:
95 |
96 | ```bash
97 | # Clone your fork of the repo into the current directory
98 | git clone https://github.com//
99 | # Navigate to the newly cloned directory
100 | cd
101 | # Assign the original repo to a remote called "upstream"
102 | git remote add upstream https://github.com//
103 | ```
104 |
105 | 2. If you cloned a while ago, get the latest changes from upstream:
106 |
107 | ```bash
108 | git checkout develop
109 | git pull upstream develop
110 | ```
111 |
112 | 3. Create a new topic branch (off the main project `develop` branch) to
113 | contain your feature, change, or fix:
114 |
115 | ```bash
116 | git checkout -b
117 | ```
118 |
119 | 4. Commit your changes in logical chunks. Please adhere to these [git commit
120 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
121 | or your code is unlikely be merged into the main project. Use Git's
122 | [interactive rebase](https://help.github.com/articles/interactive-rebase)
123 | feature to tidy up your commits before making them public.
124 |
125 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
126 |
127 | ```bash
128 | git pull [--rebase] upstream develop
129 | ```
130 |
131 | 6. Push your topic branch up to your fork:
132 |
133 | ```bash
134 | git push origin
135 | ```
136 |
137 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
138 | with a clear title and description.
139 |
140 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to
141 | license your work under the same license as that used by the project.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Matt Brailsford
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # HeadRest
4 | A REST based headless approach for Umbraco.
5 |
6 | HeadRest converts your Umbraco front end into a REST API by passing ModelsBuilder models through a mapping function to create serializable ViewModels and returning them as JSON payloads.
7 |
8 | Out of the box HeadRest is configured to use UmbracoMapper to perform it's mappings, however you can define your own custom mapper function to use other model mappers such as AutoMapper.
9 |
10 | ## Installation
11 |
12 | ### Nuget
13 |
14 | PM> Install-Package Our.Umbraco.HeadRest
15 |
16 | ## Configuration
17 | In order to configure HeadRest you will first of all need to create an Umbraco composer + compontent combination, resolving the HeadRest service from the DI container like so:
18 | ````csharp
19 | public class HeadRestConfigComponent : IComponent
20 | {
21 | private readonly HeadRest _headRest;
22 |
23 | public HeadRestConfigComponent(HeadRest headRest)
24 | => _headRest = headRest;
25 |
26 | public void Initialize()
27 | {
28 | // Configuration goes here
29 | }
30 |
31 | public void Terminate() { }
32 | }
33 |
34 | public class HeadRestConfigComposer : ComponentComposer
35 | { }
36 | ````
37 |
38 | From within the `Initialize` method, you can then configure your endpoint(s) via the `ConfigureEndpoint` method on the resolved HeadRest service instance:
39 | ````csharp
40 | ...
41 | _headRest.ConfigureEndpoint(...);
42 | ...
43 | ````
44 |
45 | ### Basic Configuration
46 | For the most basic implementation, the following minimal configuration is all that is needed:
47 | ````csharp
48 | _headRest.ConfigureEndpoint(new HeadRestOptions {
49 | ViewModelMappings = new HeadRestViewModelMap()
50 | .For(HomePage.ModelTypeAlias).MapTo()
51 | ...
52 | });
53 | ````
54 |
55 | This will create an API endpoint at the path `/`, and will be anchored to the first content at the root of the site. It will use the list of `ViewModelMappings` provided to lookup the viewmodel to map a given node to.
56 |
57 | ### Advanced Configuration
58 | For a more advanced implementation, the following configuration shows all the supported options.
59 | ````csharp
60 | _headRest.ConfigureEndpoint("/api/", "/root//nodeTypeAlias[1]", new HeadRestOptions {
61 | Mode = HeadRestEndpointMode.Dedicated,
62 | ControllerType = typeof(HeadRestController),
63 | Mapper = ctx => AutoMapper.Map(ctx.Content, ctx.ContentType, ctx.ViewModelType),
64 | ViewModelMappings = new HeadRestViewModelMap()
65 | .For(HomePage.ModelTypeAlias)
66 | .If(x => x.Request.HeadRestRouteParam("altRoute") == "init")
67 | .MapTo()
68 | .For(HomePage.ModelTypeAlias).MapTo()
69 | .Default().MapTo(),
70 | CustomRouteMappings = new HeadRestRouteMap()
71 | .For("/(?init)/?$").MapTo("/")
72 | });
73 | ````
74 | This will create an endpoint at the url `/api/`, and will be anchored to the node at the XPath `/root//nodeTypeAlias[1]`. In addition, the supplied controller will be used to handle the HeadRest requests and the supplied mapper function will be used to perform the mapping. It will use the list of `ViewModelMappings` provided to lookup the viewmodel to map a given node to. Lastly, a custom route is defined to map the url`/api/init/` to the `/api/` URL, storing the string `"init"`, captured via the route pattern, which is then used in the `ViewModelMappings` to map `/api/init/` request to the `InitViewModel` instead of the default `HomePageViewModel`.
75 |
76 | ### Configuration Options
77 | * __basePath : string__
78 | _[optional, default:"/"]_
79 | The base path from which your API will be accessible from.
80 | * __rootNodeXPath : string__
81 | _[optional, default:"/root/*[@isDoc][1]"]_
82 | The XPath statement for the root node from which to anchor your API endpoint to.
83 | * __Mode : HeadRestEndpointMode__
84 | _[optional, default:HeadRestEndpointMode.Dedicated]_
85 | The mode in which the headrest endpoint will run. Can be either `HeadRestEndpointMode.Dedicated`, in which case the generated endpoint URLs will be the primary URLs for the relevant content nodes, or `HeadRestEndpointMode.Mixed`, in which case node will maintain their primary URLs and the endpoint URLs will be added to the nodes "Other URLs" collection.
86 | * __ControllerType : Type__
87 | _[optional, default:typeof(HeadRestController)]_
88 | The Controller to use to service the API requests. Controllers must inherit from `HeadRestController`. Useful to add extra FilterAttributes to the request such as AuthU and the `OAuth` attribute.
89 | * __Mapper : Func__
90 | _[optional, default:ctx => UmbracoMapper.Map(ctx.Content)]_
91 | A function to perform the map between the nodes ModelsBuilder model and it's associated ViewModel. Defaults to using the build in UmbracoMapper.
92 | * __ViewModelMappings : HeadRestViewModelMap__
93 | _[required, default:null]_
94 | A fluent list of mappings to determine which ViewModel a given content type should be mapped to. Multiple mappings can be defined for the same content type by defining a condition for the mapping via the flient `.If(...)` interface. Any conditional mappings should be defined before any non-conditional (fallback) mappings. You can set a default map that will be used if no other matching maps are found by using the `.ForEverythingElse().MapTo()` syntax. If a default map is defined, it must be the last map defined.
95 | * __CustomRouteMappings : HeadRestRouteMap__
96 | _[optional, default:null]_
97 | A fluent list of custom route mappings to map any custom routes to a standard built in route. Routes are defined as regex patterns with any captured paramters being made accessible via a `HeadRestRouteParam` extension on the standard `Request` object. Usefull to support multiple routes pointing to the same endpoint URL, or for things like pagination URLs.
98 |
99 | **NB** Whilst the `ViewModelMappings` tells HeadRest which ViewModel to map a content model to, it does *not* tell it how to actually map the properties over. For this you will need to instruct the model mapper using it's predefined mapping approach, for example, with UmbracoMapper you will want to define your mappings via a `MapDefinition` class registered via a composer like so:
100 | ````csharp
101 | public class MyHeadRestMapDefinition : IMapDefinition
102 | {
103 | public void DefineMaps(UmbracoMapper mapper)
104 | {
105 | mapper.Define(
106 | (frm, ctx) => ..., // Constructor function
107 | (frm, to, ctx) => ... // Map function
108 | }
109 | }
110 |
111 | public class MyHeadRestMapDefinisionComposer : IUserComposer
112 | {
113 | public void Compose(Composition composition)
114 | {
115 | composition.WithCollectionBuilder()
116 | .Add();
117 | }
118 | }
119 | ````
120 | ## How is HeadRest different to Umbraco Headless?
121 | | | HeadRest | Umbraco Headless |
122 | |-|----------|---------------|
123 | | Configuration | HeadRest requires configuring via a code based API so requires C# knowledge | Zero config required |
124 | | Maintenance | You are responsible for applying security patches / updates | Automatic updates |
125 | | Scalability | You are responsible for making the API scalable for your needs | Automatic scalability |
126 | | API | Fixed API based on compiled ViewModels | Flexible query language |
127 | | Security | You are responsible for securing your API (Suggest [AuthU](https://github.com/mattbrailsford/umbraco-authu)) | Security baked in |
128 | | Back office UI | Potentially unused features visible | Unused features disabled |
129 | | Supports use of 3rd party packages | Yes | No |
130 | | Supports mixed install | Yes. You can have a headless API + website in one install | No. Headless only install |
131 | | Supports custom backend code | Yes | No |
132 | | Client libraries available | None. Roll your own | .NET Framework, .NET Core, Node.js |
133 | | Hosting | Installs to any Umbraco version (7.12+) | Umbraco Cloud service only |
134 | | Support | Limited community support | HQ Supported |
135 |
136 | ## Contributing To This Project
137 |
138 | Anyone and everyone is welcome to contribute. Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md).
139 |
140 | * [Bug reports](CONTRIBUTING.md#bugs)
141 | * [Feature requests](CONTRIBUTING.md#features)
142 | * [Pull requests](CONTRIBUTING.md#pull-requests)
143 |
144 | ## License
145 |
146 | Copyright © 2019 Matt Brailsford, Outfield Digital Ltd
147 |
148 | Licensed under the [MIT License](LICENSE)
149 |
150 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2017
2 |
3 | # version format
4 | version: 0.2.0.{build}
5 |
6 | # UMBRACO_PACKAGE_PRERELEASE_SUFFIX if a rtm release build this should be blank, otherwise if empty will default to alpha
7 | # example UMBRACO_PACKAGE_PRERELEASE_SUFFIX=beta
8 | init:
9 | - set UMBRACO_PACKAGE_PRERELEASE_SUFFIX=
10 |
11 | cache:
12 | - src\packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified
13 |
14 | before_build:
15 | - nuget restore src
16 |
17 | build_script:
18 | - build-appveyor.cmd
19 |
20 | artifacts:
21 | - path: artifacts\*.nupkg
22 | - path: artifacts\*.zip
23 |
24 | deploy:
25 | # GitHub Deployment for releases
26 | - provider: GitHub
27 | auth_token:
28 | secure: k1eoY0KXg/FPYKGDeVJCl31exw28E7ToEZmAgc2M9VfbnSN6HeWltfTe7u20lvP7
29 | artifact: /.*\.zip/ # upload all Zip packages to release assets
30 | draft: false
31 | prerelease: false
32 | on:
33 | branch: master
34 | appveyor_repo_tag: true # deploy on tag push only
35 |
36 | # NuGet Deployment for releases
37 | - provider: NuGet
38 | server:
39 | api_key:
40 | secure: q2Aov00i+eWTpbwWHB1JN8EAGw4o8FaOC2lj2MolyS6TGkUUFTJK/vQTQzaf/EQ5
41 | artifact: /.*\.nupkg/
42 | on:
43 | branch: master
44 | appveyor_repo_tag: true
45 |
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/a50f41145e6b9481ed5b739fa2cbf6e7ef1cb27b/assets/logo.png
--------------------------------------------------------------------------------
/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
132 |
--------------------------------------------------------------------------------
/build-appveyor.cmd:
--------------------------------------------------------------------------------
1 | ECHO APPVEYOR_REPO_BRANCH: %APPVEYOR_REPO_BRANCH%
2 | ECHO APPVEYOR_REPO_TAG: %APPVEYOR_REPO_TAG%
3 | ECHO APPVEYOR_BUILD_NUMBER : %APPVEYOR_BUILD_NUMBER%
4 | ECHO APPVEYOR_BUILD_VERSION : %APPVEYOR_BUILD_VERSION%
5 |
6 | CALL src\.nuget\NuGet.exe restore src\Our.Umbraco.HeadRest.sln
7 | CALL "%programfiles(x86)%\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\amd64\MsBuild.exe" build\package.proj
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | ECHO off
2 |
3 | SET /P APPVEYOR_BUILD_NUMBER=Please enter a build number (e.g. 134):
4 | SET /P PACKAGE_VERISON=Please enter your package version (e.g. 1.0.5):
5 | SET /P UMBRACO_PACKAGE_PRERELEASE_SUFFIX=Please enter your package release suffix or leave empty (e.g. beta):
6 |
7 | SET /P APPVEYOR_REPO_TAG=If you want to simulate a GitHub tag for a release (e.g. true):
8 |
9 | if "%APPVEYOR_BUILD_NUMBER%" == "" (
10 | SET APPVEYOR_BUILD_NUMBER=100
11 | )
12 | if "%PACKAGE_VERISON%" == "" (
13 | SET PACKAGE_VERISON=0.0.0
14 | )
15 |
16 | SET APPVEYOR_BUILD_VERSION=%PACKAGE_VERISON%.%APPVEYOR_BUILD_NUMBER%
17 |
18 | build-appveyor.cmd
19 |
20 | @IF %ERRORLEVEL% NEQ 0 GOTO err
21 | @EXIT /B 0
22 | :err
23 | @PAUSE
24 | @EXIT /B 1
--------------------------------------------------------------------------------
/build/package.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 0.0.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/build/package.proj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | $(MSBuildProjectDirectory)\tools\MSBuildCommunityTasks
7 | $(MSBuildProjectDirectory)\tools\MSBuildNugetTasks
8 | $(MSBuildProjectDirectory)\tools\AppVeyorUmbraco
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Our.Umbraco.HeadRest
18 | Our.Umbraco.HeadRest
19 | Our.Umbraco.HeadRest
20 | A REST based headless approach for Umbraco
21 | Matt Brailsford
22 | https://github.com/mattbrailsford/umbraco-headrest/graphs/contributors
23 | Matt Brailsford
24 | Copyright © 2018 Matt Brailsford
25 | MIT license
26 | http://opensource.org/licenses/MIT
27 | false
28 | https://github.com/mattbrailsford/umbraco-headrest
29 | https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/master/assets/logo.png
30 | umbraco rest headless
31 | en-GB
32 |
33 |
34 |
35 |
36 | $(APPVEYOR_BUILD_VERSION)
37 |
38 |
39 |
40 |
41 | false
42 |
43 |
44 |
45 |
46 | true
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | true
55 |
56 |
57 |
58 |
59 | false
60 |
61 |
62 |
63 |
64 |
65 |
66 | Release
67 | $(MSBuildProjectDirectory)\..
68 | $(MSBuildProjectDirectory)\_nuget
69 | $(RootDir)\artifacts
70 | $(RootDir)\src\$(ProjectName)
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
167 |
168 |
169 |
170 |
--------------------------------------------------------------------------------
/build/readme.txt:
--------------------------------------------------------------------------------
1 | ===============================================================
2 | HeadRest
3 | ===============================================================
4 |
5 | HeadRest has been installed!
6 |
7 | Please review the documentation on GitHub to get starterd
8 | - https://github.com/mattbrailsford/umbraco-headrest
--------------------------------------------------------------------------------
/build/tools/AppVeyorUmbraco/AppVeyorUmbraco.Targets:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/build/tools/MSBuildCommunityTasks/ICSharpCode.SharpZipLib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/a50f41145e6b9481ed5b739fa2cbf6e7ef1cb27b/build/tools/MSBuildCommunityTasks/ICSharpCode.SharpZipLib.dll
--------------------------------------------------------------------------------
/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.Targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | $(MSBuildExtensionsPath)\MSBuildCommunityTasks
7 | $(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.dll
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.chm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/a50f41145e6b9481ed5b739fa2cbf6e7ef1cb27b/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.chm
--------------------------------------------------------------------------------
/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/a50f41145e6b9481ed5b739fa2cbf6e7ef1cb27b/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll
--------------------------------------------------------------------------------
/build/tools/MSBuildCommunityTasks/Sample.proj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
67 |
68 |
69 |
70 |
71 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
91 |
92 |
93 |
94 |
96 |
97 |
99 |
100 |
102 |
103 |
104 |
105 |
106 |
107 | list = new List();
110 | list.Add("Happy");
111 | list.Add("New");
112 | list.Add("Year");
113 | Console.WriteLine("Hello MSBuild Community Scripting World.");
114 | foreach(string s in list)
115 | {
116 | Console.WriteLine(s);
117 | }
118 | }
119 | ]]>
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.Targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $(MSBuildProjectDirectory)\MSBuildTasks
6 | $(MSBuildNuGetTasksPath)\MSBuild.NuGet.Tasks.dll
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/a50f41145e6b9481ed5b739fa2cbf6e7ef1cb27b/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.dll
--------------------------------------------------------------------------------
/src/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattbrailsford/umbraco-headrest/a50f41145e6b9481ed5b739fa2cbf6e7ef1cb27b/src/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/src/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 "$(NuGetExePath)"
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir) "
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2015
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Our.Umbraco.HeadRest", "Our.Umbraco.HeadRest\Our.Umbraco.HeadRest.csproj", "{AB6DCB6D-A8F0-4627-906C-77EB840276F1}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {AB6DCB6D-A8F0-4627-906C-77EB840276F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {AB6DCB6D-A8F0-4627-906C-77EB840276F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {AB6DCB6D-A8F0-4627-906C-77EB840276F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {AB6DCB6D-A8F0-4627-906C-77EB840276F1}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {3C19969F-168A-49FD-90FA-95580CFE2318}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Composing/HeadRestComponent.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Routing;
2 | using Umbraco.Core.Composing;
3 | using Umbraco.Web;
4 |
5 | namespace Our.Umbraco.HeadRest.Composing
6 | {
7 | public class HeadRestComponent : IComponent
8 | {
9 | public void Initialize()
10 | {
11 | RouteTable.Routes.IgnoreStandardExclusions();
12 | }
13 |
14 | public void Terminate()
15 | { }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Composing/HeadRestComposer.cs:
--------------------------------------------------------------------------------
1 | using Our.Umbraco.HeadRest.Mapping;
2 | using Our.Umbraco.HeadRest.Web.Routing;
3 | using Umbraco.Core;
4 | using Umbraco.Core.Composing;
5 | using Umbraco.Core.Mapping;
6 | using Umbraco.Web.Routing;
7 |
8 | namespace Our.Umbraco.HeadRest.Composing
9 | {
10 | public class HeadRestComposer : IUserComposer
11 | {
12 | public void Compose(Composition composition)
13 | {
14 | composition.WithCollectionBuilder()
15 | .InsertBefore();
16 |
17 | composition.WithCollectionBuilder()
18 | .Add();
19 |
20 | composition.Register();
21 |
22 | composition.Components()
23 | .Append();
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/HeadRest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Routing;
3 | using System.Collections.Concurrent;
4 | using Umbraco.Web;
5 | using Umbraco.Core;
6 | using Our.Umbraco.HeadRest.Web.Routing;
7 | using Our.Umbraco.HeadRest.Web.Controllers;
8 | using Umbraco.Core.Mapping;
9 |
10 | namespace Our.Umbraco.HeadRest
11 | {
12 | public class HeadRest
13 | {
14 | internal static string RoutePathKey = "path";
15 | internal static string ControllerConfigKey = "headRestConfig";
16 | internal static string RouteMapMatchKey = "HeadRestRouteMapMatch";
17 | internal static string MappingContextKey = "HeadRestMappingContext";
18 |
19 | internal static ConcurrentDictionary Configs = new ConcurrentDictionary();
20 |
21 | private readonly UmbracoMapper _mapper;
22 |
23 | public HeadRest(UmbracoMapper mapper)
24 | => _mapper = mapper;
25 |
26 | public void ConfigureEndpoint(HeadRestOptions options)
27 | {
28 | ConfigureEndpoint("/", "/root/*[@isDoc][1]", options);
29 | }
30 |
31 | public void ConfigureEndpoint(string basePath, HeadRestOptions options)
32 | {
33 | ConfigureEndpoint(basePath, "/root/*[@isDoc][1]", options);
34 | }
35 |
36 | public void ConfigureEndpoint(string basePath, string rootNodeXPath, HeadRestOptions options)
37 | {
38 | var config = _mapper.Map(options);
39 | config.BasePath = basePath;
40 | config.RootNodeXPath = rootNodeXPath;
41 | ConfigureEndpoint(config);
42 | }
43 |
44 | private void ConfigureEndpoint(HeadRestConfig config)
45 | {
46 | ValidateConfig(config);
47 |
48 | if (!Configs.ContainsKey(config.BasePath))
49 | {
50 | if (Configs.TryAdd(config.BasePath, config))
51 | {
52 | RouteTable.Routes.MapUmbracoRoute(
53 | $"HeadRest_{config.BasePath.Trim('/').Replace("/", "_")}",
54 | config.BasePath.EnsureEndsWith("/").TrimStart("/") + "{*"+ RoutePathKey + "}",
55 | new
56 | {
57 | controller = config.ControllerType.Name.TrimEnd("Controller"),
58 | action = "Index",
59 | headRestConfig = config
60 | },
61 | new HeadRestRouteHandler(config),
62 | new { path = new UmbracoRoutesConstraint() });
63 | }
64 | }
65 | }
66 |
67 | private static void ValidateConfig(HeadRestConfig config)
68 | {
69 | if (!typeof(HeadRestController).IsAssignableFrom(config.ControllerType))
70 | {
71 | throw new Exception("Supplied controller type must inherit from HeadRestController");
72 | }
73 |
74 | if (config.ViewModelMappings == null)
75 | {
76 | throw new Exception("ViewModelMappings can not be null");
77 | }
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/HeadRestConfig.cs:
--------------------------------------------------------------------------------
1 | using Our.Umbraco.HeadRest.Interfaces;
2 |
3 | namespace Our.Umbraco.HeadRest
4 | {
5 | internal class HeadRestConfig : HeadRestOptions, IHeadRestConfig
6 | {
7 | public string BasePath { get; set; }
8 | public string RootNodeXPath { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/HeadRestEndpointMode.cs:
--------------------------------------------------------------------------------
1 | namespace Our.Umbraco.HeadRest
2 | {
3 | public enum HeadRestEndpointMode
4 | {
5 | Dedicated,
6 | Mixed
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/HeadRestOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Our.Umbraco.HeadRest.Interfaces;
4 | using Our.Umbraco.HeadRest.Web.Controllers;
5 | using Our.Umbraco.HeadRest.Web.Mapping;
6 | using Our.Umbraco.HeadRest.Web.Routing;
7 | using Umbraco.Core.Composing;
8 | using Umbraco.Core.Mapping;
9 |
10 | namespace Our.Umbraco.HeadRest
11 | {
12 | public class HeadRestOptions : IHeadRestOptions
13 | {
14 | public HeadRestEndpointMode Mode { get; set; }
15 | public Type ControllerType { get; set; }
16 | public Func Mapper { get; set; }
17 | public HeadRestViewModelMap ViewModelMappings { get; set; }
18 | public HeadRestRouteMap CustomRouteMappings { get; set; }
19 |
20 | public HeadRestOptions()
21 | {
22 | Mode = HeadRestEndpointMode.Dedicated;
23 | ControllerType = typeof(HeadRestController);
24 | Mapper = (ctx) => {
25 |
26 | // Currently have to call the UmbracoMapper.Map function
27 | // via reflection as there are no non-generic versions
28 | // available, and as we only know types at runtime, this
29 | // is the only way we can call them. We should keep an eye
30 | // on https://github.com/umbraco/Umbraco-CMS/issues/6250
31 | // and if this ever changes, we should switch to use the
32 | // map methods instead
33 | var mapFunc = typeof(UmbracoMapper).GetMethods()
34 | .First(m => m.Name == "Map"
35 | && m.GetGenericArguments().Count() == 1
36 | && m.GetParameters()
37 | .Select(p => p.ParameterType)
38 | .SequenceEqual(new[] {
39 | typeof(object),
40 | typeof(Action)
41 | })
42 | )
43 | .MakeGenericMethod(ctx.ViewModelType);
44 |
45 | var ctxAction = new Action(x => x.SetHeadRestMappingContext(ctx));
46 |
47 | return mapFunc.Invoke(Current.Mapper, new object[] { ctx.Content, ctxAction });
48 |
49 | };
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Interfaces/IHeadRestConfig.cs:
--------------------------------------------------------------------------------
1 | namespace Our.Umbraco.HeadRest.Interfaces
2 | {
3 | internal interface IHeadRestConfig : IHeadRestOptions
4 | {
5 | string BasePath { get; }
6 | string RootNodeXPath { get; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Interfaces/IHeadRestOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Our.Umbraco.HeadRest.Web.Mapping;
3 | using Our.Umbraco.HeadRest.Web.Routing;
4 |
5 | namespace Our.Umbraco.HeadRest.Interfaces
6 | {
7 | public interface IHeadRestOptions
8 | {
9 | HeadRestEndpointMode Mode { get; }
10 | Type ControllerType { get; }
11 | Func Mapper { get; }
12 | HeadRestViewModelMap ViewModelMappings { get; }
13 | HeadRestRouteMap CustomRouteMappings { get; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Mapping/HeadRestMapDefinition.cs:
--------------------------------------------------------------------------------
1 | using Umbraco.Core.Mapping;
2 |
3 | namespace Our.Umbraco.HeadRest.Mapping
4 | {
5 | public class HeadRestMapDefinition : IMapDefinition
6 | {
7 | public void DefineMaps(UmbracoMapper mapper)
8 | {
9 | mapper.Define(
10 | (src, ctx) => new HeadRestConfig(),
11 | (src, dst, ctx) =>
12 | {
13 | dst.Mode = src.Mode;
14 | dst.ControllerType = src.ControllerType;
15 | dst.Mapper = src.Mapper;
16 | dst.ViewModelMappings = src.ViewModelMappings;
17 | dst.CustomRouteMappings = src.CustomRouteMappings;
18 | });
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Our.Umbraco.HeadRest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {AB6DCB6D-A8F0-4627-906C-77EB840276F1}
8 | Library
9 | Properties
10 | Our.Umbraco.HeadRest
11 | Our.Umbraco.HeadRest
12 | v4.7.2
13 | 512
14 |
15 |
16 |
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\ClientDependency.1.9.7\lib\net45\ClientDependency.Core.dll
38 |
39 |
40 | ..\packages\ClientDependency-Mvc5.1.8.0.0\lib\net45\ClientDependency.Core.Mvc.dll
41 |
42 |
43 | ..\packages\CSharpTest.Net.Collections.14.906.1403.1082\lib\net40\CSharpTest.Net.Collections.dll
44 |
45 |
46 | ..\packages\Examine.1.0.0\lib\net452\Examine.dll
47 |
48 |
49 | ..\packages\HtmlAgilityPack.1.8.14\lib\Net45\HtmlAgilityPack.dll
50 |
51 |
52 | ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll
53 |
54 |
55 | ..\packages\ImageProcessor.2.7.0.100\lib\net452\ImageProcessor.dll
56 |
57 |
58 | ..\packages\ImageProcessor.Web.4.8.7\lib\net45\ImageProcessor.Web.dll
59 |
60 |
61 | ..\packages\LightInject.5.4.0\lib\net46\LightInject.dll
62 |
63 |
64 | ..\packages\LightInject.Annotation.1.1.0\lib\net46\LightInject.Annotation.dll
65 |
66 |
67 | ..\packages\LightInject.Mvc.2.0.0\lib\net46\LightInject.Mvc.dll
68 |
69 |
70 | ..\packages\LightInject.Web.2.0.0\lib\net46\LightInject.Web.dll
71 |
72 |
73 | ..\packages\LightInject.WebApi.2.0.0\lib\net46\LightInject.WebApi.dll
74 |
75 |
76 | ..\packages\Log4Net.Async.2.0.4\lib\net40\Log4Net.Async.dll
77 |
78 |
79 | ..\packages\Lucene.Net.3.0.3\lib\NET40\Lucene.Net.dll
80 |
81 |
82 | ..\packages\Markdown.2.2.1\lib\net451\Markdown.dll
83 |
84 |
85 | ..\packages\Microsoft.AspNet.Identity.Core.2.2.2\lib\net45\Microsoft.AspNet.Identity.Core.dll
86 |
87 |
88 | ..\packages\Microsoft.AspNet.Identity.Owin.2.2.2\lib\net45\Microsoft.AspNet.Identity.Owin.dll
89 |
90 |
91 | ..\packages\Microsoft.AspNet.SignalR.Core.2.4.0\lib\net45\Microsoft.AspNet.SignalR.Core.dll
92 |
93 |
94 | ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
95 |
96 |
97 | ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.2\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll
98 |
99 |
100 | ..\packages\Microsoft.Owin.4.0.1\lib\net45\Microsoft.Owin.dll
101 |
102 |
103 | ..\packages\Microsoft.Owin.Host.SystemWeb.4.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
104 |
105 |
106 | ..\packages\Microsoft.Owin.Security.4.0.1\lib\net45\Microsoft.Owin.Security.dll
107 |
108 |
109 | ..\packages\Microsoft.Owin.Security.Cookies.4.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll
110 |
111 |
112 | ..\packages\Microsoft.Owin.Security.OAuth.4.0.1\lib\net45\Microsoft.Owin.Security.OAuth.dll
113 |
114 |
115 | ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
116 |
117 |
118 | ..\packages\MiniProfiler.4.0.138\lib\net461\MiniProfiler.dll
119 |
120 |
121 | ..\packages\MiniProfiler.Shared.4.0.138\lib\net461\MiniProfiler.Shared.dll
122 |
123 |
124 | ..\packages\MySql.Data.6.9.12\lib\net45\MySql.Data.dll
125 |
126 |
127 | ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll
128 |
129 |
130 | ..\packages\NPoco.3.9.4\lib\net45\NPoco.dll
131 |
132 |
133 | ..\packages\Owin.1.0\lib\net40\Owin.dll
134 |
135 |
136 | ..\packages\Semver.2.0.4\lib\net452\Semver.dll
137 |
138 |
139 | ..\packages\Serilog.2.8.0\lib\net46\Serilog.dll
140 |
141 |
142 | ..\packages\Serilog.Enrichers.Process.2.0.1\lib\net45\Serilog.Enrichers.Process.dll
143 |
144 |
145 | ..\packages\Serilog.Enrichers.Thread.3.0.0\lib\net45\Serilog.Enrichers.Thread.dll
146 |
147 |
148 | ..\packages\Serilog.Filters.Expressions.2.0.0\lib\net45\Serilog.Filters.Expressions.dll
149 |
150 |
151 | ..\packages\Serilog.Formatting.Compact.1.0.0\lib\net45\Serilog.Formatting.Compact.dll
152 |
153 |
154 | ..\packages\Serilog.Formatting.Compact.Reader.1.0.3\lib\net45\Serilog.Formatting.Compact.Reader.dll
155 |
156 |
157 | ..\packages\Serilog.Settings.AppSettings.2.2.2\lib\net45\Serilog.Settings.AppSettings.dll
158 |
159 |
160 | ..\packages\Serilog.Sinks.Async.1.3.0\lib\net45\Serilog.Sinks.Async.dll
161 |
162 |
163 | ..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll
164 |
165 |
166 | ..\packages\Serilog.Sinks.Map.1.0.0\lib\netstandard2.0\Serilog.Sinks.Map.dll
167 |
168 |
169 | ..\packages\Superpower.2.0.0\lib\net45\Superpower.dll
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 | ..\packages\Umbraco.SqlServerCE.4.0.0.1\lib\net472\System.Data.SqlServerCe.dll
178 |
179 |
180 | ..\packages\Umbraco.SqlServerCE.4.0.0.1\lib\net472\System.Data.SqlServerCe.Entity.dll
181 |
182 |
183 | ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll
184 |
185 |
186 |
187 |
188 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 | ..\packages\System.Threading.Tasks.Dataflow.4.9.0\lib\netstandard2.0\System.Threading.Tasks.Dataflow.dll
197 |
198 |
199 |
200 | ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
201 |
202 |
203 |
204 |
205 | ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll
206 |
207 |
208 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll
209 |
210 |
211 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll
212 |
213 |
214 | ..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll
215 |
216 |
217 | ..\packages\Microsoft.AspNet.Razor.3.2.7\lib\net45\System.Web.Razor.dll
218 |
219 |
220 | ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.dll
221 |
222 |
223 | ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Deployment.dll
224 |
225 |
226 | ..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Razor.dll
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | ..\packages\UmbracoCms.Core.8.1.3\lib\net472\Umbraco.Core.dll
236 |
237 |
238 | ..\packages\UmbracoCms.Web.8.1.3\lib\net472\Umbraco.Examine.dll
239 |
240 |
241 | ..\packages\UmbracoCms.Web.8.1.3\lib\net472\Umbraco.Web.dll
242 |
243 |
244 | ..\packages\UmbracoCms.Web.8.1.3\lib\net472\Umbraco.Web.UI.dll
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 | Designer
278 |
279 |
280 |
281 |
282 |
283 |
284 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
285 |
286 |
287 |
288 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Our.Umbraco.HeadRest")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Matt Brailsford")]
11 | [assembly: AssemblyProduct("Our.Umbraco.HeadRest")]
12 | [assembly: AssemblyCopyright("Copyright © Matt Brailsford 2018")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("ab6dcb6d-a8f0-4627-906c-77eb840276f1")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Properties/VersionInfo.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | using System;
12 | using System.Reflection;
13 | using System.Runtime.CompilerServices;
14 | using System.Runtime.InteropServices;
15 |
16 | [assembly: AssemblyVersion("1.0.*")]
17 | [assembly: AssemblyInformationalVersion("1.0.0-alpha-000100")]
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/UmbracoMapperContextExtensions.cs:
--------------------------------------------------------------------------------
1 | using Our.Umbraco.HeadRest.Web.Mapping;
2 | using Umbraco.Core.Mapping;
3 |
4 | namespace Our.Umbraco.HeadRest
5 | {
6 | public static class MapperContextExtensions
7 | {
8 | public static void SetHeadRestMappingContext(this MapperContext context, HeadRestMappingContext headRestContext)
9 | {
10 | context.Items[HeadRest.MappingContextKey] = headRestContext;
11 | }
12 |
13 | public static HeadRestMappingContext GetHeadRestMappingContext(this MapperContext context)
14 | {
15 | return context.HasItems
16 | && context.Items.TryGetValue(HeadRest.MappingContextKey, out var obj)
17 | && obj is HeadRestMappingContext ctx
18 | ? ctx
19 | : null;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Controllers/AuthorizedHeadRestController.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Mvc;
2 | using Our.Umbraco.HeadRest.Web.Mvc;
3 | using Umbraco.Web.Models;
4 |
5 | namespace Our.Umbraco.HeadRest.Web.Controllers
6 | {
7 | public class AuthorizedHeadRestController : HeadRestController
8 | {
9 | [HeadRestAuthorize]
10 | public override ActionResult Index(ContentModel model)
11 | {
12 | return base.Index(model);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Controllers/HeadRestController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web.Mvc;
3 | using Umbraco.Web.Models;
4 | using Umbraco.Web.Mvc;
5 | using Our.Umbraco.HeadRest.Web.Mvc;
6 | using Our.Umbraco.HeadRest.Web.Mapping;
7 | using Our.Umbraco.HeadRest.Interfaces;
8 | using Our.Umbraco.HeadRest.Web.Models;
9 |
10 | namespace Our.Umbraco.HeadRest.Web.Controllers
11 | {
12 | [HeadRestExceptionFilter]
13 | public class HeadRestController : RenderMvcController
14 | {
15 | internal IHeadRestConfig Config
16 | {
17 | get
18 | {
19 | return RouteData.Values[HeadRest.ControllerConfigKey] as IHeadRestConfig;
20 | }
21 | }
22 |
23 | public override ActionResult Index(ContentModel model)
24 | {
25 | // Check for 404
26 | if (model.Content is NotFoundPublishedContent)
27 | {
28 | Response.StatusCode = 404;
29 |
30 | return new HeadRestResult
31 | {
32 | Data = new { message = "404 Not Found" }
33 | };
34 | }
35 |
36 | // Process the model mapping request
37 | var contentTypeAlias = model.Content.ContentType.Alias;
38 | var viewModelType = Config.ViewModelMappings.GetViewModelTypeFor(contentTypeAlias, new HeadRestPreMappingContext
39 | {
40 | Request = Request,
41 | HttpContext = HttpContext,
42 | UmbracoContext = UmbracoContext
43 | });
44 |
45 | if (viewModelType == null)
46 | throw new InvalidOperationException($"No view model map found for type '{contentTypeAlias}' at route {Request.Url}");
47 |
48 | var viewModel = Config.Mapper.Invoke(new HeadRestMappingContext
49 | {
50 | Content = model.Content,
51 | ContentType = model.Content.GetType(),
52 | ViewModelType = viewModelType,
53 | Request = Request,
54 | HttpContext = HttpContext,
55 | UmbracoContext = UmbracoContext
56 | });
57 |
58 | return new HeadRestResult
59 | {
60 | Data = viewModel
61 | };
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/HttpRequestBaseExtensions.cs:
--------------------------------------------------------------------------------
1 | using Our.Umbraco.HeadRest.Web.Routing;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Web;
5 |
6 | namespace Our.Umbraco.HeadRest.Web
7 | {
8 | public static class HttpRequestBaseExtensions
9 | {
10 | public static string HeadRestRouteParam(this HttpRequestBase request, int index, string defaultValue = null)
11 | {
12 | var routeParams = request.HeadRestRouteParams();
13 | return routeParams?[index];
14 | }
15 |
16 | public static TType HeadRestRouteParam(this HttpRequestBase request, int index, TType defaultValue = default(TType))
17 | {
18 | var param = request.HeadRestRouteParam(index);
19 | if (param == null || string.IsNullOrWhiteSpace(param.ToString()))
20 | return defaultValue;
21 |
22 | var val = (TType)Convert.ChangeType(param, typeof(TType));
23 | return val = !EqualityComparer.Default.Equals(val, default(TType))
24 | ? val
25 | : defaultValue;
26 | }
27 |
28 | public static string HeadRestRouteParam(this HttpRequestBase request, string name, string defaultValue = null)
29 | {
30 | var routeParams = request.HeadRestRouteParams();
31 | return routeParams?[name];
32 | }
33 |
34 | public static TType HeadRestRouteParam(this HttpRequestBase request, string name, TType defaultValue = default(TType))
35 | {
36 | var param = request.HeadRestRouteParam(name);
37 | if (param == null || string.IsNullOrWhiteSpace(param.ToString()))
38 | return defaultValue;
39 |
40 | var val = (TType)Convert.ChangeType(param, typeof(TType));
41 | return val = !EqualityComparer.Default.Equals(val, default(TType))
42 | ? val
43 | : defaultValue;
44 | }
45 |
46 | private static HeadRestRouteParamsCollection HeadRestRouteParams(this HttpRequestBase request)
47 | {
48 | if (request.RequestContext?.RouteData?.Values == null)
49 | return null;
50 |
51 | if (!request.RequestContext.RouteData.Values.ContainsKey(HeadRest.RouteMapMatchKey))
52 | return null;
53 |
54 | var routeMap = request.RequestContext.RouteData.Values[HeadRest.RouteMapMatchKey] as HeadRestRouteMapMatch;
55 |
56 | return routeMap?.Params;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Mapping/HeadRestMappingContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Web;
3 | using Umbraco.Core.Models.PublishedContent;
4 | using Umbraco.Web;
5 |
6 | namespace Our.Umbraco.HeadRest.Web.Mapping
7 | {
8 | public class HeadRestMappingContext : HeadRestPreMappingContext
9 | {
10 | public Type ViewModelType { get; internal set; }
11 |
12 | internal HeadRestMappingContext()
13 | { }
14 | }
15 |
16 | public class HeadRestPreMappingContext
17 | {
18 | public IPublishedContent Content { get; internal set; }
19 | public Type ContentType { get; internal set; }
20 |
21 | public HttpRequestBase Request { get; internal set; }
22 | public HttpContextBase HttpContext { get; internal set; }
23 | public UmbracoContext UmbracoContext { get; internal set; }
24 |
25 | internal HeadRestPreMappingContext()
26 | { }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Mapping/HeadRestViewModelMap.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Our.Umbraco.HeadRest.Web.Mapping
5 | {
6 | public class HeadRestViewModelMap
7 | {
8 | private IList _modelMaps;
9 | private HeadRestViewModelMapping _mapping;
10 | private HeadRestDefaultModelMapping _defaultMapping;
11 | private Type _defaultViewModelMapType;
12 |
13 | public HeadRestViewModelMap()
14 | {
15 | _modelMaps = new List();
16 | _mapping = new HeadRestViewModelMapping(this);
17 | _defaultMapping = new HeadRestDefaultModelMapping(this);
18 | _defaultViewModelMapType = null;
19 | }
20 |
21 | public HeadRestViewModelMapping For(string contentTypeAlias)
22 | {
23 | _mapping.Reset(new HeadRestViewModelMapInfo(contentTypeAlias));
24 | return _mapping;
25 | }
26 |
27 | public HeadRestDefaultModelMapping ForEverythingElse()
28 | {
29 | return _defaultMapping;
30 | }
31 |
32 | [Obsolete("Use 'ForEverythingElse' instead")]
33 | public HeadRestDefaultModelMapping Default()
34 | {
35 | return _defaultMapping;
36 | }
37 |
38 | internal void AddViewModelMap(HeadRestViewModelMapInfo viewModelMap)
39 | {
40 | _modelMaps.Add(viewModelMap);
41 | }
42 |
43 | internal void SetDefaultViewModelMapType(Type defaultViewModelMapType)
44 | {
45 | _defaultViewModelMapType = defaultViewModelMapType;
46 | }
47 |
48 | internal Type GetViewModelTypeFor(string contentTypeAlias, HeadRestPreMappingContext ctx)
49 | {
50 | foreach(var map in _modelMaps)
51 | {
52 | if (map.ContentTypeAlias == contentTypeAlias && map.Condition.Invoke(ctx))
53 | {
54 | return map.ViewModelType;
55 | }
56 | }
57 |
58 | return _defaultViewModelMapType;
59 | }
60 | }
61 |
62 | public class HeadRestViewModelMapping
63 | {
64 | private HeadRestViewModelMap _modelMap;
65 |
66 | private HeadRestViewModelMapInfo _mapInfo { get; set; }
67 |
68 | internal HeadRestViewModelMapping(HeadRestViewModelMap modelMap)
69 | {
70 | _modelMap = modelMap;
71 | }
72 |
73 | internal HeadRestViewModelMapping Reset(HeadRestViewModelMapInfo mapInfo)
74 | {
75 | _mapInfo = mapInfo;
76 | return this;
77 | }
78 |
79 | public HeadRestViewModelMapping If(Func condition)
80 | {
81 | _mapInfo.Condition = condition;
82 | return this;
83 | }
84 |
85 | public HeadRestViewModelMap MapTo()
86 | where TViewModel : class
87 | {
88 | _mapInfo.ViewModelType = typeof(TViewModel);
89 | _modelMap.AddViewModelMap(_mapInfo);
90 | return _modelMap;
91 | }
92 | }
93 |
94 | public class HeadRestDefaultModelMapping
95 | {
96 | private HeadRestViewModelMap _modelMap;
97 |
98 | internal HeadRestDefaultModelMapping(HeadRestViewModelMap modelMap)
99 | {
100 | _modelMap = modelMap;
101 | }
102 |
103 | public HeadRestViewModelMap MapTo()
104 | where TViewModel : class
105 | {
106 | _modelMap.SetDefaultViewModelMapType(typeof(TViewModel));
107 | return _modelMap;
108 | }
109 | }
110 |
111 | internal class HeadRestViewModelMapInfo
112 | {
113 | public string ContentTypeAlias { get; set; }
114 | public Func Condition { get; set; }
115 | public Type ViewModelType { get; set; }
116 |
117 | public HeadRestViewModelMapInfo(string contentTypeAlias)
118 | {
119 | ContentTypeAlias = contentTypeAlias;
120 | Condition = (ctx) => true;
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Models/NotFoundPublishedContent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using Umbraco.Core.Models;
6 | using Umbraco.Core.Models.PublishedContent;
7 |
8 | namespace Our.Umbraco.HeadRest.Web.Models
9 | {
10 | ///
11 | /// Special instance of IPublishedContent to allow our custom route
12 | /// to run all the way to our controller, then we can throw a 404
13 | /// and return JSON content instead of the HTML 404 that would
14 | /// ordinarily return HTML
15 | ///
16 | internal class NotFoundPublishedContent : IPublishedContent
17 | {
18 | public object this[string alias] => null;
19 |
20 | public IEnumerable ContentSet => Enumerable.Empty();
21 |
22 | public PublishedContentType ContentType => null;
23 |
24 | public int Id => -404;
25 |
26 | public int TemplateId => 0;
27 |
28 | public int SortOrder => 0;
29 |
30 | public string Name => "404 Not Found";
31 |
32 | public string UrlName => null;
33 |
34 | public string DocumentTypeAlias => "404";
35 |
36 | public int DocumentTypeId => 0;
37 |
38 | public string WriterName => null;
39 |
40 | public string CreatorName => null;
41 |
42 | public int WriterId => 0;
43 |
44 | public int CreatorId => 0;
45 |
46 | public string Path => "-404";
47 |
48 | public DateTime CreateDate => DateTime.MinValue;
49 |
50 | public DateTime UpdateDate => DateTime.MinValue;
51 |
52 | public Guid Version => Guid.Empty;
53 |
54 | public int Level => 0;
55 |
56 | public string Url => null;
57 |
58 | public PublishedItemType ItemType => PublishedItemType.Content;
59 |
60 | public bool IsDraft => false;
61 |
62 | public IPublishedContent Parent => null;
63 |
64 | public IEnumerable Children => null;
65 |
66 | public ICollection Properties => new Collection();
67 |
68 | public string UrlSegment => null;
69 |
70 | public IReadOnlyDictionary Cultures => new Dictionary();
71 |
72 | public IEnumerable ChildrenForAllCultures => Enumerable.Empty();
73 |
74 | public Guid Key => Guid.Empty;
75 |
76 | int? IPublishedContent.TemplateId => null;
77 |
78 | IPublishedContentType IPublishedElement.ContentType => new NotFoundPublishedContentType();
79 |
80 | IEnumerable IPublishedElement.Properties => Enumerable.Empty();
81 |
82 | public int GetIndex() => 0;
83 |
84 | public IPublishedProperty GetProperty(string alias) => null;
85 |
86 | public IPublishedProperty GetProperty(string alias, bool recurse) => null;
87 |
88 | public bool IsPublished(string culture = null) => true;
89 |
90 | bool IPublishedContent.IsDraft(string culture) => false;
91 | }
92 |
93 | internal class NotFoundPublishedContentType : IPublishedContentType
94 | {
95 | public int Id => 0;
96 |
97 | public string Alias => "404";
98 |
99 | public PublishedItemType ItemType => PublishedItemType.Unknown;
100 |
101 | public HashSet CompositionAliases => new HashSet();
102 |
103 | public ContentVariation Variations => ContentVariation.Nothing;
104 |
105 | public bool IsElement => false;
106 |
107 | public IEnumerable PropertyTypes => Enumerable.Empty();
108 |
109 | public int GetPropertyIndex(string alias) => -1;
110 |
111 | public IPublishedPropertyType GetPropertyType(string alias) => null;
112 |
113 | public IPublishedPropertyType GetPropertyType(int index) => null;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Mvc/HeadRestAuthorizeAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Mvc;
2 |
3 | namespace Our.Umbraco.HeadRest.Web.Mvc
4 | {
5 | internal class HeadRestAuthorizeAttribute : AuthorizeAttribute
6 | {
7 | protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
8 | {
9 | filterContext.HttpContext.Response.StatusCode = 401;
10 | filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
11 | filterContext.Result = new HeadRestResult
12 | {
13 | Data = new { Message = "Not authorized" }
14 | };
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Mvc/HeadRestExceptionFilterAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Mvc;
2 |
3 | namespace Our.Umbraco.HeadRest.Web.Mvc
4 | {
5 | internal class HeadRestExceptionFilterAttribute : FilterAttribute, IExceptionFilter
6 | {
7 | public void OnException(ExceptionContext filterContext)
8 | {
9 | filterContext.HttpContext.Response.StatusCode = 500;
10 | filterContext.ExceptionHandled = true;
11 | filterContext.Result = new HeadRestResult
12 | {
13 | Data = new { message = filterContext.Exception.Message }
14 | };
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Mvc/HeadRestResult.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Serialization;
3 | using Umbraco.Web.Mvc;
4 |
5 | namespace Our.Umbraco.HeadRest.Web.Mvc
6 | {
7 | internal class HeadRestResult : JsonNetResult
8 | {
9 | public HeadRestResult()
10 | {
11 | SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
12 | SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Routing/HeadRestRouteHandler.cs:
--------------------------------------------------------------------------------
1 | using Our.Umbraco.HeadRest.Web.Models;
2 | using System;
3 | using System.Web.Routing;
4 | using Umbraco.Core.Models.PublishedContent;
5 | using Umbraco.Web;
6 | using Umbraco.Web.Mvc;
7 |
8 | namespace Our.Umbraco.HeadRest.Web.Routing
9 | {
10 | internal class HeadRestRouteHandler : UmbracoVirtualNodeRouteHandler
11 | {
12 | private HeadRestConfig _config;
13 |
14 | public HeadRestRouteHandler(HeadRestConfig config)
15 | {
16 | _config = config;
17 | }
18 |
19 | protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
20 | {
21 | var nodeXPath = _config.RootNodeXPath;
22 |
23 | if (requestContext?.RouteData?.Values != null)
24 | {
25 | if (requestContext.RouteData.Values.ContainsKey(HeadRest.RoutePathKey)
26 | && requestContext.RouteData.Values[HeadRest.RoutePathKey] != null)
27 | {
28 | var path = requestContext.RouteData.Values[HeadRest.RoutePathKey].ToString();
29 |
30 | // Check for a configured custom route
31 | if (_config.CustomRouteMappings != null)
32 | {
33 | var match = _config.CustomRouteMappings.GetRouteMapFor(path);
34 | if (match != null)
35 | {
36 | path = match.Target;
37 |
38 | requestContext.RouteData.Values.Add(HeadRest.RouteMapMatchKey, match);
39 | }
40 | }
41 |
42 | // Construct xpath from path
43 | var pathParts = path.Trim('/').Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
44 | foreach (var pathPart in pathParts)
45 | {
46 | nodeXPath += $"/*[@urlName='{pathPart}'][1]";
47 | }
48 | }
49 | }
50 |
51 | var node = umbracoContext.Content.GetSingleByXPath(nodeXPath);
52 |
53 | return node ?? new NotFoundPublishedContent();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Routing/HeadRestRouteMap.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.RegularExpressions;
3 | using Umbraco.Core;
4 |
5 | namespace Our.Umbraco.HeadRest.Web.Routing
6 | {
7 | public class HeadRestRouteMap
8 | {
9 | private IList _routeMaps;
10 | private HeadRestRouteMapping _mapping;
11 |
12 | public HeadRestRouteMap()
13 | {
14 | _routeMaps = new List();
15 | _mapping = new HeadRestRouteMapping(this);
16 | }
17 |
18 | public HeadRestRouteMapping For(string pattern)
19 | {
20 | _mapping.Reset(new HeadRestRouteMapInfo(pattern));
21 | return _mapping;
22 | }
23 |
24 | internal void AddRouteMap(HeadRestRouteMapInfo routeMap)
25 | {
26 | _routeMaps.Add(routeMap);
27 | }
28 |
29 | internal HeadRestRouteMapMatch GetRouteMapFor(string path)
30 | {
31 | if (path == null)
32 | return null;
33 |
34 | path = path.EnsureStartsWith("/");
35 |
36 | foreach (var map in _routeMaps)
37 | {
38 | var match = map.Pattern.Match(path);
39 | if (match.Success)
40 | {
41 | return new HeadRestRouteMapMatch
42 | {
43 | Target = match.Result(map.Target),
44 | Params = match.Groups
45 | };
46 | }
47 | }
48 | return null;
49 | }
50 | }
51 |
52 | public class HeadRestRouteMapping
53 | {
54 | private HeadRestRouteMap _routeMap;
55 | private HeadRestRouteMapInfo _mapInfo;
56 |
57 | internal HeadRestRouteMapping(HeadRestRouteMap routeMap)
58 | {
59 | _routeMap = routeMap;
60 | }
61 |
62 | internal HeadRestRouteMapping Reset(HeadRestRouteMapInfo mapInfo)
63 | {
64 | _mapInfo = mapInfo;
65 | return this;
66 | }
67 |
68 | public HeadRestRouteMap MapTo(string target)
69 | {
70 | _mapInfo.Target = target;
71 | _routeMap.AddRouteMap(_mapInfo);
72 | return _routeMap;
73 | }
74 | }
75 |
76 | public class HeadRestRouteMapInfo
77 | {
78 | public Regex Pattern { get; set; }
79 | public string Target { get; set; }
80 |
81 | public HeadRestRouteMapInfo(string pattern)
82 | {
83 | Pattern = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
84 | }
85 | }
86 |
87 | public class HeadRestRouteMapMatch
88 | {
89 | public string Target { get; set; }
90 | public HeadRestRouteParamsCollection Params { get; set; }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Routing/HeadRestRouteParamsCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace Our.Umbraco.HeadRest.Web.Routing
4 | {
5 | public class HeadRestRouteParamsCollection
6 | {
7 | private GroupCollection _groups;
8 |
9 | internal HeadRestRouteParamsCollection(GroupCollection groups)
10 | {
11 | _groups = groups;
12 | }
13 |
14 | public string this[int index]
15 | {
16 | get
17 | {
18 | var result = _groups[index];
19 | return result != null ? result.Value : null;
20 | }
21 | }
22 |
23 | public string this[string name]
24 | {
25 | get
26 | {
27 | var result = _groups[name];
28 | return result != null ? result.Value : null;
29 | }
30 | }
31 |
32 | public static implicit operator HeadRestRouteParamsCollection(GroupCollection groups)
33 | {
34 | return new HeadRestRouteParamsCollection(groups);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Routing/HeadRestUrlProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Umbraco.Core.Configuration;
5 | using Umbraco.Core.Logging;
6 | using Umbraco.Core;
7 | using Umbraco.Core.Configuration.UmbracoSettings;
8 | using Umbraco.Web;
9 | using Umbraco.Web.Routing;
10 | using Umbraco.Core.Models.PublishedContent;
11 |
12 | namespace Our.Umbraco.HeadRest.Web.Routing
13 | {
14 | internal class HeadRestUrlProvider : DefaultUrlProvider
15 | {
16 | public HeadRestUrlProvider(IRequestHandlerSection requestSettings, ILogger logger,
17 | IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper)
18 | : base(requestSettings, logger, globalSettings, siteDomainHelper)
19 | { }
20 |
21 | public override UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlMode mode, string culture, Uri current)
22 | {
23 | var headRestUrl = GetHeadRestUrl(umbracoContext, content.Id, culture, HeadRestEndpointMode.Dedicated);
24 |
25 | return headRestUrl ?? base.GetUrl(umbracoContext, content, mode, culture, current);
26 | }
27 |
28 | public override IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
29 | {
30 | var headRestUrl = GetHeadRestUrl(umbracoContext, id, null, HeadRestEndpointMode.Mixed);
31 |
32 | return headRestUrl != null ? new[] { headRestUrl } : base.GetOtherUrls(umbracoContext, id, current);
33 | }
34 |
35 | protected UrlInfo GetHeadRestUrl(UmbracoContext umbracoContext, int id, string culture, HeadRestEndpointMode endpointMode)
36 | {
37 | var content = umbracoContext.Content.GetById(id);
38 |
39 | foreach (var headRestConfig in HeadRest.Configs.Values.Where(x => x.Mode == endpointMode))
40 | {
41 | var rootNode = umbracoContext.Content.GetSingleByXPath(headRestConfig.RootNodeXPath);
42 | if (content.Path.StartsWith(rootNode.Path))
43 | {
44 | var subUrl = string.Join("/", content.AncestorsOrSelf(true, x => x.Level > rootNode.Level)
45 | .Select(x => x.UrlSegment)
46 | .Reverse());
47 |
48 | var url = (headRestConfig.BasePath
49 | .EnsureStartsWith("/")
50 | .EnsureEndsWith("/") + subUrl).EnsureEndsWith('/');
51 |
52 | return UrlInfo.Url(url, culture); ;
53 | }
54 | }
55 |
56 | return null;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/Web/Routing/UmbracoRoutesConstraint.cs:
--------------------------------------------------------------------------------
1 | using System.Web.Routing;
2 | using System.Web;
3 | using System.Linq;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace Our.Umbraco.HeadRest.Web.Routing
7 | {
8 | internal class UmbracoRoutesConstraint : IRouteConstraint
9 | {
10 | private readonly string[] UmbracoReservedPaths = new[]
11 | {
12 | "^umbraco/.*",
13 | "^media/.*"
14 | };
15 |
16 | public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
17 | {
18 | return UmbracoReservedPaths.All(x => !Regex.IsMatch(("" + values["path"]), x));
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/Our.Umbraco.HeadRest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------