` as opposed to TinyMCE's more semantically correct `
` paragraph.
75 |
76 | **Loading and Rendering**
77 | Trix is a more leightweight library and is able to load very quickly, however when considering the functionality and additions that the TinyMCE editor can currently do. It is an unfair comparisson.
78 |
79 | We are aware that TinyMCE takes longer to do its initial load into the backoffice for the first request due to the size of the library. Subsequent requests - regardless if TinyMCE uses an IFrame or not - are fractionaly slower when rendering the editor and its contents than Trix. As mentioned above we aim to reduce the initial file size to help with the first render of a TinyMCE editor.
80 |
81 | One thing from our testing we noticed that the current implementation of TinyMCE when testing with a slow/throttled network connection will load several times. This is due to an implemntation flaw where we currently check that the TinyMCE library is loaded. Due to the way TinyMCE has been developed checking the presence of the main library is loaded is not the best way to know if an editor is ready. We plan to fix this as part of our improvements as TinyMCE gives us a callback function to notify us when it has fully loaded and initilased the editor to help the jarring experience of several loading phases.
82 |
83 |
84 | ## Out of Scope
85 |
86 | **Upgrading to TinyMCE 5**
87 |
88 | After some initital research & findings, we found that it would cause breaking changes for any Umbraco sites who have written their own TinyMCE plugins/buttons. We also found that upgrading to TinyMCE version 5 would give us little benefit, apart from the underlying APIs that it uses has been made neater.
89 |
90 |
91 | ## Unresolved Issues
92 |
93 | * Finding an OpenSource library to help cleanup pasted Word HTML, either JavaScript or C# that does the best job
94 |
95 |
96 | ## Related RFCs
97 |
98 |
99 |
100 | * https://github.com/umbraco/rfcs/pull/15#issuecomment-514550647
101 | * https://github.com/umbraco/rfcs/pull/5
102 |
103 |
104 |
105 | ## Contributors
106 |
107 | This RFC was compiled by:
108 |
109 | * Warren Buckley (Umbraco HQ)
110 |
--------------------------------------------------------------------------------
/cms/0017-media-tracking.md:
--------------------------------------------------------------------------------
1 | # RFC Name
2 |
3 | Request for Contribution (RFC) 0017: _Umbraco media item tracking_
4 |
5 | ## Code of conduct
6 |
7 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md)
8 |
9 | ## Intended Audience
10 |
11 | The intended audience for this RFC is:
12 |
13 | * Umbraco developers
14 | * Umbraco users
15 |
16 | ## Summary
17 |
18 | We would like to enable Umbraco to natively track references between items so that users can visually see how items are interlinked and where they are used. For example, we want to allow users to be able to see where a particular media item is being referenced, or for a developer to see what content is using a particular Macro.
19 |
20 | For this RFC we will focus on Media tracking.
21 |
22 | The goal of this RFC is to detail the MVP (Minimum Viable Product) for an initial release of Media tracking in the CMS and shed light on any issues or pitfalls with the approach described below. In the future, we will add further support for tracking Umbraco item relations.
23 |
24 | ## Motivation
25 |
26 | There is currently no way to tell if an Umbraco item is being referenced. This makes cleanup a difficult process since deleting an item that is referenced can have an unintentional impact on content and potentially break functionality on a site. Adding tracking and overview of Umbraco item usage would help increase confidence when working in the backoffice as users have a chance to either avoid deleting an item or remove/change referenced items before deleting, mitigating unexpected outcomes. It will enable users and developers to be proactive instead of reactive when working with Umbraco items.
27 |
28 | ## Detailed Design
29 |
30 | ### Property Editors
31 |
32 | The fundamental change in Umbraco to support this is to update the Property Editor APIs so that a Property Editor itself returns the UDIs that it is referencing. This will occur whenever an Umbraco content item (Content, Media, Member) is saved that uses Property Editors. When Umbraco saves the data, it will iterate over all of its property types and ask the corresponding Property Editor to return a list of UDIs.
33 |
34 | The method could be something along the lines of:
35 |
36 | ```cs
37 | IEnumerable GetReferences(TContent content, Property property);
38 | ```
39 |
40 | This means the power of resolving related items is in the hands of the Property Editor since it knows exactly what data it is storing and how to process it.
41 |
42 | The property editors that will need to be updated to use this method are:
43 |
44 | * Content Picker
45 | * Media Picker
46 | * Member Picker
47 | * MNTP
48 | * Rich Text Editor
49 | * Grid
50 | * Multi URL Picker
51 | * Nested Content
52 | * Markdown editor
53 |
54 | ### Relations
55 |
56 | When an Umbraco content item (Content, Media, Member) is saved and all of the UDI references have been resolved from the Property Editors, we will store these relations in the Umbraco Relations database table using the Umbraco Relations Service APIs. For content, we will be tracking relations for "Published" and "Pending" content.
57 |
58 | The relations Service APIs may need to be enhanced to properly support Umbraco media item tracking and reporting detailed in this RFC.
59 |
60 | _NOTE:_ We need to investigate how Umbraco Deploy is handling relations since we don't want to reinvent anything. We will either leverage what Umbraco Deploy is doing or Umbraco Deploy will leverage this implementation.
61 |
62 | ### Media reporting
63 |
64 | We intend on adding a report to the "Info" tab of a media item to show it's relations. The "Info" tab on a media item currently has quite a lot of available real estate so we plan on using that instead of creating another content app for this.
65 |
66 | This report will be a list showing which content, media and member items the media item is being used in. We will also indicate if these references are in the recycle bin. Each item will have a link to navigate to the related item and ideally, this will be in infinite editing but as an MVP this may mean linking to the item directly.
67 |
68 | ## Drawbacks
69 |
70 | * Potential performance implications
71 | * Tracked relations between Umbraco items might potentially become out of sync which can add more confusion
72 |
73 | ## Alternatives
74 |
75 | Let users choose community packages to achieve this functionality such as Nexu. However, we want to be able to enable more than media tracking. We want to ensure that the functionality for reporting dependencies is baked into the Umbraco core editors instead of relying on external logic trying to manage these relations. We hope to borrow some inspiration from community packages and their users to achieve the best results.
76 |
77 | ## Out of Scope
78 |
79 | * Although the design of this implementation will support tracking other Umbraco items other than media, the implementation of this RFC will focus solely on Media which means any reporting and functionality relating to other Umbraco items: Macros, Content, Templates, Members, Forms will not be part of the MVP.
80 | * Users, Languages, Views, Partial Views, Stylesheets, Scripts will not be part of the relationship tracking APIs developed.
81 | * For this MVP, reporting of media relations will be limited to the Info app on Media items only. Any additional reporting is outside of the scope of this RFC and may be included in future RFCs.
82 | * For this MVP we do not plan on warning the user upon deletion of a media item if it is actively being used, in the future this will be a feature.
83 | * For this MVP we do not plan on making it possible to create connectors/resolvers (similar to [Property Value Converters](https://our.umbraco.com/documentation/Extending/Property-Editors/value-converters)) for getting references from a property editor. We plan to add this functionality in the future.
84 | * It will not be possible register a property editor for tracking via package.manifest in the MVP. We hope to add this functionality in the future.
85 |
86 | ## Unresolved Issues
87 |
88 | * How do we handle variants? The plan is to use the relations APIs and tables to do the tracking but they don't support "Culture" which would be required to link (for example) and media item to an English content variant. Currently, we can only link a media item to an entire Content item. To support this we would either need to: Change the relation table to have an additional language column - but this will mean that this table becomes less generic - and also change the relations APIs to support language, or we create a brand new tracking data set and services APIs. Still needs investigating.
89 |
90 | ## Related RFCs
91 |
92 | _No related RFCs_
93 |
94 | ## Contributors
95 |
96 | This RFC was compiled by:
97 |
98 | * Shannon Deminick (https://twitter.com/shazwazza)
99 | * Rune Strand (https://twitter.com/hemraker)
100 |
101 | Feedback, suggestions and comments contributed by:
102 |
103 | * Nathan Woulfe
104 | * Carole Rennie Logan
105 | * Bjarne Fyrstenborg
106 | * Kenn Jacobsen
107 | * Claus Jensen
108 | * Dave Woestenborghs
109 |
110 | Thanks for participating 🙌
--------------------------------------------------------------------------------
/cms/0020-install-process-under-net-core.md:
--------------------------------------------------------------------------------
1 | # RFC Name
2 |
3 | Request for Contribution (RFC) 0020 : _Install Process under .NET Core_
4 |
5 | ## Code of conduct
6 |
7 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md)
8 |
9 | ## Intended Audience
10 |
11 | The intended audience for this RFC is technical users and developers.
12 |
13 | ## Summary
14 |
15 | This RFC is a child of [Project UniCore - Introduction & Strategy](https://github.com/umbraco/rfcs/blob/master/cms/0001-project-unicore-intro.md)
16 |
17 | In moving the Umbraco project to .NET Core, there will be changes needed in how a new Umbraco project is started. Existing methods for starting a new project won’t work with changes that have been made to NuGet. This RFC focuses on what those changes are, how they affect the Umbraco install process, and how we can continue to make the install process easy for installers.
18 |
19 |
20 |
21 | ## Motivation
22 |
23 | The change from .NET Framework to .NET Core as the targeted framework means that we will need to reference NuGet packages using the `PackageReference` syntax instead of the legacy `packages.config` file. This change brings with it certain restrictions that Microsoft has imposed on the `PackageReference` dependencies. In particular, scripts are not supported cross-platform and install scripts and config file transformations are no longer executed at all.
24 |
25 | Not being able to transform configuration files and not being able to run install scripts means that Umbraco does not have a simple way to set up all the deep interactions it needs with the ASP.NET Core website.
26 |
27 | ## Detailed Design
28 |
29 | We suggest introducing a `dotnet new` template. These templates are used by Visual Studio, other IDEs, and the `dotnet` CLI to create new projects for .NET Core applications. Custom templates are supported, and allow for completely customizing the initial project structure and configuration.
30 |
31 | The template would include the `UmbracoCMS` NuGet package and configure the project settings and initial files to allow for a user to start developing.
32 |
33 | The templates allow for parameters to be provided to customize the installation process. For the initial implementation, we will support a project name as the only available parameter. Other parameters can be added in the future to further customize the Umbraco project.
34 |
35 | ### Installing Umbraco from the .NET Core CLI would then be:
36 | One time install of the template: `dotnet new -i UmbracoCms` and then `dotnet new UmbracoCms -n MyProjectName` when you want to create a new project.
37 |
38 | ## Drawbacks
39 | The primary drawback is that the template needs to be installed before it can be used for the first time. This is an additional step before users can get started.
40 |
41 | Another drawback is that configuration changes needed during upgrades would have to be handled in a different manner, since the `PackageReference` syntax no longer allows for install scripts, and .the `dotnet new` templates would only apply to new projects.
42 |
43 | ## Alternatives
44 |
45 | The only alternative we can come up with is to include a script that will do the configuration transformation and other needed things when executed. If we were to provide a script, it would need to be run manually after installing the NuGet package.
46 |
47 | ## Out of Scope
48 |
49 | * The advanced configuration of the project template is out for the scope of the first edition.
50 |
51 | ## Unresolved Issues
52 |
53 | The answers that we are hoping to get from the community are:
54 |
55 | 1. Are you aware of any better alternatives?
56 | 1. Are you aware of any possible way to achieve the existing workflow?
57 |
58 | ## Related RFCs
59 |
60 | * [RFC - Introduction & Strategy](https://github.com/umbraco/rfcs/blob/master/cms/0001-project-unicore-intro.md)
61 |
62 | ## Contributors
63 |
64 | This RFC was compiled by:
65 |
66 | * Benjamin Carleski (Umbraco Community)
67 | * Bjarke Berg (Umbraco HQ)
68 | * Emma Garland (Umbraco Community)
69 | * Steve Temple (Umbraco Community)
70 | * Andy Butland (Umbraco Community)
71 | * Yvo Linssen (Umbraco Community)
72 | * The Umbraco Unicore Team
73 |
--------------------------------------------------------------------------------
/cms/0021-future-proofing-the-umbraco-backoffice.md:
--------------------------------------------------------------------------------
1 | # Future-proofing the Umbraco backoffice
2 | Request for Contribution (RFC) 0021 : Future-proofing the Umbraco backoffice
3 |
4 | ## Code of conduct
5 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md)
6 |
7 | ## Intended Audience
8 | The intended audience for this RFC is: technical users, developers and package authors
9 |
10 | ## Summary
11 | To future-proof Umbraco’s backoffice, we intend to run a three-part process - each with their own RFC.
12 | 1. Standalone UI Component library (RFC soon - January 2021)
13 | 2. Defining backoffice extension API (RFC spring 2021)
14 | 3. Implement backoffice (RFC summer 2021)
15 |
16 | It can be helpful to watch [this umbracoCoffee](https://youtu.be/i0QfgRYj0zQ?t=1681) episode where Filip Bech, CMS Program Manager for Umbraco, introduces the RFC and the thoughts behind it.
17 |
18 | ## Motivation
19 | Umbraco’s backoffice is a big reason editors AND developers choose Umbraco. It is easy to use and flexible to customize to specific needs for a project/client
20 |
21 | The current (second) generation back-office is built using AngularJS. It was a great choice in 2013, and has served us well, but has long been considered outdated. From December 2021 it will no longer receive updates/patches (RIP).
22 |
23 | Since 2012 we have learnt a lot about building a complex Single Page Application (SPA). The current situation doesn’t allow us (or makes it really hard) to fix things we have done inherently wrong. The current state is holding development - and excitement - back.
24 |
25 | There are 4 challenges of the current backoffice, that motivates us for this project:
26 | 1. Product-specific UI makes the Umbraco journey (CMS, 1st- and 3rd party packages, Cloud, etc.) inconsistent and development inefficient.
27 | 2. Unclear extension API surface makes developers reach for undocumented hacks, resulting in an unofficial API layer that we still try to maintain.
28 | 3. The current architecture and framework is holding back and/or slowing down development.
29 | 4. AngularJS enters EOL in ~12 months.
30 |
31 | With the next generation of Umbraco’s backoffice, we want to bring a number of benefits to Umbraco, namely:
32 | - Speed up maintenance and future development of BackOffice for Umbraco HQ and community
33 | - Make integrations to 3rd parties more straightforward
34 | - Make Umbraco more attractive for newcomers, by using modern technology and methodology - thereby also making it easier to contribute
35 | - Simplify and speed up work for developers and package-authors, by enhancing the ability to create great UI/UX
36 | - Streamline the experience throughout the entire journey incl. 1st- and 3rd party packages, Cloud etc.
37 | - Reuse work across our products for less development but also for improvements to be distributed.
38 | - Maintain market leadership in customizable editing experience (tailored to clients)
39 | - Improve the upgrade experience
40 |
41 | ## Detailed Design
42 | To tackle the issues and achieve the benefits described above, we intend to split the project into 3 parts/major milestones.
43 |
44 | We do this in order to get real user feedback through an exploratory and agile process, and to make a difference for developers and editors in increments.
45 |
46 | The three milestones will have individual RFCs and are as follows:
47 | 1. Build a UI Library
48 | 2. Define backoffice extension API
49 | 3. Implement backoffice
50 |
51 | The parts are not entirely sequential and can run in parallel to a certain extent.
52 |
53 | ### Timing
54 | RFC for part 1 will be published in January and we expect work to start shortly thereafter.
55 |
56 | We feel this is important and that we risk halting further development if we don't act now and with sufficient team-size.
57 |
58 | We expect part 2 to start in the spring. Part 1 does not need to be done before this can start
59 |
60 | Part 3 will start when part 2 is ready.
61 |
62 | Read on to learn more about the different parts, and, as mentioned, there will be separate RFCs for each part (we will make sure to update this with links as they are published). We also intend to create a new community team around these efforts.
63 |
64 | ### 1. Build a UI Component Library
65 | We want to create a library of new UI components that are thoughtfully built with reuse and accessibility in mind.
66 | The UI library will be its own separate Open Source project with source code on GitHub and distributed for use oustside the Umbraco backoffice via NPM (packages that are not exclusive to Umbraco, other Umbraco HQ products etc.).
67 |
68 | The goal of part 1 is to create a consistent set of components covering our needs for the CMS. Additionally these should make it easy for developers and package-authors to make something that "feels like Umbraco".
69 |
70 | We want documentation to be built in, so it won't become outdated or left out.
71 |
72 | We want the components to work with any framework so it is not tied to Umbraco’s backoffice only.
73 |
74 | We intend to start releasing public builds when we have sufficient components to get an understanding of how the UI Library will work and in order to get feedback and collaboration as soon as possible.
75 |
76 | **A UI Component Library will make the Umbraco experience more beautiful, accessible and consistent, while also making it easier and less work for developers and maintainers.**
77 |
78 | ### 2. Define BackOffice Extension API
79 | In the second part, the goal is to define a public API for the backoffice. The official/documented API for the current backoffice implementation is too limited, which has led developers to rely on undocumented hacks, that the backoffice now unofficially supports.
80 |
81 | The current API is not easy for newcomers to understand, and it makes it hard to know what can be safely used/changed.
82 |
83 | Furthermore, the current API is tightly coupled to AngularJS, which means developers have to learn an additional (outdated) framework to extend/customize Umbraco and also means that changes to the framework can break the API.
84 |
85 | The goal of part 2 is to define an API that is based on Web Standards and best practices, without any ties to a specific framework.
86 |
87 | This second part will define what extending Umbraco will look like. We want to keep our options open and not decide anything just yet. This will be discussed and decided in the RFC for part 2 and there will be examples to start the discussion. Further prototyping will also be done in part 2.
88 | _An example of how this could work_, as opposed to today where you define an angular-template and a .js-resource for a dashboard or property editor, you would instead define a tagName and a .js-resource. This will give developers the flexibility to decide on their own implementation of choice.
89 |
90 | We want to support all the current documented/official use cases, and a subset of the undocumented use cases. This is crucial to help lighten the burden on package developers in terms of migrating to the new API.
91 |
92 | It should be easy and intuitive to extend and customize Umbraco’s backoffice - familiar for experienced Umbraco developers and recognizable for frontend developers.
93 |
94 | The API-implementation will have TypeScript definitions, enabling IDEs and tooling to help developers as much as possible, but TypeScript will not be forced on developers.
95 |
96 | **A clearly defined API will make it easier to develop and extend Umbraco’s backoffice, provide better tooling and make it safer to upgrade Umbraco.**
97 |
98 | ### 3. Rebuild BackOffice
99 | We need to rebuild the existing Umbraco backoffice, with the new API defined in part 2. This will be a complete rewrite, not a migration, and can only be released after it is completely done!
100 |
101 | The goal of part 3 is to have a modern and maintainable platform, where the technology doesn't complicate or slow down further development. Umbraco’s backoffice should be flexible enough to allow for new ideas and should be testable. The rebuild will use TypeScript (mandatory for Core developers).
102 |
103 | When rebuilding backoffice a choice of a framework can become relevant. No matter the choice of framework, this won’t make any difference for third party extensions/packages as backoffice APIs have no framework coupling.
104 |
105 | In order to mitigate the impact of releasing part 3 for developers and package authors, we will ensure that documentation and training are updated in due time, and that a Release Candidate will be available for an extended period of time before the final release.
106 |
107 | This will be done in collaboration with the various community teams, such as the documentation curators and the package team.
108 |
109 | Furthermore we will aim to have the changes running on the existing backend codebase allowing for "the old" and new backoffice to run simultaneously during development. This means that alpha/beta/release candidates will include both "the old" and new backoffice, whereas final release will be the new backoffice codebase.
110 |
111 |
112 | **A new implementation of Umbraco’s backoffice will give us a better platform to build on and allow us to remove AngularJS. Furthermore, allowing us to evolve the backoffice without breaking the API.**
113 |
114 | ## Drawbacks
115 | Part 3 will be a big-bang release (everything released at once) and will effectively break all existing packages.
116 |
117 | We have considered alternative approaches such as a more incremental approach using adapter/API-bridge/compatibilty layer. We chose to move on with the proposed solution, to allow the flexibility we want in a new architecture, to "cut the strings" and not have legacy-code to support.
118 |
119 | It will require a major version release of Umbraco CMS.
120 |
121 | ## Alternatives
122 | **We could just pick a framework and build everything in that**
123 | While this might initially make it easier for everyone, it would likely put us in the same place in the future. Also, this would mean we have to do all the work up front, with no clear benefit for other products.
124 |
125 | **We could do a more incremental upgrade and have adapters/bridges providing some backwards compatibility**
126 | This would result in a larger project scope and we would eventually either be stuck maintaining the bridge/adapter or having to break everything later. This won’t allow us to get the cleanup or architectural freedom we want.
127 |
128 | **We could stick with AngularJS - it works and won’t stop working. Or just wait some more...**
129 | It’s not really a solution. We get further and further away from the “hype-train” and new developers will have to learn something “only” for Umbraco. It is already an argument against Umbraco. This compares to staying on the ASP .NET framework. The longer we wait, the longer we have to maintain potential issues in angularJS ourselves (after December, 2021).
130 |
131 | ## Out of Scope
132 | This RFC describes the project structure/process and that’s what we would like to discuss. There will be detailed RFCs for each part of the process.
133 |
134 | Any discussion about specific technologies is out of scope for this RFC. That will be appropriate in the individual RFCs, as they are specific to each part. Use of TypeScript can be debated here, as it spans all three parts!
135 |
136 | No new features will be added when rebuilding backoffice, as we aim to keep the same feature set as latest v8. This can be discussed but not any specific new features.
137 |
138 | ## Unresolved Issues
139 | The answers that we are hoping to get from the community & Umbraco HQ is:
140 |
141 | - Validating the overall approach for future-proofing Umbraco’s backoffice
142 | - Any obstacles/issues that need to be taken into account either for the overall process or for RFCs for the individual parts.
143 | - How to approach migration of packages. What can be done to ease the process while maintaining the ability to define a new API and rebuild the backoffice.
144 |
145 | ## Related RFCs
146 | - Previous RFC about [Future of the Back-Office Front-End](https://github.com/umbraco/rfcs/pull/8). Many ideas from the current RFC surfaced in this previous RFC, especially the description of motivation and benefits/opportunities have been influential.
147 | - The main differences are around the proposed process and approach.
148 |
149 | ## Contributors
150 | This RFC was compiled by:
151 |
152 | * Filip Bech-Larsen (Umbraco HQ)
153 | * Niels Lyngsø (Umbraco HQ)
154 | * Rune Strand (Umbraco HQ)
155 |
--------------------------------------------------------------------------------
/cms/0022-ui-library.md:
--------------------------------------------------------------------------------
1 | # UI Component Library
2 | Request for Contribution (RFC) 0022 : UI Component Library
3 |
4 | ## Code of conduct
5 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md)
6 |
7 | ## Intended Audience
8 | The intended audience for this RFC is: technical users, developers and package authors
9 |
10 | ## Summary
11 | We want to ensure a coherent experience for both Content Editors and Developers across products and extensions of these. By separating the essential user interface components from any specific project we ensure usage and maintenance of these will stay as straightforward as possible, making the life of maintainers, contributors, and package developers easier.
12 |
13 | The UI Library is a central source of visual building blocks for Umbraco applications and extensions of such.
14 |
15 | Sharing improvements and ensuring the functionality for a component fits in the bigger perspective and not just solving an immediate need.
16 |
17 | The UI Library will serve as a component guide and come with a browser application (Storybook) in which developers can view, try out and learn about the available components.
18 |
19 | The UI Component library will be a separate Open Source Project with source code on Github and distributed via NPM.
20 |
21 |
22 | ## Motivation
23 | We want to ease the development process of the Umbraco backoffice and packages for it. A UI component library will lower the learning curve and make it easy for developers to get an overview of components and their purposes.
24 |
25 | This overview will also help to design a consistent product experience. By easing the process of identifying components, their role, and their relationships.
26 |
27 | The UI Library is a standalone project, which can be used in multiple projects, including third-party.
28 | The components of the UI Library will be based on web standards and will work in any project.
29 |
30 | The library provides a workspace for developing UI components, enabling us to work on UI without being bound to a specific technology. Initially, we will use this to create components for the future backoffice without relying on any framework decisions.
31 |
32 | We want to ensure that our core UI Components serve a general-purpose. Having them developed separately from any specific project will help ensure the scope of the components keep the right level of abstraction for reusability across projects, extensions, and packages.
33 |
34 | The workspace will enable us to ensure a set of requirements of each component, for example making accessibility a first-class citizen.
35 |
36 | ## Out of Scope
37 | - **Technology/framework** for Umbraco’s backoffice
38 | - **How components are used in the backoffice**: The usage examples in this RFC are examples of how this could be done but not settled.
39 | - **Umbraco Design System** It's our intention to provide the parts necessary for package developers to follow the Umbraco style, but this will not be part of this project.
40 | - **No specific purpose** We want to ensure any product can use this Library, therefore we do not want this to be specific towards the backoffice or any other usages.
41 |
42 |
43 | ## Detailed Design
44 |
45 | For the choice of technology for our UI components, we want to stay as close to native browser technologies as possible. Therefore Web Components is our choice. The components should work in any other tech stack to achieve this it must not have any external technology requirements.
46 |
47 | The components should only contain presentational logic, not any application logic, this architectural choice ensures that the components will never mess up the logic of a project. The only purpose they serve is as visual building blocks. This ensures a clear boundary between the responsibilities of the UI Library and the project using this. Making project code much simpler as the interface code is sealed within the UI Library components.
48 |
49 | We do not want to reinvent the wheel and therefore we have settled for using Google’s LitElement to handle the common needs for building Web Components. It is extremely lightweight and broadly used in design-systems at companies like IBM, Adobe, and others.
50 |
51 | To ensure high-quality code we use TypeScript. This will help ensure consistent and solid code for developers of components. It's optional to use TypeScript as an implementer (i.e. when using components in a project). For those that do use TypeScript, there will be definitions to enable intellisense in IDEs, making it easier to discover and implement the components correctly.
52 |
53 | The project will be published on NPM, to enable ease of use and developers without .Net knowledge. The library will ship with the backoffice, meaning that backoffice packages will not require the use of NPM.
54 |
55 | For the presentation of the components, we will use [Storybook](https://storybook.js.org/) as it’s used widely and supports Web Components. Storybook enables use to document and demonstrate each component, with the ability to interact with the component and explore the configuration options.
56 |
57 | Storybook also enables us to write a story for a component before it has been developed, in this way it becomes a communication tool that enables us to share the ideas and scopes of components before development has started.
58 | We intend to have the parts specific for Storybook as automated as possible to keep maintenance of Storybook as little as possible. Configuration options for components should propagate from Type definitions, code comments etc.
59 |
60 |
61 | ### Lit-element
62 | We want to use Lit-Element to provide helpers for managing the basics of Web Components.
63 |
64 | [](https://www.youtube.com/watch?v=ADgo_JVK02A)
65 | *To suit you with the base knowledge of what Web Components and Lit-Element does, we had Filip make an [introduction video](https://www.youtube.com/watch?v=ADgo_JVK02A). We recommend you to watch this if you are interested in the code behind the components.*
66 |
67 | We have highlighted the two most impactful parts of Lit-Element here:
68 |
69 | #### HTML and CSS Template Literals
70 | Lit-Element provides HTML and CSS Template Literal Tags which smoothens the experience when writing web component templates. This enables us to set component properties, add event listeners, and much more.
71 |
72 | ```
73 | html`
74 |
75 | `
76 | ```
77 |
78 | Notice how properties are bound by inserting a dot in front of the property name. Similarly, we can listen for events by using the @ symbol in front of any event name.
79 |
80 | #### Property-Decorators
81 | Property-Decorators simplifies the initialization of web-component properties.
82 | Most commonly used is @property(), which will make a property reactive, meaning that the element will rerender when the value is changed.
83 |
84 | @property() also enables us to turn a property into an element attribute, by turning on reflection the value of the attribute will be synchronized. Which is a great way to make styling based on the state of a property.
85 |
86 | [Read more about Lit-Element property decorators here](https://lit-element.polymer-project.org/guide/properties#declare-with-decorators)
87 |
88 | ### Styling
89 |
90 | As the web components will be using Shadow DOM, the styling of these can stay fairly simple as they are fully isolated from each other. We don't intend to use any preprocessor framework for this at the current state.
91 | The UI Library will host a set of CSS custom properties, which will be used for styling its components. These properties will also be available for any project implementing the UI Library.
92 |
93 | Additionally, we will use custom properties for the style-values of a component that we want to enable developers to change. Custom Properties inherits through the DOM and ShadowDOMs which enables a property to be overridden for a certain part of the application without affecting other parts.
94 |
95 | This means it would be possible to overwrite the background-color of `` by defining the custom property “--uui-button-background”, like this “--uui-button-background: red;”
96 |
97 | Common styling cases should be available through attributes of components. To exemplify this, let's have a look at the UUI-Button component:
98 |
99 | `Click me`
100 |
101 | This component will in many cases need to be styled to stand out visually or communicate its effect.
102 | For a delete button we would apply the danger-style through the `look` attribute:
103 |
104 | `Delete`
105 |
106 |
107 | #### Theming
108 |
109 | We will enable a limited set of color and sizing customization options, based on CSS Custom Properties. This is mainly to enable a high-contrast mode for accessibility purposes.
110 |
111 |
112 | #### Parts
113 |
114 | CSS-parts is also one of the features of Web Components that can be used to overwrite certain styling of a Web Component. Currently, we do not have any case where this provides any reasonable value. But we will consider enabling this when it makes sense.
115 |
116 | ### A few notes on architecture
117 |
118 | #### Tag-Names
119 | All elements of this library should be prefixed with “UUI-”. This prefix will ensure that Elements from the UI Component Library will differ from specific project Elements and be different from the umb-directives from current backoffice. See the following example of an element from the CMS and two from the library:
120 |
121 | ```
122 |
123 |
124 |
125 |
126 |
127 | ```
128 |
129 | To enable component versioning at a later stage: No code should depend on TagNames of library elements, neither their own nor any of its children. This means when querying or writing CSS selectors we must use `:host`, `this` or use classes/IDs. The reason for this is to enable component versioning at one point.
130 |
131 | #### Use attributes for styling
132 |
133 | When styles are derived from the state of a property, they should be turned into an attribute with reflection.
134 | In this way, we can use CSS Selectors for the attribute rather than mapping values to CSS classes. Example:
135 |
136 | Component CSS:
137 |
138 | ```
139 | :host([disabled]) {
140 | /* styling specific to the disabled state */
141 | }
142 | ```
143 |
144 | Component usage via attribute:
145 | ``````
146 |
147 | Component usage via property:
148 | ``````
149 |
150 |
151 | #### Slots
152 |
153 | Web Components allows the use of Slots, which enables implementers to inject markup into one or more selected slots. This enables us to make compositions of components, simplifying each component as they can stay true to their own responsibility.
154 |
155 | Enabling us to make very simple base components and extend those for specific needs.
156 |
157 | This can be exemplified by the dialog component. It will just serve as a visual frame for any dialog-content. The dialog will provide the base for specific usages of the dialog. This could be the confirm-dialog component which will accept a few properties: headline, description, confirm-label. When a different case is needed this can be built upon the dialog-component, helping us avoid bloated components.
158 |
159 | #### Events
160 |
161 | To make Events propagate outside the scope of a Web Component we have created a base Event class, called UUIEvent. For any custom events, we recommend extending this class to make a proper typed Event for the given Component.
162 |
163 | Events should only hold data if there is specific data for the event. If developers need data of the state of the component they should retrieve this from the element, eventually through `event.target`.
164 |
165 | ### Workflow
166 |
167 | #### Contribution process
168 |
169 | When contributing to the library it should be submitted as a PR to the library.
170 | PR specific for adding a new Element will first be accepted(merged) when it lives up to these criteria:
171 |
172 | - Element name must be prefixed with “UUI-”
173 | - Elements must have tests and pass those
174 | - Elements must pass the basic accessibility test
175 | - Elements must be represented through a story in Storybook
176 | - Elements must follow the Umbraco look and feel
177 | - Source-code must follow the ES-lint rules
178 |
179 | #### Publishing and versioning
180 | We intend for the UI Library to have independent semantic versioning and rapid releases through CI/CD. Releases will automatically be available on NPM.
181 |
182 | ### Documentation
183 | We intend to make documentation auto-generated from component source code. Via TypeScript and JS-docs.
184 |
185 | ### Usage
186 | Any project can implement and use components of the UI Library. A project can combine this with their own components.
187 |
188 | ###Demo
189 |
190 | To get some context to this concept we have already implemented a prototype, which you can try out today.
191 |
192 | [Find the repository here](https://github.com/umbraco/Umbraco.UI.RFCDemo)
193 |
194 | Read `readme.md` for a guide on how to get the project up and running.
195 |
196 | If you like to view the solution without building it by yourself, [we hosted the demo here.](https://umbraco.github.io/Umbraco.UI.RFCDemoStatic)
197 |
198 | [](https://www.youtube.com/watch?v=yb41HCdvjFE)
199 | *If you like a brief introduction, please watch this video where Niels Lyngsø shows the solution and presents some of the thoughts behind the code. [Available on YouTube here](https://www.youtube.com/watch?v=yb41HCdvjFE)*
200 |
201 | ### Contributions
202 |
203 | After the RFC is accepted we would love code contributions to the repository. For now, we would love to focus on the RFC.
204 |
205 |
206 | ## Drawbacks
207 | There is an added complexity as a result of separating the UI Components from the CMS Project.
208 |
209 | ## Alternatives
210 | ***We could build our UI Library in VueJS or any other framework.***
211 |
212 | Developing the UI components without a framework enables us to incorporate them in any other project, and it enables projects to evolve without changing the UI Library, as the UI Library does not rely on any specifics regarding project implementations.
213 |
214 | ***VueJS***
215 |
216 | We know VueJS is popular among many of our community members, but we do not see any benefits in using a specific framework for the scope of the Component Library. Staying with native technology will enable the library to be used in multiple contexts.
217 |
218 | ***Use Tailwind for styling UI Components in the UI Library***
219 |
220 | We do not see any gains by enforcing contributors to learn Tailwind, the benefits of Tailwind are strongest when styles are applied for one DOM with no style encapsulations, in this project we do not have any shared classes across components. If so they will be purposely imported for the given cases.
221 |
222 | ***Use an existing UI Library***
223 |
224 | There are many libraries out there that can solve our needs for basic UI components. There are none that deal with Umbraco-specific use cases. In order for us to have, as simple as possible, native Umbraco UI that deals with Nodes, Media, Trees, etc. We need to own the code ourselves. We expect to use parts from existing libraries in cases where it makes sense. (i.e. incorporating a third party DatePicker)
225 |
226 | ***Element name prefix***
227 |
228 | In regards to choosing the prefix for element names ("")
229 | We have considered other and shorter prefixes (u-, ui-, umb-) but uui- still looks like the best option in order to avoid potential collisions. We see many other UI Libraries using a single letter or a very short prefix and we want enable usage of such libraries. The use of "umb-" would collide with existing backOffice elements.
230 |
231 |
232 | ## Unresolved Issues
233 | - Is this approach understandable?
234 | - Are there aspects of this approach missing?
235 | - Where is the line drawn between specific project components and UI Library components?
236 |
237 |
238 | ## Related RFCs
239 | - Overall process RFC about [Future-proofing the Umbraco backoffice](https://github.com/umbraco/rfcs/blob/main/cms/0021-future-proofing-the-umbraco-backoffice.md).
240 |
241 | ## Contributors
242 | This RFC was compiled by:
243 |
244 | * Niels Lyngsø (Umbraco HQ)
245 | * Filip Bech-Larsen (Umbraco HQ)
246 | * Mads Rasmussen (Umbraco HQ)
247 | * Julia Gruszczynska (Umbraco HQ)
248 | * Rune Strand (Umbraco HQ)
249 |
250 |
251 |
--------------------------------------------------------------------------------
/cms/0024-implement-the-new-backoffice.md:
--------------------------------------------------------------------------------
1 | # Implement The New Backoffice
2 |
3 | ### Request for Comments (RFC) 0024: Implement The New Backoffice
4 |
5 | ## Code of Conduct
6 |
7 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md).
8 |
9 | ## Intended Audience
10 |
11 | The intended audience for this RFC is
12 |
13 | - Technical users
14 | - Developers
15 | - Extension developers
16 | - Package authors
17 |
18 | ## Summary
19 |
20 | To future-proof Umbraco’s backoffice, we intend to run a three-part process - each with an RFC:
21 |
22 | 1. Standalone UI Component library (RFC accepted January 2021)
23 | 2. Defining backoffice extension API (RFC accepted December 2021)
24 | 3. Implement the new backoffice (This RFC)
25 |
26 | It can be helpful to watch this [umbracoCoffee episode](https://youtu.be/i0QfgRYj0zQ?t=1681) where Filip Bruun Bech-Larsen, CTO for Umbraco, introduces the concept.
27 |
28 | ## Motivation
29 |
30 | Umbraco’s backoffice is a big reason why editors AND developers choose Umbraco. It is easy to use and flexible to customize to the specific needs of a project/client
31 |
32 | The current (second) generation backoffice is built using AngularJS. It was a great choice in 2013, and has served us well, but has long been considered outdated. Since December 31, 2021, AngularJS has reached its [end-of-life state](https://blog.angular.io/discontinued-long-term-support-for-angularjs-cc066b82e65a).
33 |
34 | Since 2012 we have learned a lot about building a complex Single Page Application (SPA). The current situation does not allow us (or makes it hard) to fix things we have done inherently wrong. The current state is holding development - and excitement - back.
35 |
36 | With the next generation of Umbraco’s backoffice, we want to bring several benefits to Umbraco, namely:
37 |
38 | - Speed up the maintenance and future development of the backoffice for Umbraco HQ and community developers alike
39 | - The code will utilize TypeScript and accompanying types in the high-seat
40 | - 3rd-party integrations will be considered as first-class citizens
41 | - Make Umbraco more attractive for new contributors, by using modern technology and methodology - thereby also making it easier to contribute
42 | - Simplify and speed up work for developers and package authors, by enhancing the ability to create great UI/UX
43 | - Streamline the experience throughout the entire journey incl. 1st- and 3rd-party packages, Cloud, etc.
44 | - Reuse work across our products for less development but also for improvements to be distributed
45 | - Maintain market leadership in a customizable editing experience (tailored to clients)
46 |
47 | ## Detailed Design
48 |
49 | ### Building the application
50 |
51 | #### Single-Page Application
52 |
53 | As with the old backoffice, the new backoffice will be built as a single-page application. In addition to being served solely in the browser, the new backoffice will also be built as a true standalone application meaning that it can be hosted anywhere since it only requires a browser and a file host to run. No server-side rendering will be required to run the backoffice application.
54 |
55 | #### Routing
56 |
57 | In single-page applications routing is usually a non-trivial area. We are looking into what routers are available at the moment. We need something for the application that can handle both top-level routing and deep-level nesting so that each section, content app, infinite editor, tab, and so on, can push a state to either the URL, the browser history, or both. It should also be possible for any frontend extension to register its own set of routes enabling anyone to deep-link into an extension subarea.
58 |
59 | #### Web Components
60 |
61 | We are going to use a standards-based way to build our custom UI as close to modern browser conventions and technology as possible. We have considered many options - libraries and frameworks alike - and have concluded that we would like to avoid falling into another technical grave in the years to come when what we choose now becomes obsolete.
62 |
63 | > _When you're working with the browser rather than against it, code, skills, and knowledge remain relevant for a longer time. Development becomes faster and debugging is easier because there are fewer layers of abstractions involved._ — [modern-web.dev](https://modern-web.dev/discover/about/)
64 |
65 | We have a firm belief that sticking with the browser standards will continue to evolve and benefit us in the years to come. Therefore we have chosen to build the new backoffice UI with [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components).
66 |
67 | JavaScript frameworks are all doing essentially the same thing: Producing custom elements with syntactic sugar, and also added benefits of having built-in routers, state machines, and stores. The concept of Web Components is created such that the browsers natively support all the things we love from libraries such as React and Vue including having custom elements, reactivity, and state. It allows us to create the most optimal setup and environment for the backoffice to ship modern, slim, chunked modules for the UI. It also allows us to choose whatever libraries we like for state management, routing, and observability.
68 |
69 | Building our software with Web Components - a native web standard - ensures that our software works and will keep working for at least a longer period than any external library will. The APIs will keep getting updated along with the browser itself, patching any security vulnerability along the way.
70 |
71 | ##### Boilerplating
72 |
73 | Web Components require a lot of boilerplate in their current state, so to speed up development, we are going to work with a very efficient, [tiny library called Lit](https://lit.dev/). This library has helped us tremendously in building the [Umbraco UI Library](https://github.com/umbraco/Umbraco.UI) and is also now serving parts of the [Umbraco Cloud portal](https://umbraco.com/products/umbraco-cloud/). Choosing Lit also has the added benefit of being able to share tech and collaborate throughout Umbraco HQ.
74 |
75 | A component for the new backoffice can be built with only a few lines of code:
76 |
77 | ```ts
78 | import { html, css, LitElement } from "lit";
79 | import { customElement, property } from "lit/decorators.js";
80 |
81 | @customElement("my-simple-greeting")
82 | export class SimpleGreeting extends LitElement {
83 | static styles = css`
84 | p {
85 | color: blue;
86 | }
87 | `;
88 |
89 | @property()
90 | name = "Somebody";
91 |
92 | render() {
93 | return html`Hello, ${this.name}!
`;
94 | }
95 | }
96 | ```
97 |
98 | Incidentally, Web Components and Lit take care of most of the stuff we know from AngularJS, such as the concept of "components" and data binding. Running through a data set is fairly easy with Lit's template interpolation with native JavaScript, we can accomplish this in a much cleaner way than AngularJS ever did, where you had two-way data binding. Two-way data binding is, however, something that only AngularJS supported and is not something you would use today. Instead, the flow would be: input = data, output = events.
99 |
100 | ```mermaid
101 | flowchart
102 | ParentComponent -- data in through attributes --> ChildComponent
103 | ChildComponent -- events out through an emitter --> ParentComponent
104 | ```
105 |
106 | Although we have chosen Lit and TypeScript to build the new backoffice, developers are free to use any framework and libraries of their choice to implement backoffice extensions. We will be building an extension API that is framework agnostic as stated in an [earlier RFC](https://github.com/umbraco/rfcs/blob/main/cms/0023-define-the-backoffice-extension-api.md#detailed-design).
107 |
108 | Please have a look at [Lit’s excellent playground](https://lit.dev/playground/) to learn more about it.
109 |
110 | #### Umbraco UI Library
111 |
112 | We have built a UI Library that will serve as a great base for the backoffice (and some of our other products). Using the Umbraco UI Library provides us with custom-made components needed for the backoffice UI. This means the code for composing UI will be as little as possible. Additionally, we can extend components of the UI Library to make special parts for the backoffice or extensions.
113 |
114 | #### Build setup
115 |
116 | A lot of tools come with the territory when building a modern web application. With the choice of Lit and TypeScript, we need something to help us compile/transpile TypeScript code, bundle everything together in small chunks, and remove any code that is not in use from libraries, etc.
117 |
118 | Many setups exist today with support for Lit and TypeScript. We especially like the premise of [esbuild](https://esbuild.github.io/) to transpile the code due to its swiftness and lightness. For tree shaking and splitting, we like what [Rollup](https://rollupjs.org/) has done in terms of supporting esbuild. However, managing the configuration of these and keeping them up-to-date is not a task that we want to spend too much time on. Therefore we have chosen to use [Vite.js](https://vitejs.dev/) to handle everything related to the development and build processes, including running the entire application in a development mode when building it as well as creating the production build. Vite uses Rollup underneath the surface and it also exposes the underlying configuration if we need to change something, but all-in-all it abstracts the whole process away to ensure that we are always running the most optimal setup.
119 |
120 | The development flow of the new backoffice will simply consist of cloning down the repository, installing the npm dependencies, running Vite, and a browser will launch immediately supporting live reload and debugging:
121 |
122 | ```bash
123 | git clone git@github.com:umbraco/Umbraco.CMS.Backoffice.git
124 | cd Umbraco.CMS.Backoffice
125 | npm install
126 | npm run dev
127 | ```
128 |
129 | ### Extension API
130 |
131 | With the old backoffice, it was possible to expose the entire AngularJS API globally enabling extension developers to not only use built-in components but also create their own on the same runtime as the backoffice. In addition to AngularJS, we also ship the old backoffice with other globals such as Underscore, jQuery, SignalR, and many more.
132 |
133 | With modern web development, it is customary to run the code through both a minification and a tree-shaking process and even split the code into chunks to avoid loading the entire application initially. This process also puts all dependencies into their own scope, preventing them from leaking and/or being used outside by other bundles such as extensions loaded dynamically unless the dependencies are specifically exposed.
134 |
135 | Initially, we will not expose anything, because the combined bundled versions of our tools like Lit and others are rather large and do not necessarily support being exposed at all. This means that extensions will not be able to hook onto most of the underlying libraries of the backoffice. Instead, extensions will have to provide their dependencies and run them through a tree-shaking process with tools like Rollup. However, we are looking to see if some tools could benefit from being provided globally - provided that they come with a universal module that runs in the browser. We expect that the overhead of overlapping dependencies between the backoffice and extensions will be very small, but a lot of upcoming features such as [import maps](https://github.com/WICG/import-maps) will be interesting to look into to solve that particular drawback.
136 |
137 | The recommendation to extension developers is that they also choose Lit and TypeScript. To help them do all of that, we are planning to release a helper tool that will allow developers to start building extensions against the new extension API. The tool will bootstrap their development in such a way that they get up and running very quickly and can start writing code.
138 |
139 | There are several ways to go about building extensions for the new backoffice and this tool will cover a few ways of doing so, both for developers wanting to start with TypeScript and Lit, and for those wanting to start with HTML and vanilla JavaScript. The tool could come in the form of either a dotnet template (adding to [the existing templates](https://www.nuget.org/packages/Umbraco.Templates)) or an npm package, and could be invoked like this:
140 |
141 | **Dotnet:**
142 |
143 | ```bash
144 | dotnet new --install Umbraco.Templates
145 | dotnet new umbracopackage --name MyPropertyEditor --type propertyeditor
146 | ```
147 |
148 | **NPM:**
149 |
150 | ```bash
151 | npm create @umbraco/package --name MyPropertyEditor --type propertyeditor
152 | ```
153 |
154 | #### Typings
155 |
156 | The new backoffice will ship with [TypeScript declaration files](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html), either physically or in a [Definitely Typed](https://github.com/DefinitelyTyped/DefinitelyTyped) package, for all relevant components enabling TypeScript as a first-class citizen for Web API interaction with both request and response types, JavaScript runtime services through the Context API, Umbraco backoffice components and UI Library components.
157 |
158 | #### Context API
159 |
160 | To provide contextual shared logic we have established a system called Context API. This is an event-based protocol that components can use to retrieve context from any location in the DOM. A context is an instance of a class that is provided to a certain scope of the DOM.
161 |
162 | The Context API consists of two parts: A Context Provider and a Context Consumer.
163 |
164 | The following example shows how to provide context (in this instance it is a service) for other components to consume:
165 |
166 | ```ts
167 | import { UmbContextProviderMixin } from "@umbraco/context";
168 |
169 | // Create a class that extends from the context PROVIDER mixin, which gives you access to the provideContext method
170 | class UmbAppElement extends UmbContextProviderMixin(HTMLElement) {
171 | constructor() {
172 | super();
173 |
174 | // Provide anything you like here and send it downstream in the DOM tree
175 | this.provideContext("myContextAPIService", new MyContextAPIService());
176 | }
177 | }
178 | ```
179 |
180 | And the context service is then requested through the consumer, which has a handy mixin to extend the component from:
181 |
182 | ```ts
183 | import { UmbContextConsumerMixin } from "@umbraco/context";
184 |
185 | // Create a class that extends from the context CONSUMER mixin, which gives you the consumeContext method
186 | class UmbMyElement extends UmbContextConsumerMixin(HTMLElement) {
187 | constructor() {
188 | super();
189 |
190 | // Request anything you like here such as the myContextAPIService that was provided above
191 | this.consumeContext("myContextAPIService", (api) => {
192 | const myContextApi = api;
193 |
194 | // Invoke a method on the provided service
195 | myContextApi.fooBar();
196 | });
197 | }
198 | }
199 | ```
200 |
201 | #### Styling
202 |
203 | We recommend initiating Web Components with ShadowDOM, and this is always the case when using Lit.
204 |
205 | ShadowDOM encapsulates the insides of a Web Component which means no CSS classes will be available nor will any of the styling interfere with the backoffice.
206 |
207 | Style selectors can be much simpler and separated for each component. The styling (CSS) of a Web Component is provided inline in the JavaScript code by a static `styles` property:
208 |
209 | ```ts
210 | import { LitElement, css, html } from "lit";
211 |
212 | export class MyStylingExampleElement extends LitElement {
213 | static styles = css`
214 | p {
215 | color: darkgrey;
216 | }
217 | div {
218 | border: 1px solid green;
219 | color: green;
220 | padding: 6px;
221 | }
222 | `;
223 |
224 | render() {
225 | return html`
226 | Hello world
227 | My styles are just for my template
228 | This component contains its own styling
231 | `;
232 | }
233 | }
234 | ```
235 |
236 | The Umbraco UI Library will be available for extensions and this lays the foundation for the UI of extensions. If the available UI components do not cover the specific needs on their own, then styling can be based on the CSS Custom Properties provided by the UI Library (example: `--uui-color-text`)
237 |
238 | Read more about the currently available custom properties here: [Sizing Custom-Properties](https://uui.umbraco.com/?path=/story/design-custom-properties--sizing) and [Color Custom-Properties](https://uui.umbraco.com/?path=/story/design-custom-properties--interface-colors)
239 |
240 | Using the provided CSS Custom Properties will ensure that the UI looks right cascading down in the DOM without affecting any backoffice UI:
241 |
242 | ```ts
243 | import { LitElement, css, html } from "lit";
244 |
245 | export class MyStylingExampleElement extends LitElement {
246 | static styles = css`
247 | p {
248 | color: var(--uui-color-text-alt);
249 | }
250 | div {
251 | border: 1px solid var(--uui-color-positive-standalone);
252 | color: var(--uui-color-positive-standalone);
253 | padding: var(--uui-size-space-2);
254 | }
255 | `;
256 |
257 | render() {
258 | return html`
259 | Hello world
260 | My styles are just for my template
261 | This component contains its own styling
264 | `;
265 | }
266 | }
267 | ```
268 |
269 | We recommend using Web Components with ShadowDOM for each component of your extension. Extension developers are free to structure their extensions as desired, and as long as the root element uses a ShadowDOM you can use any tech of choice.
270 |
271 | ### Backoffice Web API
272 |
273 | We are taking the opportunity to create new controllers for the web API during this project. In recent years, a lot of business logic has been moved to a service layer in the backend of Umbraco CMS making the controllers focus only on authentication and creating models around the data.
274 |
275 | #### Authentication
276 |
277 | The controllers for the old backoffice relied on cookie authentication, but this is quickly becoming outdated and can be problematic in a multi-tenancy setup where the backoffice frontend and server do not use the same hostname. We want to adopt the OAuth 2.0 protocol with OpenID Connect for authentication and authorization beginning with the new backoffice. This approach will also allow server-to-server authentication down the road using app tokens and/or having a concept of authorized apps.
278 |
279 | #### Taking a schema-first approach
280 |
281 | When rewriting all the controllers, it is also the perfect time to adopt another paradigm: Schema-first. For each endpoint and model, we are going to describe it in an OpenAPI 3.0 schema (sometimes also known as Swagger) first before implementing it using commonly available tools for the .NET backend and the TypeScript frontend.
282 |
283 | This becomes especially handy for TypeScript, where it will allow us to swiftly convert the schema to several interfaces and then using something like [openapi-typescript-fetch](https://github.com/ajaishankar/openapi-typescript-fetch#readme), we can quickly set up fetchers for getting the data in a type-safe way:
284 |
285 | ```ts
286 | import { paths } from "./schema";
287 | import { Fetcher } from "openapi-typescript-fetch";
288 |
289 | // First we generate a HTTP fetcher (using Fetch) from the generated schema
290 | const fetcher = Fetcher.for();
291 |
292 | // We assign one of the paths (content-by-id) to a constant which makes it type-strong
293 | const getContentById = fetcher.path("/content/{id}").method("get").create();
294 |
295 | // Try and get the data (error codes are not thrown as exceptions but will have to be read from the response
296 | try {
297 | const { data } = await getContentById({ id: 1 });
298 |
299 | // If the data is truthy we know we got the content
300 | if (data) {
301 | console.log("node", data);
302 |
303 | // If the data is not truthy perhaps the data did not exist
304 | } else {
305 | console.log("node does not exist");
306 | }
307 |
308 | // If we land in the catcher, something went wrong with the connection or perhaps the endpoint did not exist
309 | } catch (e) {
310 | console.log("error", e);
311 | }
312 | ```
313 |
314 | ### Documentation
315 |
316 | Documentation for the new backoffice should be explorative, interactive, and as much as possible auto-generated. We want to provide a combined backoffice and UI Library documentation to enable developers to find the relevant details of interest with ease. Therefore we will use Storybook just as the UI Library does, enabling us to merge the two and present combined documentation for the backoffice.
317 |
318 | The documentation should be versioned so users can get the documentation for the specific version of interest. This will replace what we today know as [ApiDocs](https://apidocs.umbraco.com/v9/ui#/api).
319 |
320 | All frontend-related documentation on [our.umbraco.com](https://our.umbraco.com/documentation/) will have to be rewritten. Ideally, code examples will originate from backoffice documentation and thereby be auto-generated.
321 |
322 | ### Testing
323 |
324 | It should be as simple as issuing the command `npm test` to run all tests, and there is going to be a way to get test coverage reports in various formats.
325 |
326 | The old backoffice has a set of unit tests using Karma and a set of acceptance tests using Cypress for the UI. The acceptance tests for the old backoffice will be converted to [Playwright](https://playwright.dev/) in the future and we will try to transfer that work to the new backoffice project, and use it for both unit and end-to-end testing.
327 |
328 | Traditional tools are not designed for piercing the Shadow DOM of Web Components, but this is where Playwright becomes especially handy since it by default looks through an arbitrary number of open shadow roots when selecting elements.
329 |
330 | ### Security & Updates
331 |
332 | Security is an ever present thing to consider: Any external library that we install must be maintained and updated regularly whenever a security patch is released following a CVE or otherwise. This is more easily done on an auto-updated product such as a SaaS product, but with Umbraco CMS being an installed product, we do not have that luxury. As an example, Umbraco 7 was released in 2013 using the new-at-the-time AngularJS framework. This framework is now considered end-of-life as of January 2022 and will no longer receive updates.
333 |
334 | We will strive to maintain the security of the backoffice and the UI Library by using tools such as GitHub's Dependabot to automatically keep the backoffice and UI Library up to date and [CodeQL](https://codeql.github.com/) to catch low-hanging fruit before any code is merged. We will also try to lock down the versions of the various third-party libraries to a specific version so that we may vet any updates before they are applied.
335 |
336 | ## Moving forward
337 |
338 | ### What is going to happen?
339 |
340 | The new backoffice will be shipped in a major version of Umbraco CMS as yet to be determined and the old backoffice will be marked as deprecated in the major version leading up to that one.
341 |
342 | ### GitHub
343 |
344 | The development of the new backoffice will happen in its own GitHub repository to not pollute the main repository of the CMS. This allows us to make sure that we separate all concerns from the backend/C# code. The repository will be made open to the public allowing everyone to follow the project from infancy to completion. When it is finished, we will include it in Umbraco CMS either as seen today, where it is simply included as a web project, or it will be installed during the build step as a submodule or downloaded from somewhere like NuGet.
345 |
346 | ### Property editors and data types
347 |
348 | There are a lot of Property Editors and Data Types built into the old backoffice. Most of these will be migrated to the new backoffice using the same aliases.
349 |
350 | #### Media Picker (Legacy)
351 |
352 | The Media Picker Property Editor has been marked as legacy/deprecated for some time and this editor will not be migrated to the new backoffice. Instead, the new Media Picker, [introduced in Umbraco 8.14](https://umbraco.com/blog/umbraco-814-release/#mediapicker), will be the only one available.
353 |
354 | #### Nested Content
355 |
356 | We do not plan to migrate the Nested Content Property Editor to the new backoffice because the Block List editor supports similar data structures and editing experience. We expect that Nested Content will be marked as a legacy in one of the upcoming major versions of the CMS. Instead, we will see if and how the Block List type can be improved going forward.
357 |
358 | #### Grid Layout
359 |
360 | We do not plan to migrate the Grid Layout Property Editor. The future of Umbraco points towards using Element Types (just like the Block List Property Editor) and the Grid Layout editor does not support this natively. We are currently investigating what a new block-based grid editor will look like.
361 |
362 | #### Rich Text Editor
363 |
364 | We do not plan to keep on using the current version of [TinyMCE](https://github.com/tinymce/tinymce) in the new backoffice. Instead, we will see how the Rich Text Editor can be improved going forward using either the newest version of TinyMCE or something else entirely.
365 |
366 | #### Pre-Configured Data Types
367 |
368 | There are a lot of different Data Types in the old backoffice, and frankly, some of them have been unused for a long time. We are going to comb through the existing ones during this project to see what makes sense to have in the new backoffice.
369 |
370 | ## Drawbacks
371 |
372 | This will be a complete breaking change of the backoffice as a piece of software. It will also disrupt all existing packages that add backoffice elements. They will need to update to the new extension API as discussed with the [RFC #0023](https://github.com/umbraco/rfcs/blob/main/cms/0023-define-the-backoffice-extension-api.md), and they will have to update the UI to use the Umbraco UI Library.
373 |
374 | ## Alternatives
375 |
376 | The main problem to solve is to remove AngularJS. Angular provides something called [Angular Upgrade](https://angular.io/guide/upgrade) which is very extensive. However, upgrading to modern Angular will still give us all of the drawbacks and will not satisfy the wish to become independent of any framework.
377 |
378 | Being independent of any one framework leaves out the possibility of choosing one of the major players in the market such as React or Vue. However, extension developers are free to choose whatever they feel comfortable with for adding functionality to the backoffice.
379 |
380 | A big part of this project is also to reduce and minimize technical debt; a lot of the existing functionality is either duplicated multiple times or written into a service loop where some of the existing AngularJS services do not make sense to have anymore. There is also a varying degree of quality to the existing components in terms of documentation, usability, and accessibility.
381 |
382 | Other alternatives were discussed in [the original RFC for future-proofing the backoffice](https://github.com/umbraco/rfcs/blob/main/cms/0021-future-proofing-the-umbraco-backoffice.md#alternatives).
383 |
384 | ## Out of Scope
385 |
386 | ### Web API
387 |
388 | New Web API controllers will be made available for the new backoffice, however, any technological change on how to build a Web API will be considered out of scope for this RFC and in general, any backend changes to facilitate the new backoffice are out of scope.
389 |
390 | ### Caching
391 |
392 | To have good caching requires a good foundation. We will try to see how we can implement such a foundation that we might support even better caching in the future. We will also try to see how to make user actions such a Save & Publish asynchronous to allow a quicker workflow for editors. To do so, we will implement a series of stores or repositories to manage the data. However, a complete caching solution is considered out-of-scope for this RFC.
393 |
394 | ## Proof of Concept
395 |
396 | We have built a proof-of-concept application along with this RFC. The application can be installed and run on your machine, and you can read the source code freely. The application demonstrates a few things on how to implement the backoffice, namely:
397 |
398 | - How we expect to build the web application
399 | - How we expect to run a test suite
400 | - What is the structure of the application like
401 | - How we expect to handle contexts inside the application
402 | - How we expect extensions to interact with the backoffice and its contexts
403 |
404 | [Check out the dedicated repository right away to get started](https://github.com/umbraco/Umbraco.Backoffice.POC).
405 |
406 | ## Related RFCs
407 |
408 | [#0021 - Future-proofing the backoffice](https://github.com/umbraco/rfcs/blob/main/cms/0021-future-proofing-the-umbraco-backoffice.md)
409 |
410 | [#0022 - UI library](https://github.com/umbraco/rfcs/blob/main/cms/0022-ui-library.md)
411 |
412 | [#0023 - Defining the backoffice extension API ](https://github.com/umbraco/rfcs/blob/main/cms/0023-define-the-backoffice-extension-api.md)
413 |
414 | ## Contributors
415 |
416 | This RFC was compiled by:
417 |
418 | - Jacob Overgaard (Umbraco HQ)
419 | - Mads Rasmussen (Umbraco HQ)
420 | - Niels Lyngsø (Umbraco HQ)
421 | - Jesper Møller Jensen (Umbraco HQ)
422 | - Filip Bruun Bech-Larsen (Umbraco HQ)
423 | - Kenn Jacobsen (Backoffice Community Team)
424 | - Laura Weatherhead (Backoffice Community Team)
425 | - Lee Kelleher (Backoffice Community Team)
426 | - Maud Langaskens (Backoffice Community Team)
427 | - Matt Sutherland (Backoffice Community Team)
428 | - Blake Watt (Backoffice Community Team)
429 | - Paul Sterling (Community)
430 | - Kyle Weems (Community)
431 | - Mark Drake (Community)
432 | - Michael Argentini (Community)
433 | - Dan Diplo (Community)
434 | - Biagio Paruolo (Community)
435 | - Tiffany Prosser (Community)
436 | - PascalIcatt (Community)
437 |
--------------------------------------------------------------------------------
/cms/0025-reusable-content-with-global-blocks.md:
--------------------------------------------------------------------------------
1 | # Reusable Content with Global Blocks
2 |
3 | ### Request for Contribution (RFC) 0025 : Reusable Content with Global Blocks
4 |
5 | **Please comment on this RFC in this [RFC Discussion](https://github.com/umbraco/rfcs/discussions/35)**
6 |
7 | ## Code of Conduct
8 |
9 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md).
10 |
11 | ## Intended Audience
12 |
13 | The intended audience for this RFC is
14 |
15 | - Technical users
16 | - Developers
17 | - Package authors
18 | - Editors
19 |
20 |
21 | ## Summary
22 |
23 | We want to create Global Blocks that can be referenced from multiple Block Lists or Block Grids, and we want these to be editable from the Content Section as well as a Global Block tree in a dedicated section. This new section should include "global content that the editor can/should maintain". We propose naming this new section "Library" and include global Blocks, Tags, and Content Templates. Within the Library section, the Global Blocks should be maintained using the same (or similar) conventions and functionality as in the Content Section - e.g. functionality to publish, unpublish, schedule a block, and handle language variations.
24 |
25 | ## Motivation
26 |
27 | Since the very beginning of Umbraco, we have thrived to create a flexible CMS that gives both developers and content editors the freedom to be creative. The introduction of Blocks, via first the Block List editor and later the Block Grid, is one of the concepts that support this journey. As we now have multiple Property Editors using Blocks, we see an opportunity for introducing even more flexibility and making it easier to reuse and maintain Block content specifically, and content in general.
28 |
29 | We believe Global Blocks will help increase flexibility for developers by adding more options for constructing a good editing experience, and for content editors as they can now publish, unpublish and schedule the publishing of Block content. In addition, maintaining a single Global Block will be easier, as you can do it in one place while having the Blocks that are referenced in multiple places, introducing a more streamlined content model which helps the editor be more effective and consistent.
30 |
31 | ## General Design
32 |
33 | Blocks can now be made local or global. Local Blocks work in the same way as current Blocks. Global Blocks can be created and edited in the same way as Local Blocks, or via a new “Library” section.
34 | To facilitate a good experience all Block content, whether it is local or global, will be handled as standard content. This means storing it in the same way in the database, providing similar actions for managing publish state, variants, and so forth.
35 |
36 | ## Detailed Design
37 |
38 | ### Library Section
39 |
40 | We want to offer a section where Global Blocks can be maintained, however, doing this, opens up the question if other types of content are placed the right way today. Global Blocks are by nature something that is not related to a specific page. It's used on multiple pages but that's also the case for tags, and content templates. We, therefore, suggest that the new Library section also includes these types. An example of such a section could look like this:
41 |
42 | 
43 |
44 | As seen, we want to introduce a folder structure for the global blocks so that the editor has a way to control the structure.
45 | Also be aware, that while Blocks have both Content and Settings, we only consider the Content part to be Global as we believe the Settings part could benefit from being specific for each usage of a Global Block. We, therefore, don’t plan to show any Settings for blocks in the Library section.
46 |
47 | #### Preview
48 |
49 | As a block will always be shown and styled depending on the content wherein it exists, it will never make sense to try to preview a Block without also choosing a Content Node. The editor should therefore be asked to select the Content Node before an actual preview can be shown:
50 |
51 | 
52 |
53 | In case a Block has not yet been referenced from any Content Node, a message should be shown to the user, that a Global Block can't be previewed when it's not yet in use on any Content Items.
54 |
55 | #### Info tab
56 |
57 | Like with Content Nodes, we want to show the general status and history, and we also want to show a list of the Content Items where the Block is referenced. That list should include information about the status of the Content Nodes concerning published, unpublished, scheduled, created and latest changes so that the editor can be guided in updating the right content.
58 |
59 | 
60 |
61 | #### Language
62 |
63 | In short, there should be the same opportunities for languages as with Content Nodes - e.g. with split-view and with publishing messages:
64 |
65 | 
66 |
67 | ### Content Section
68 |
69 | We believe that Local Blocks still have relevance on sites, where you want to limit complexity. We, therefore, want to offer a setting on the Block List and the Block Grid to indicate if it accepts Local and/or Global Blocks. However, as we also see use cases for Block Lists/Grids with both Local and Global Blocks, we want to present this in a friendly way in the UI. This could either be through labels, icons, or both - and should of course work with both the Block List and the Block Grid:
70 |
71 | 
72 | 
73 |
74 | ##### Make Global
75 |
76 | We want to ensure a smooth transition between Local and Global Blocks, so that if you have a Local Block that you want to use in more than one Block List or Block Grid, - it should simply be possible to make it global. In this process, you need to give the Block a name and also place it in the correct folder - and the block will afterwards be available in the Library folder:
77 |
78 | 
79 |
80 | #### Make Local
81 |
82 | While using Global Blocks there might be cases, where the Global Block is almost as you want it - but somehow not 100% the right fit. In these cases, we want to offer an opportunity to make a Block local. By doing this we'll create a new local instance while still keeping the Global Block:
83 |
84 | 
85 |
86 | #### Publish content
87 |
88 | With Global Blocks that can have various states and e.g. be unpublished or scheduled for publishing, we want to make it visible for the editor during the publishing of a content node if there are Global Blocks that are either unpublished or scheduled for publishing. As the Global Blocks are published, unpublished, and scheduled individually they will not affect the publishing of a Content Node, but we see the information about these states as important for the editor:
89 |
90 | 
91 |
92 | #### Edit block
93 |
94 | The experience of editing a Block in the context of a Content Node should be the same as with Local Blocks today, however, instead of a submit button we want to provide the editor with the option to save, unpublish, schedule, and publish.
95 |
96 | 
97 |
98 | #### Add block
99 |
100 | We want the editor to be able to decide up-front if she would like to create a new Local/Global Block or add an existing Global Block to the currently selected Block List or Block Grid. If creating a new Global Block, we want that to be done within the Block tree:
101 |
102 | 
103 |
104 | #### Language
105 |
106 | As mentioned above, we want to provide the same language opportunities for Global Blocks as for Content Nodes. A Global Block should therefore always be visible in all languages, although its content might fall back to the default language. Contrary, a Local Block - just as it is today - is considered unique for a specific language.
107 |
108 | 
109 |
110 |
111 | ### Permissions
112 |
113 | In short, we want to offer the same permissions as exist today on Content Nodes, - meaning that there should be permissions for browse, delete, create, notifications, publish, permission changes, send to publish, unpublish, update, etc. - and that these permissions should be available to set both as default permissions and as specific granular permissions for a node. Also, like with content and media, we want to offer opportunities for limiting the Block Library Tree to a specific start node.
114 |
115 | ## Technical considerations
116 |
117 | We propose creating a similar data structure for Global Blocks as we have it for regular Content Nodes and thereby adding all the same options as with Content Nodes, except for routing. That also means that it should e.g. be possible to find deleted Global Blocks in the recycle bin.
118 | As we think it makes sense to have the Block Settings as something that relates to the specific usage of a Global Block Content, we only consider the Content part to be stored in this “new” structure, whereas we suggest storing the Settings part within the Content Node - as we do it today.
119 |
120 | ## Out of Scope
121 |
122 | Although we mention Tags as part of the new Library section, it's not in the scope of this RFC to discuss moving and updating Tags.
123 |
124 | ### Unresolved issues
125 |
126 | Some of the answers that we hope to get out of this are:
127 | - Related to the technical considerations, if we are right, that it makes most sense to have Block Settings stored within the Content Node or if there are more/better use cases for storing Block Settings globally too.
128 | - In case you work with segments, if you can see any implications in this proposal.
129 | - More perspectives on the proposed new section called “Library”. If it should be there at all and if so what it should include.
130 | - The naming. Should we call it Global Blocks, reusable content or something else.
131 |
132 | ## Contributors
133 |
134 | This RFC was compiled by:
135 |
136 | - Bjarke Berg (Umbrac HQ)
137 | - Jacob Overgaard (Umbraco HQ)
138 | - Filip Bech-Larsen (Umbraco HQ)
139 | - Rune Strand (Umbraco HQ)
140 | - Lasse Fredslund (Umbraco HQ)
141 |
142 | - The CMS Group
143 | - The CMS Community Team
144 |
145 |
--------------------------------------------------------------------------------
/cms/0026-blocks-in-the-rte.md:
--------------------------------------------------------------------------------
1 | # Blocks in the RTE
2 |
3 | ### Request for Contribution (RFC) 0026 : Blocks in the Rich Text Editor (RTE)
4 |
5 | **Please comment on this RFC in this [RFC Discussion](https://github.com/umbraco/rfcs/discussions/42)**
6 |
7 | ## Code of Conduct
8 |
9 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md).
10 |
11 | ## Intended Audience
12 |
13 | The intended audience for this RFC is
14 |
15 | - Technical users
16 | - Developers
17 | - Extension developers
18 | - Package authors
19 |
20 | ## Summary
21 | With the new backoffice (Bellissima) currently targeted for Umbraco 14, we want to remove macros and, consequently, with the release of Umbraco 13m deprecate macros and introduce an alternative.
22 |
23 | We have good alternatives to macros in most use cases, but we are still missing the use case where the editor can insert complex content in the Rich Text Editor (RTE) that is styled and controlled by the developer - e.g. inserting a call to action button within the RTE.
24 |
25 | In this RFC we propose a solution for inserting complex content into the RTE based on Blocks.
26 |
27 | ## Motivation
28 | We have for some time wanted to avoid implementing Macros for the New Backoffice, as the approach is a bit misaligned with the current state of Umbraco. We know, that for some developers it has worked fine and used frequently, but for many developers, it has been an approach “a bit out of context” and not as intuitive as we would like it to be.
29 |
30 | We have investigated the usage of macros and identified a few use cases where there currently lacks an alternative to the functionality provided by Macros.
31 |
32 | We believe these remaining use cases are solved by introducing blocks in the RTE.
33 | As we see more and more usage of blocks and e.g. are looking into [Reusable Content with Global Blocks], it makes sense to build this functionality based on blocks so that we can benefit from future improvements and functionality related to blocks.
34 |
35 | ## Detailed Design
36 | We suggest introducing Blocks in the RTE by offering functionality similar to the Block List and the Block Grid editors.
37 |
38 | #### Editor Settings
39 | Like inserting a macro today, we propose an additional button in the RTE toolbar. As with other buttons in the RTE, this should be enabled in the toolbar configuration together with a block configuration where you can choose the block types that should be allowed for this specific instance of the RTE. It could then look something like this:
40 |
41 | 
42 |
43 | #### Configuration of the block
44 | For each block configured to work in the RTE, there should be additional configuration on the block level to choose block appearance, data models (both content- and settings models), and catalog appearance - just like we know it from the block list and the block grid.
45 |
46 | For the first implementation of this feature, we propose not to include custom backoffice views, as it - compared to the expected usage - will be time-consuming to implement. This could be something to consider for later.
47 |
48 | 
49 |
50 | #### Button in the RTE toolbar
51 | When the Blocks button is enabled in the RTE toolbar configuration, we suggest a button like the one to the right in the mockup below will be available.
52 |
53 | 
54 |
55 | #### Insert block
56 | When clicking on this button, the configured blocks for this instance of the RTE should be selectable in a way similar to the one known from the block list and the block grid.
57 |
58 | 
59 |
60 | #### Edit block
61 | When inserting the block (or clicking on it later) we should show an edit panel to the right where both content and settings can be edited.
62 |
63 | 
64 |
65 | #### Show the block in the RTE
66 | When inserted, we propose to show the block as a bar/box similar to how we show blocks in the block list and the block grid when they have no custom view. Unlike the macros where you can choose to insert it as inline or not, the block will always be shown inline but can of course be controlled simply by adding paragraphs like shown below.
67 |
68 | 
69 |
70 | ## Technical considerations
71 | We could extend the RTE in other ways than with blocks but believe that by building this on top of the block technology, we open up for benefiting from future block improvements while at the same time ensuring a more aligned developer- and editor experience.
72 | For the Content Delivery API, we propose that blocks should be listed in an array in the Json output and then referenced from within the RTE HTML element. While not used in a headless context, a partial view is needed, just like with blocks for the block list and the block grid.
73 |
74 | ## Out of Scope
75 | We hope to introduce global blocks (as described in the RFC for [Reusable Content with Global Blocks]) but that is not part of this RFC, although we believe that blocks in the RTE should also work with global blocks when these are implemented.
76 |
77 | ### Unresolved issues
78 | The primary purpose of this RFC is to ensure that we with this - and other already existing functionality - have covered all use cases that are today handled by macros. Please let us know if you see use cases for macros that are still not solved.
79 | Also, as described above, we propose blocks without custom views as a starting point but let us know if you see that as a problem.
80 |
81 | ## Contributors
82 | This RFC was compiled by:
83 |
84 | - Bjarke Berg (Umbraco HQ)
85 | - Jacob Overgaard (Umbraco HQ)
86 | - Filip Bech-Larsen (Umbraco HQ)
87 | - Rune Strand (Umbraco HQ)
88 | - Lasse Fredslund (Umbraco HQ)
89 | - The CMS Group
90 | - The CMS Community Team
91 |
--------------------------------------------------------------------------------
/cms/0027-the-future-of-search.md:
--------------------------------------------------------------------------------
1 | # The Future of Search
2 |
3 | ### Request for Contribution (RFC) 0027 : The Future of Search
4 |
5 | **Please comment on this RFC in this [RFC Discussion](https://github.com/umbraco/rfcs/discussions/43)**
6 |
7 | ## Code of Conduct
8 |
9 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md).
10 |
11 | ## Intended Audience
12 |
13 | The intended audience for this RFC is
14 |
15 | - Technical users
16 | - Developers
17 | - Extension developers
18 | - Package authors
19 |
20 | ## Summary
21 |
22 | This RFC proposes a new search and indexing abstraction for Umbraco.
23 | This new architecture aims to address limitations with network drive hosting, simplify integrations with advanced search providers, and optimize the index rebuilding process for increased performance.
24 | The proposed abstraction will allow for the use of different search providers, offering greater flexibility and scalability.
25 | It outlines details for both indexing and searching, covering aspects like content variance, protected content, full-text search, filtering, faceting, and an extensible architecture.
26 | This change aims to provide a more robust and performant search experience within Umbraco.
27 |
28 | ## Terminology
29 |
30 | - **Content**: Common denominator for Documents, Media and Members.
31 | - **Search implementation**: An implementation of the search and indexing abstraction for a specific search provider.
32 | - **Search provider**: The underlying search technology for a search implementation (e.g. Elasticsearch).
33 | - **Field**: A container of data in the search index.
34 |
35 | ## Motivation
36 |
37 | For many years, search functionality in Umbraco has relied on Examine, utilizing a standard implementation of Lucene.NET. However, Lucene.NET is known for its issues with hosting on network drives, making deployments on platforms like Azure Web Services particularly challenging. This has required extensive configuration adjustments and has been a frequent source of support cases for Umbraco.
38 |
39 | Additionally, we recognize that many of our partners already use more advanced search providers. As a result, we aim to simplify integrations with these platforms, regardless of the specific technology used.
40 |
41 | Finally, we see an opportunity to optimize the index rebuilding process, enabling it to run more efficiently and faster.
42 |
43 | ## Detailed design
44 |
45 | We plan to introduce a complete new search and indexing abstraction, which will allow for different search providers to be used for searching Umbraco content.
46 |
47 | During our investigation, we have looked into Elasticsearch, Algolia, Meilisearch and Examine (Lucene.NET) as potential search providers.
48 |
49 | We plan the following design, based on the these criteria:
50 |
51 | 1. The abstraction should be easy to use.
52 | 2. It should be possible to implement with any search provider.
53 |
54 | When reading through this, it is important to keep in mind that the search abstraction will cover both backoffice and frontend search. This is the equivalent of what is currently commonly known as search in "internal" and "external" indexes.
55 |
56 | ### Indexing
57 |
58 | We will handling indexing for the search index via cache refreshers.
59 |
60 | This approach allows different search implementations to determine which server roles should perform a task. For all out-of-process search implementations, indexing would only be performed on servers with the `Publisher` role.
61 |
62 | The indexing will support both protected content (public access restrictions) and content variance (culture and segment).
63 |
64 | The indexing will explicitly exclude sensitive data - e.g. property types marked as sensitive.
65 |
66 | Umbraco will be responsible for gathering index data when content changes. This includes the gathering of index data for all relevant related content items - for example:
67 |
68 | - Documents affected by the publishing of an ancestor document.
69 | - Changes made to document protection (public access).
70 |
71 | Subsequently, the index data will be handed off to the search implementation, which is then responsible for updating the search index accordingly.
72 |
73 | #### Required indexes
74 |
75 | We plan on creating four core indexes:
76 |
77 | - **Documents** for end user document search.
78 | - Contains only published document data.
79 | - Replaces the current indexes "external index" and "Delivery API index".
80 | - **Draft Documents** to power the backoffice document search.
81 | - Includes the most recent version of the document. So for a published document with no draft, it will have the published version. For an edited document that has a draft and a published version, the draft will be the one indexed.
82 | - Replaces the current "internal index".
83 | - **Media** to power all media search.
84 | - **Members** to power all member search.
85 |
86 | Note that as of today, media are included in both the "external index" and the "internal index". We want to change this moving forward, as we perceive documents and media as two different things - particularly from a search perspective.
87 |
88 | #### Custom indexes
89 |
90 | It should be possible to define and maintain custom indexes from any Umbraco extension (for example Umbraco Forms).
91 |
92 | #### Storing master data in indexes
93 |
94 | We will _not_ storing master data (commonly known as "stored fields" or "raw fields") within the indexes.
95 |
96 | An index is only meant to be an instrument for querying and resolving IDs for master data records. The actual master data records can subsequently be retrieved from their concrete storage.
97 |
98 | #### Index data per property value
99 |
100 | We will create new property index value factories to generate the property level index data for property editors. This replaces the `IPropertyIndexValueFactory` implementations in the current codebase.
101 |
102 | There will be different property index value factory implementations for different property editors, and they will return a common data format for indexing property data.
103 |
104 | The index data format will include the following:
105 |
106 | - `Texts` (`string[]`)
107 | - `TextsH1` (`string[]`)
108 | - `TextsH2` (`string[]`)
109 | - `TextsH3` (`string[]`)
110 | - `TextsH4` (`string[]`)
111 | - `TextsH5` (`string[]`)
112 | - `TextsH6` (`string[]`)
113 | - `Keywords` (`string[]`)
114 | - `Decimals` (`Decimal[]`)
115 | - `Integers` (`int[]`)
116 | - `DateTimeOffsets` (`DateTimeOffset[]`)
117 |
118 | In this index data format:
119 |
120 | - All `Texts` will be used for full text search.
121 | - The granular division of text types are to be used as "buckets" of text for implementation-specific boosting with a defined order of importance.
122 | - `Keywords`, `Decimals`, `Integers` and `DateTimeOffsets` are meant for filtering and/or faceting.
123 |
124 | The property names in the index data format are likely subject to change, but they will serve as a reference point throughout this RFC.
125 |
126 | We will _not_ provide specifics on how the index data should be indexed (e.g. how to analyze for full text search). We consider this an implementation detail for the individual search implementations, as it varies greatly between providers.
127 |
128 | #### Index data per content item
129 |
130 | All relevant properties of a content item will have their values indexed as per the previous section.
131 |
132 | The property level index data will be passed to the index as individual fields by property type alias, thus allowing for filtering and faceting by property at query time.
133 |
134 | Additionally, the following content level data will be included, using the same index data format:
135 |
136 | - Name (as `TextsH1`)
137 | - Creation date (as `DateTimeOffsets`)
138 | - Update date (as `DateTimeOffsets`)
139 | - Content type alias (as `Keywords`)
140 | - ID (GUID) (as `Keywords`)
141 | - Tags (as `Keywords`)
142 | - All tags accumulated across all property values.
143 | - Ancestor IDs (GUIDs) (as `Keywords`)
144 | - Included to facilitate structural search queries.
145 |
146 | #### Persisting index data in the database
147 |
148 | We propose storing index data in the database, as calculating it can be highly CPU-intensive — particularly for content with deeply nested types, such as block lists within block lists.
149 |
150 | The index data stored in the database is not to be confused with the data stored in the search indexes by the search implementations. Instead, the index data in the database should be considered as the "raw" input for the search implementations.
151 |
152 | By persisting the index data, it only needs to be calculated once per change, even for search implementations that require a search index per machine, such as Examine (Lucene.NET).
153 |
154 | The primary benefit of persisting index data in the database is significantly faster indexing during cold-boot scenarios, where the entire index needs to be rebuilt.
155 |
156 | For regular indexing of individual content items, we anticipate no noticeable difference.
157 |
158 | The only drawback we foresee is an increase in database size by approximately 5-10%, due to this new data.
159 |
160 | ##### Persisted index data format
161 |
162 | The persisted index data will be stored in a new table, similarly to how we store published content today.
163 |
164 | The table will be named `umbracoIndexData` and contain the following columns:
165 |
166 | - `Key`: GUID (primary key)
167 | - `Published`: Bit
168 | - `DataRaw`: Binary (same binary serialization as the published content)
169 |
170 | The `DataRaw` column will contain the serialized index data for all variants of an entire content item.
171 |
172 | #### Indexing protected documents
173 |
174 | _This section applies only to indexing of published documents._
175 |
176 | Protected documents are configured in Umbraco by specifying the concrete members and/or member groups that should have access to the documents.
177 |
178 | When Umbraco hands off protected documents to be indexed by the search implementation, the configured member and member group IDs (GUIDs) will be passed to the search implementation. The search implementation must either:
179 |
180 | - Index the documents accordingly, so they can be made available for the relevant members at query time, or
181 | - Discard the documents, thus not indexing it at all (effectively not supporting protected documents).
182 |
183 | #### Indexing variants of content
184 |
185 | For variant content, Umbraco will append the relevant variance qualifiers (culture and/or segment) to the index data at property level. The search implementation must either:
186 |
187 | - Index the variance qualifiers, so variant content can be found at query time (given a concrete variant context), or
188 | - Discard (parts of) the variance qualifiers and the corresponding property data (e.g. discard segmented property data if not supporting segmented querying).
189 |
190 | #### Full re-indexing versus partial index updates
191 |
192 | Some search providers support zero downtime for full re-indexing - for example by means of "index swapping".
193 |
194 | Umbraco will be handing off either individual content items or collections of content for indexing, but for large sites it will eventually not be possible to hand off _all_ content in a single operation. However, we still want to support zero downtime re-indexing if at all possible.
195 |
196 | This requires us to introduce an instruction protocol for communicating re-indexing start and end. The specifics are yet to be defined, but it will require some queueing mechanism to ensure atomic operations ("transactions") across multiple batches of content indexing.
197 |
198 | #### Extensibility
199 |
200 | We envision an extension model for indexing, which will allow for changing the default behavior (altering, adding and removing index data before it is sent to the search implementation).
201 |
202 | Part of this extension model will be the property index value factories themselves. They will be interchangeable, so custom indexing can be performed for _all_ properties of a given property editor.
203 |
204 | However, this will not cover all cases, so additional extension points will be called for. These are yet to be defined, but _could_ include:
205 |
206 | - An "also index these items" option to include additional content items for re-indexing when changes are made to specific content.
207 | - A "last minute" option to manipulate all index data for a single content item before it is passed to the search implementation for indexing.
208 |
209 | #### Examples of index data
210 |
211 | See [Appendix A](#appendix-a-example-of-index-data-being-gathered-for-indexing) for a detailed example of index data being gathered for indexing.
212 |
213 | ### Searching
214 |
215 | We are going to defining a search abstraction that covers:
216 |
217 | - Full text search
218 | - Filtering
219 | - Faceting (including range facets)
220 | - Autocomplete/suggestions
221 | - Variance (as defined by Umbraco, by culture and/or segment)
222 | - Protected content (as defined by Umbraco, by concrete principal or group membership)
223 | - Sorting and pagination
224 |
225 | Full text search, filtering and faceting can be used in conjunction with one another.
226 |
227 | The goal is to cover the most common use cases, not to define an exhaustive search abstraction for all search providers. For concrete search provider features outside the scope of the search abstraction, implementors are expected to utilize the search provider directly instead of going through this abstraction.
228 |
229 | #### Example of the search abstraction
230 |
231 | See [Appendix B](#appendix-b-code-example-for-the-search-abstraction) for a code example of what the search abstraction signature _could_ look like.
232 |
233 | #### Full text search
234 |
235 | Full text search will be defined as:
236 |
237 | - A query (`string`) containing one or more words to search for.
238 | - An operator (`enum`) specifying if the words in the query should be matched as `OR` or `AND`.
239 |
240 | The search should be performed across the `Texts` from the property level index data.
241 |
242 | Relevant boosting should be applied for the individual `Texts` data, e.g. ensuring higher search result relevance for data from `TextsH1` than `TextsH4` (where `Texts` is considered of the least relevance value).
243 |
244 | We do _not_ plan to include detailed boosting levels as part of the search abstraction. We consider this an implementation detail for the individual search implementations.
245 |
246 | #### Filtering
247 |
248 | Filtering allows for narrowing down the search results by exact matching against the following from the property level index data:
249 |
250 | - `Keywords`: For exact matches on text.
251 | - `Decimals`: For exact matches on decimal values.
252 | - `Integers`: For exact matches on integer values.
253 | - `DateTimeOffsets`: For exact matches on date and time.
254 |
255 | The abstraction will support:
256 | - Filtering against specific fields, thus narrowing down by content property value.
257 | - For example: The "color" property should have the value "Red".
258 | - Chaining filters in a single query. Filters will be combined with an AND.
259 | - For example: ("color" must be "Red") AND ("price" must be less than 100).
260 | - Providing multiple values per filter. Filter values will be combined with an OR.
261 | - For example: ("color" must be "Red" OR "Blue") AND ("size" must be "M" OR "L").
262 |
263 | We propose splitting filtering into two parts:
264 |
265 | - **Exact filtering** for concrete filter values.
266 | - For example: "color" is "Red" OR "Blue".
267 | - **Range filtering** for range bound filter values.
268 | - For example: "price" is between 100 AND 200.
269 |
270 | #### Faceting
271 |
272 | From a search abstraction point of view, faceting works in much the same way as filtering; faceting can be performed against the same fields as filtering, and follows the same combination rules.
273 |
274 | Faceting yields the same content results as filtering, but adds facet values to the search result.
275 |
276 | From a search implementation perspective, faceting likely works very differently from filtering, and might incur a performance penalty over filtering.
277 |
278 | We propose splitting faceting into two parts:
279 |
280 | - **Exact faceting** for concrete facet values.
281 | - For example: "color" is "Red" OR "Blue".
282 | - **Range faceting** for range bound facet values.
283 | - For example: "price" is between 100 AND 200.
284 |
285 | #### Autocomplete/suggestions
286 |
287 | We plan to include an optional "suggested next query" in the search results. This can be used by the search implementation to supply options for autocompletion or query suggestions for a given query.
288 |
289 | The "suggested next query" will be a collection of strings, meant to replace or complement the active full text search query (if any).
290 |
291 | #### Variance
292 |
293 | Given a concrete variation context (culture and/or segment), the search implementation should be able to yield content results relevant to this context.
294 |
295 | See the indexing section for more details.
296 |
297 | #### Protected content
298 |
299 | Given the context of a "current principal" (by means of a concrete principal identifier and/or group memberships), the search implementation should be able to include relevant protected content in search results.
300 |
301 | See the indexing section for more details.
302 |
303 | #### Search result
304 |
305 | The search implementation should produce search results that contain:
306 |
307 | - The total number of results.
308 | - The IDs (GUIDs) of the content items within the current result page.
309 | - Matching facets, if any have been requested.
310 | - A collection of "suggested next queries", if any have been requested.
311 |
312 | The search abstraction will resolve the actual content items for results generated by the search implementation - for example, retrieve matching published documents from the cache.
313 |
314 | #### Extensibility
315 |
316 | We also envision an extension model for searching. The details are yet to be defined, but _will likely_ as a minimum include a means to intercept and perform custom handling for all search results.
317 |
318 | ### Graceful degradation
319 |
320 | We expect the search and indexing abstraction to be able to support graceful degradation for unsupported features of a particular search implementation.
321 |
322 | For example, the search implementation might not support:
323 |
324 | - Protected content.
325 | - Segmented content.
326 | - Faceting.
327 | - Autocomplete.
328 | - ...
329 |
330 | It is yet to be determined if this can be covered by search implementation documentation alone, or if we need to include code level support for graceful degradation as well (e.g. marker interfaces like `ISupportFaceting`).
331 |
332 | ### Configuration
333 |
334 | We have not identified any candidates for configuration of the search and indexing abstraction.
335 |
336 | The individual search implementations may allow for search provider specific configuration, but this is considered an implementation detail for this RFC.
337 |
338 | ## Out of Scope
339 |
340 | We plan to ship a single implementation of the search abstraction, which will be based on Examine to be backward compatible. We will not implement other search providers for the initial release.
341 |
342 | The following index values have been identified as potential candidates for future extension, but will not be included in the initial release:
343 |
344 | - `GeoPoints` (`GeoPoint[]`)
345 | - `Dates` (`DateOnly[]`)
346 | - `Times` (`TimeOnly[]`)
347 | - `Booleans` (`bool[]` or maybe `string[]`)
348 | ## Contributors
349 |
350 | This RFC was compiled by:
351 |
352 | - Bjarke Berg (Umbraco HQ)
353 | - Kenn Jacobsen (Umbraco HQ)
354 |
355 | ## Appendix A: Example of index data being gathered for indexing.
356 |
357 | As mentioned, a property editor defines its own property index value factory for generating values for indexing. The following exemplifies how this will work.
358 |
359 | In this example, we are indexing a document with:
360 |
361 | - Name: Hitchhiker's Guide
362 | - Document type alias: bookPage
363 | - Created at: 2024-11-27 12:00:00
364 | - Last updated at: 2024-11-27 14:30:00
365 | - ID: 0e88ec21-dc56-4f8c-8247-40f72b2fd676
366 | - Ancestor IDs: [33d41c8b-f541-4237-96ed-c5c85d4c3edd, 5ce303c9-8e0f-41cd-80d8-3f7906604e6a]
367 |
368 | ...and the following properties:
369 |
370 |
371 |
372 | Alias |
373 | Type |
374 | Value |
375 |
376 |
377 | title |
378 | Umbraco.TextBox |
379 | The Hitchhiker's Guide to the Galaxy |
380 |
381 |
382 | featured |
383 | Umbraco.Toggle |
384 | true |
385 |
386 |
387 | pages |
388 | Umbraco.Integer |
389 | 255 |
390 |
391 |
392 | published |
393 | Umbraco.DateTime |
394 | 1979-10-12 |
395 |
396 |
397 | isbn13 |
398 | Umbraco.TextBox |
399 | 9780330258647 |
400 |
401 |
402 | genre |
403 | Umbraco.Tags |
404 | [Science Fiction, Comedy] |
405 |
406 |
407 | subjects |
408 | Umbraco.MultipleTextstring |
409 | [Travel, Philosophy, Towels] |
410 |
411 |
412 | price |
413 | Umbraco.Decimal |
414 | 119.95 |
415 |
416 |
417 | score |
418 | Umbraco.Slider |
419 | [7.8, 9.3] |
420 |
421 |
422 | abstract |
423 | Umbraco.RichText |
424 |
425 |
426 | <h1>A brilliant read</h1>
427 | <p>This is the first book.</p>
428 | <umb-rte-block (RTE block 1)/>
429 | <h2>Science Fiction</h2>
430 | <p>A trilogy of five books.</p>
431 |
432 |
433 | RTE block 1 (element type alias: relatedBook)
434 |
435 |
436 | Alias |
437 | Type |
438 | Value |
439 |
440 |
441 | heading |
442 | Umbraco.TextBox |
443 | Fancy a bite? |
444 |
445 |
446 | text |
447 | Umbraco.TextArea |
448 | Check The Restaurant at the End of the Universe |
449 |
450 |
451 | tags |
452 | Umbraco.Tags |
453 | [Related, Sequel] |
454 |
455 |
456 | subjects |
457 | Umbraco.MultipleTextstring |
458 | [Food, Restaurant, Universe] |
459 |
460 |
461 | |
462 |
463 |
464 | blocks |
465 | Umbraco.BlockList |
466 |
467 | Block 1 (element type alias: characterBlock)
468 |
469 |
470 | Alias |
471 | Type |
472 | Value |
473 |
474 |
475 | title |
476 | Umbraco.TextBox |
477 | Arthur Dent |
478 |
479 |
480 | text |
481 | Umbraco.RichText |
482 |
483 |
484 | <h1>Earthman and Englishman</h1>
485 | <p>Arthur is mostly harmless.</p>
486 |
487 | |
488 |
489 |
490 | image |
491 | Umbraco.MediaPicker3 |
492 | 4414b7e4-7d41-45b2-9e41-d2085ed995bf |
493 |
494 |
495 | character |
496 | Umbraco.DropDown.Flexible |
497 | Main |
498 |
499 |
500 | Block 2 (element type alias: teaserBlock)
501 |
502 |
503 | Alias |
504 | Type |
505 | Value |
506 |
507 |
508 | heading |
509 | Umbraco.TextBox |
510 | Like poems? |
511 |
512 |
513 | text |
514 | Umbraco.TextArea |
515 | Be careful with Vogon poetry! |
516 |
517 |
518 | |
519 |
520 |
521 |
522 | The resulting index data is listed in the table below.
523 |
524 | Note that "system fields" (like document name and creation date) are included here with an "umb_" prefix. The final prefix is yet to be decided, but there _will_ be a prefix to tell system fields apart from regular document properties.
525 |
526 |
527 |
528 | Name |
529 | Value |
530 | Remarks |
531 |
532 |
533 | umb_type |
534 | Keywords: ["bookPage"] |
535 | The document type alias is indexed as a keyword. |
536 |
537 |
538 | umb_name |
539 | TextsH1: ["Hitchhiker's Guide"] |
540 | The document name is indexed as the most significant headline. |
541 |
542 |
543 | umb_createDate |
544 | DateTimesOffsets: ["2024-11-27 12:00:00"] |
545 | |
546 |
547 |
548 | umb_updateDate |
549 | DateTimeOffsets: ["2024-11-27 14:30:00"] |
550 | |
551 |
552 |
553 | umb_tags |
554 | Keywords: ["Related", "Sequel", "Science Fiction", "Comedy"] |
555 | All tags on the page (including those in nested property editors) are indexed as individual keywords. |
556 |
557 |
558 | umb_id |
559 | Keywords: ["0e88ec21-dc56-4f8c-8247-40f72b2fd676"] |
560 | |
561 |
562 |
563 | umb_ancestors |
564 | Keywords: ["33d41c8b-f541-4237-96ed-c5c85d4c3edd", "5ce303c9-8e0f-41cd-80d8-3f7906604e6a"] |
565 | |
566 |
567 |
568 | title |
569 | Texts: ["The Hitchhiker's Guide to the Galaxy"] |
570 | Raw text inputs are indexed as lowest value text. |
571 |
572 |
573 | pages |
574 | Integers: [255] |
575 | |
576 |
577 |
578 | published |
579 | DateTimeOffsets: ["1979-10-12"] |
580 | |
581 |
582 |
583 | isbn13 |
584 | Texts: ["9780330258647"] |
585 | |
586 |
587 |
588 | genre |
589 | Keywords: ["Science Fiction", "Comedy"] |
590 | Tags are indexed as keywords. |
591 |
592 |
593 | subjects |
594 | Texts: ["Travel", "Philosophy", "Towels"] |
595 | Multiple textstrings are indexed as individual items (lowest value text). |
596 |
597 |
598 | price |
599 | Decimals: [119.95] |
600 |
601 | |
602 |
603 | score |
604 | Decimals: [7.8, 9.3] |
605 | |
606 |
607 |
608 | abstract |
609 |
610 |
611 | - TextsH1: ["A brilliant read"]
612 | - TextsH2: ["Science Fiction"]
613 | - Texts: ["This is the first book.", "A trilogy of five books.", "Fancy a bite?", "Check The Restaurant at the End of the Universe", "Food", "Restaurant", "Universe"]
614 | - Keywords: ["Related", "Sequel"]
615 |
616 | |
617 |
618 |
619 | - The RTE takes headings into account, e.g. indexing H1 tags highest value texts.
620 | - Block properties are indexed as part of the RTE, adhering to their own indexing rules (i.e. tags become keywords).
621 | - Block document type aliases are not indexed.
622 |
623 | |
624 |
625 |
626 | blocks |
627 |
628 |
629 | - TextsH1: ["Earthman and Englishman"]
630 | - Texts: ["Arthur Dent", "Arthur is mostly harmless.", "Like poems?", "Be careful with Vogon poetry!"]
631 | - Keywords: ["Main"]
632 |
633 | |
634 |
635 |
636 | - The media picker is not indexed.
637 | - Fixed value selectors (dropdowns, radiobutton groups etc.) are indexed as keywords.
638 |
639 | |
640 |
641 |
642 |
643 | The following table shows an example of how the default index data could be altered by means of extension points.
644 |
645 |
646 |
647 | Name |
648 | Value |
649 | Changes from default |
650 |
651 |
652 | umb_type |
653 | Keywords: ["bookPage"] |
654 | |
655 |
656 |
657 | umb_name |
658 | TextsH3: ["Hitchhiker's Guide"] |
659 | The document name has been given lesser text value (from TextsH1 to TextsH3). |
660 |
661 |
662 | umb_createDate |
663 | DateTimesOffsets: ["2024-11-27 12:00:00"] |
664 | |
665 |
666 |
667 | umb_updateDate |
668 | DateTimeOffsets: ["2024-11-27 14:30:00"] |
669 | |
670 |
671 |
672 | umb_tags |
673 | Keywords: ["Related", "Sequel", "Science Fiction", "Comedy", "Robots"] |
674 | An extra tag ("Robots") has been added to the collection of page tags. |
675 |
676 |
677 | umb_id |
678 | Keywords: ["0e88ec21-dc56-4f8c-8247-40f72b2fd676"] |
679 | |
680 |
681 |
682 | umb_ancestors |
683 | Keywords: ["33d41c8b-f541-4237-96ed-c5c85d4c3edd", "5ce303c9-8e0f-41cd-80d8-3f7906604e6a"] |
684 | |
685 |
686 |
687 | title |
688 | TextsH4: ["The Hitchhiker's Guide to the Galaxy"] |
689 | The property value has been moved to a higher value text (from Texts to TextsH4). |
690 |
691 |
692 | featured |
693 | Keywords: ["featured"] |
694 | The toggle (boolean) is omitted by default, but has been included as a keyword here. |
695 |
696 |
697 | pages |
698 | Integers: [255] |
699 | |
700 |
701 |
702 | published |
703 | DateTimeOffsets: ["1979-10-12"] |
704 | |
705 |
706 |
707 | isbn13 |
708 | Keywords: ["9780330258647", "0330258648"] |
709 | The ISBN 13 has been turned into a keyword, and the corresponding ISBN 10 has been added. |
710 |
711 |
712 | genre |
713 | Keywords: ["Science Fiction", "Comedy"] |
714 | |
715 |
716 |
717 | subjects |
718 | Texts: ["Travel", "Philosophy", "Towels"] |
719 | |
720 |
721 |
722 | price |
723 | Decimals: [119.95] |
724 | |
725 |
726 |
727 | score |
728 | Decimals: [7.8, 9.3] |
729 | |
730 |
731 |
732 | abstract |
733 |
734 |
735 | - TextsH1: ["A brilliant read", "Fancy a bite?"]
736 | - TextsH2: ["Science Fiction"]
737 | - Texts: ["This is the first book.", "A trilogy of five books.", "Check The Restaurant at the End of the Universe", "Food", "Restaurant", "Universe"]
738 | - Keywords: ["Related", "Sequel"]
739 |
740 | |
741 | The "heading" property value from the block has been moved to higher value text (from Texts to TextsH1). |
742 |
743 |
744 | blocks |
745 |
746 |
747 | - TextsH1: ["Earthman and Englishman"]
748 | - TextsH4: ["Arthur Dent"]
749 | - Texts: ["Arthur is mostly harmless.", "Like poems?", "Be careful with Vogon poetry!", "Portrait of Author Dent in front of a spaceship"]
750 | - Keywords: ["Main"]
751 |
752 | |
753 |
754 |
755 | - The "title" property value from the block has been moved to higher value text (from Texts to TextsH4).
756 | - The media picker ALT text (a property value from the picked media) has been added to Texts ("Portrait of...").
757 |
758 | |
759 |
760 |
761 |
762 | ## Appendix B: Code example for the search abstraction
763 |
764 | The following is an example of what the search abstraction signature _could_ look like.
765 |
766 | This is not necessarily how the final version will be implemented. It is meant as a supplement to illustrate the points made in this RFC.
767 |
768 | ```csharp
769 | public PagedSearchResult Search(
770 | VariantFilter variantFilter,
771 | FullTextFilter? fullTextFilter = null,
772 | IEnumerable? filters = null,
773 | IEnumerable? facets = null,
774 | PrincipalFilter? principalFilter = null,
775 | Suggest? suggest = null,
776 | Sort? sort = null,
777 | int skip = 0,
778 | int take = 10
779 | )
780 | {
781 | // ...
782 | }
783 |
784 | public class VariantFilter
785 | {
786 | // nulls means invariant
787 | public string? Culture { get; set; } // null = invariant
788 |
789 | // null means default (no) segment
790 | public string? Segment { get; set; }
791 | }
792 |
793 | public enum FullTextFilterQueryOperator
794 | {
795 | Or,
796 | And
797 | }
798 |
799 | public class FullTextFilter
800 | {
801 | // query to be analyzed
802 | public required string Query { get; set; }
803 |
804 | // operator to use between the words in the query
805 | public FullTextFilterQueryOperator Operator { get; set; } = FullTextFilterQueryOperator.Or;
806 | }
807 |
808 | public static class SystemFields
809 | {
810 | public const string Name = "umb_name";
811 | public const string Id = "umb_id";
812 | public const string CreateDate = "umb_created";
813 | public const string UpdateDate = "umb_updated";
814 | public const string ContentType = "umb_type";
815 | public const string Ancestors = "umb_ancestors";
816 | }
817 |
818 | public enum FilterOperator
819 | {
820 | Is,
821 | IsNot
822 | }
823 |
824 | public abstract class Filter
825 | {
826 | // the index field name (system field or property alias)
827 | public required string Field { get; set; }
828 | }
829 |
830 | public abstract class ExactFilter : Filter
831 | {
832 | // operator to use against the filter values
833 | public FilterOperator Operator { get; set; } = FilterOperator.Is;
834 |
835 | // the filter values (OR filtering applies if multiple values are specified)
836 | public required IEnumerable Values { get; set; }
837 | }
838 |
839 | public class StringExactFilter : ExactFilter {}
840 |
841 | public class IntegerExactFilter : ExactFilter {}
842 |
843 | public class DecimalExactFilter : ExactFilter {}
844 |
845 | public class DateTimeOffsetExactFilter : ExactFilter {}
846 |
847 | public class GuidExactFilter : ExactFilter {}
848 |
849 | public abstract class RangeFilter : Filter
850 | {
851 | // null means "no lower bounds"
852 | public T? MinValue { get; set; }
853 |
854 | // null means "no upper bounds"
855 | public T? MaxValue { get; set; }
856 | }
857 |
858 | public class IntegerRangeFilter : RangeFilter {}
859 |
860 | public class DecimalRangeFilter : RangeFilter {}
861 |
862 | public class DateTimeOffsetRangeFilter : RangeFilter {}
863 |
864 | public abstract class Facet
865 | {
866 | // the index field name (system field or property alias)
867 | public required string Field { get; set; }
868 | }
869 |
870 | public abstract class ExactFacet : Facet
871 | {
872 | // the facet values to filter by (OR filtering applies if multiple values are specified)
873 | public required IEnumerable Values { get; set; }
874 | }
875 |
876 | public class StringExactFacet : ExactFacet {}
877 |
878 | public class IntegerExactFacet : ExactFacet {}
879 |
880 | public class DecimalExactFacet : ExactFacet {}
881 |
882 | public class DateTimeOffsetExactFacet : ExactFacet {}
883 |
884 | public abstract class RangeFacet : Facet
885 | {
886 | // null means "no lower bounds"
887 | public T? MinValue { get; set; }
888 |
889 | // null means "no upper bounds"
890 | public T? MaxValue { get; set; }
891 | }
892 |
893 | public class IntegerRangeFacet : RangeFacet {}
894 |
895 | public class DecimalRangeFacet : RangeFacet {}
896 |
897 | public class DateTimeOffsetRangeFacet : RangeFacet {}
898 |
899 | public class PrincipalFilter
900 | {
901 | // the principal identifier
902 | public required Guid Id { get; set; }
903 |
904 | // the group memberships of the principal
905 | public required IEnumerable Groups { get; set; }
906 | }
907 |
908 | public enum SortDirection
909 | {
910 | Ascending = 0,
911 | Descending = 1,
912 | }
913 |
914 | public class Suggest
915 | {
916 | // whether to include "suggested next queries" in the search result
917 | public bool SuggestNextQuery { get; set; } = false;
918 | }
919 |
920 | public class Sort
921 | {
922 | public SortDirection Direction { get; set; } = SortDirection.Descending;
923 |
924 | // the index field name (system field or property alias) - null means default (relevance)
925 | public string? Key { get; set; } = null;
926 | }
927 |
928 | public class PagedSearchResult
929 | {
930 | public int TotalCount { get; set; }
931 |
932 | public required IEnumerable Results { get; set; }
933 |
934 | public IEnumerable? FacetResults { get; set; }
935 |
936 | public IEnumerable? SuggestedNextQuery { get; set; }
937 | }
938 |
939 | public class SearchResult
940 | {
941 | public required Guid Id { get; set; }
942 | }
943 |
944 | public abstract class FacetResult
945 | {
946 | // the index field name (system field or property alias) - null means default (relevance)
947 | public required string Field { get; set; }
948 | }
949 |
950 | public abstract class ExactFacetResult : FacetResult
951 | {
952 | // the available facet values for the query results
953 | public required IEnumerable> Values { get; set; }
954 | }
955 |
956 | public abstract class ExactFacetResultValue
957 | {
958 | // the value in the index
959 | public required T Value { get; set; }
960 |
961 | // number of hits for this facet value
962 | public required int Count { get; set; }
963 | }
964 |
965 | public class StringExactFacetResult : ExactFacetResult {}
966 |
967 | public class IntegerExactFacetResult : ExactFacetResultValue {}
968 |
969 | public class DecimalExactFacetResult : ExactFacetResultValue {}
970 |
971 | public class DateTimeOffsetExactFacetResult : ExactFacetResultValue {}
972 |
973 | public abstract class RangeFacetResult : FacetResult
974 | {
975 | // the lower bounds in the index
976 | public T? MinValue { get; set; }
977 |
978 | // the upper bounds in the index
979 | public T? MaxValue { get; set; }
980 | }
981 |
982 | public class IntegerRangeFacetResult : RangeFacetResult {}
983 |
984 | public class DecimalRangeFacetResult : RangeFacetResult {}
985 |
986 | public class DateTimeOffsetRangeFacetResult : RangeFacetResult {}
987 | ```
988 |
989 |
--------------------------------------------------------------------------------
/cms/assets/GridStyleexamples1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/GridStyleexamples1.jpg
--------------------------------------------------------------------------------
/cms/assets/GridStyleexamples2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/GridStyleexamples2.jpg
--------------------------------------------------------------------------------
/cms/assets/GridStyleexamples3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/GridStyleexamples3.jpg
--------------------------------------------------------------------------------
/cms/assets/GridStyleexamples4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/GridStyleexamples4.jpg
--------------------------------------------------------------------------------
/cms/assets/GridStyleexamples5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/GridStyleexamples5.jpg
--------------------------------------------------------------------------------
/cms/assets/blocks-in-the-rte/Content-in-the-rte.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/blocks-in-the-rte/Content-in-the-rte.png
--------------------------------------------------------------------------------
/cms/assets/blocks-in-the-rte/Edit-content.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/blocks-in-the-rte/Edit-content.png
--------------------------------------------------------------------------------
/cms/assets/blocks-in-the-rte/Insert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/blocks-in-the-rte/Insert.png
--------------------------------------------------------------------------------
/cms/assets/blocks-in-the-rte/RTE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/blocks-in-the-rte/RTE.png
--------------------------------------------------------------------------------
/cms/assets/blocks-in-the-rte/Settings-block-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/blocks-in-the-rte/Settings-block-config.png
--------------------------------------------------------------------------------
/cms/assets/blocks-in-the-rte/Settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/blocks-in-the-rte/Settings.png
--------------------------------------------------------------------------------
/cms/assets/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/diagram.png
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - Add block.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - Add block.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - Edit block.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - Edit block.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - Make local.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - Make local.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - block grid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - block grid.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - clean.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - clean.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - make global.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - make global.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - publish.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - publish.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Content - splitview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Content - splitview.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Library - Preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Library - Preview.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Library - clean.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Library - clean.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Library - info.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Library - info.jpg
--------------------------------------------------------------------------------
/cms/assets/global-blocks/Library - language.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/global-blocks/Library - language.jpg
--------------------------------------------------------------------------------
/cms/assets/project-structure-Current.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/project-structure-Current.png
--------------------------------------------------------------------------------
/cms/assets/project-structure-Proposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umbraco/rfcs/eb744bcd37175e51bff72a24af247300cc291925/cms/assets/project-structure-Proposed.png
--------------------------------------------------------------------------------
/docs/0009-rfc-styleguide.md:
--------------------------------------------------------------------------------
1 | # RFC Style Guide for Documentation
2 |
3 | Request for Contribution (RFC) 0009 : _Style guide for documentation_
4 |
5 | ## Code of Conduct
6 |
7 | Please read and respect the [RFC Code of Conduct](https://github.com/umbraco/rfcs/blob/master/CODE_OF_CONDUCT.md)
8 |
9 | ## Intended Audience
10 |
11 | The intended audience for this RFC is:
12 |
13 | * Documentation contributors
14 | * HQ developers
15 |
16 | ## Summary
17 |
18 | What kind of voice, language and punctuation rules should 'guide' contribution to the Umbraco documentation.
19 |
20 | ## Motivation
21 |
22 | * Provide a consistent writing style and tone of voice to improve readability and ultimately comprehension of information.
23 | * Make first-time contributors feel more confident on the approach to creating documentation.
24 | * Help curators evaluate contributions consistently.
25 |
26 | ## Detailed Design
27 |
28 | ### Usage of language
29 |
30 |
31 |
32 | The audience for Umbraco documentation includes many where English is not their native language. Therefore documentation should be written with an international audience in mind. The used language should be simple and easy to understand.
33 |
34 |
35 |
36 | If in doubt, the document [How to write plain English](http://www.plainenglish.co.uk/files/howto.pdf) from plainenglish.co.uk, gives a good idea on what we expect.
37 |
38 | In summary:
39 |
40 | * Use short words
41 | * Use everyday English
42 | * Keep sentence length down
43 | * Prefer active verbs
44 | * Use lists where appropriate
45 | * Annotate screenshots for accessibility
46 |
47 | #### Tone of voice
48 |
49 | Language should be friendly and direct. But not intimidating.
50 | You can address the reader if you are telling a story, eg as part of a Tutorial - but should avoid this in most parts of the documentation.
51 |
52 | Where possible documentation should be gender neutral.
53 |
54 | Not all humor/humour translates, not all cultures have the same sense of humor or will understand sarcasm in the same way, therefore documentation should be written neutrally.
55 |
56 | There are different types of documentation, reference, tutorial etc - leading by 'code examples' is an international approach.
57 |
58 | Unless writing a tutorial, we should try to avoid... "you then have to do this, then set this, and then you do this". Try to avoid addressing the reader as 'you' - focus the writing on the words which describe the steps or details of the documentation.
59 | Tutorials are slightly different, here the language needs to take the reader on a journey through the tutorial.
60 |
61 | #### Words to avoid
62 |
63 | Categories of words we like to avoid are:
64 |
65 |
66 |
67 | * Negative words (like swearing) or words which are known to have a negative context.
68 | * Words can make the user feel stupid, therefore try avoiding the following connecting words: simply, just, of course and obviously.
69 |
70 |
71 |
72 | ### Layout rules
73 |
74 | * Making pages follow a consistent layout will mean regular visitors to the documentation will learn the conventions, and improve the experience of interacting with the documentation.
75 | * Consider different 'journeys' to access documentation - introduction, deep dive or quick reference lookup.
76 | * Embedding of screenshots - consistent annotation approach.
77 | * Informative Tips and Warnings - styled and delivered consistently.
78 |
79 | ### Navigation
80 |
81 | * Clear language in navigation structure, leading visitors through the documentation.
82 | * Landing pages for sections and topics.
83 |
84 | #### Code examples
85 |
86 | Make sure the examples are marked appropriately in order to have correct syntax highlighting.
87 |
88 | * Examples should not contain foreign languages for variables.
89 | * Try to adhere to the [.editorconfig](https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.editorconfig) style rules from the Umbraco-CMS repository.
90 | * Code examples should not contain any commercial references like company names other than Umbraco.
91 |
92 | #### Tutorial markup
93 |
94 | To ensure easy to follow tutorials the following styles will be required in any written text of the tutorial:
95 |
96 | - Use _**Bold Italic**_ for specific terms. Examples: Content Node; Property Editor; Document Type
97 | - Use "Exact Text" to indicate UI items for when they need to click on it or when you are referencing them
98 | - Use "Input value" for things you want readers to type into inputs
99 | - Use `Inline code` for names of properties, methods, namespaces, etc.
100 |
101 | ### Method of enforcement
102 |
103 | With the tooling like [Vale](https://errata-ai.github.io/vale/) we are able to enforce rules. Contributors should be able to checkout the documentation repository for the Our documentation and check whether or not it can pass the enforced rules.
104 |
105 | The Github repo could have (currently in development) a health check on every commit to be sure that commit adheres to the rules.
106 |
107 | In addition to automated tooling, the documentation curator team will use the style guide when reviewing documentation additions.
108 |
109 | #### Suggested Vale rules
110 |
111 | We have set up some suggested rules based on best practises and limiting the impact they will have for contributors:
112 |
113 |
114 |
115 |
116 | - **HeadingsPunctuation**: Warns you when using punctuation at the end of a heading
117 | - **Hyperbolic**: Warns you when using words such as _simple, just, easily and actually_
118 | - **ListStart**: Warns you when starting a list option with lower case
119 | - **SentenceLength**: Warns you when writing sentences with more than 40 words in them
120 | - **Spacing**: Warns you when having more than 1 space in succession in an article
121 | - **Terms**: Warns you when using a blacklisted term and suggests a replacement - could be from _back-office_ to _backoffice_
122 |
123 |
124 |
125 |
126 | ## Drawbacks
127 |
128 | * It might be intimidating if contributors try to submit documentation and receive errors after submitting a PR.
129 | * It might be demotivating if the errors appear to be 'trivial' to the contributor - particularly if the contributor is providing information that is completely missing from the documentation. We don't want the reputation that you have to jump through hoops to contribute - and so how the guide is applied is therefore important.
130 | * It might be off-putting for people where English is not their native language.
131 | * The rules might be too strict - and automated issues are flagged with parts of the contribution that are technical terms e.g. errors with code examples.
132 | * It might be limiting if there are too many rules to adhere to.
133 |
134 | ## Alternatives
135 |
136 | There are currently no alternatives... other than continuing without a style guide and manually reviewing submissions for perceived quality.
137 |
138 | ## Out of Scope
139 |
140 | * Discussing tooling, this is an RFC about the content of a style guide.
141 |
142 | ## Unresolved Issues
143 |
144 | The answers that we are hoping to get from the community & Umbraco HQ is:
145 |
146 | * Is a style guide a good idea?
147 | * What sort of rules should be enforced?
148 | * What rules should not be enforced?
149 | * If multi-lingual documentation is introduced how could style guides be managed per language?
150 | * Would having a style guide make a difference to whether you would be more or less likely to contribute to the community documentation?
151 |
152 | ## Related RFCs
153 |
154 | * There are no related RFCs
155 |
156 | ## Contributors
157 |
158 | This RFC was compiled by:
159 |
160 | * Jeavon Leopold
161 | * Lotte Pitcher
162 | * Jesper Mayntzhusen
163 | * Damiaan Peeters
164 | * Marc Goodson
165 | * Sofie Toft Kristensen
166 |
--------------------------------------------------------------------------------