diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..c62ad1c494 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,45 @@ +# [Choice] .NET Core version: 5.0, 3.1, 2.1 +ARG VARIANT=3.1 +FROM mcr.microsoft.com/vscode/devcontainers/dotnetcore:0-${VARIANT} + +# [Option] Install Node.js +ARG INSTALL_NODE="true" +ARG NODE_VERSION="lts/*" +RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Option] Install Azure CLI +ARG INSTALL_AZURE_CLI="false" +COPY library-scripts/azcli-debian.sh /tmp/library-scripts/ +RUN if [ "$INSTALL_AZURE_CLI" = "true" ]; then bash /tmp/library-scripts/azcli-debian.sh; fi \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts + +# Install SQL Tools: SQLPackage and sqlcmd +COPY mssql/installSQLtools.sh installSQLtools.sh +RUN bash ./installSQLtools.sh \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts + +# Update args in docker-compose.yaml to set the UID/GID of the "vscode" user. +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then groupmod --gid $USER_GID vscode && usermod --uid $USER_UID --gid $USER_GID vscode; fi + + +# [Optional] Uncomment this section to install additional OS packages. +# Following added by Warren... +# Needed to add as Gifsicle used by gulp-imagemin does not ship a Linux binary and has to be compiled from source +# And this Linux package is needed in order to build it +# https://github.com/imagemin/imagemin-gifsicle/issues/40#issuecomment-616487214 +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends dh-autoreconf chromium-browser + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 + +# Following added by Warren... +# Sets the global user for npm as 'root' due to some permission errors for gulp-imagemin creating binaries +# https://stackoverflow.com/a/45505787 +# +# Needing to set unsafe-perm as root is the user setup +# https://docs.npmjs.com/cli/v6/using-npm/config#unsafe-perm +# Default: false if running as root, true otherwise (we are ROOT) +RUN npm -g config set user vscode && npm -g config set unsafe-perm \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..c1c120d0cf --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,61 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.158.0/containers/dotnet-mssql +{ + "name": "C# (.NET) and MS SQL", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspace", + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "mssql.connections": [ + { + "server": "localhost,1433", + "database": "", + "authenticationType": "SqlLogin", + "user": "sa", + "password": "P@ssw0rd", + "emptyPasswordInput": false, + "savePassword": false, + "profileName": "mssql-container" + } + ], + "omnisharp.defaultLaunchSolution": "umbraco-netcore-only.sln", + "omnisharp.enableDecompilationSupport": true, + "omnisharp.enableRoslynAnalyzers": true + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-dotnettools.csharp", + "ms-mssql.mssql" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // 1433 for SQL if you want to connect from local into the one running inside the container + // Can connect to the SQL Server running in the image on local with 'host.docker.internal' as hostname + "forwardPorts": [1433, 9000, 5000, 25], + + // [Optional] To reuse of your local HTTPS dev cert: + // + // 1. Export it locally using this command: + // * Windows PowerShell: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // * macOS/Linux terminal: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // + // 2. Uncomment these 'remoteEnv' lines: + // "remoteEnv": { + // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", + // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", + // }, + // + // 3. Next, copy your certificate into the container: + // 1. Start the container + // 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer + // 3. Open a terminal in VS Code and run "mkdir -p /home/vscode/.aspnet/https && mv aspnetapp.pfx /home/vscode/.aspnet/https" + + // postCreateCommand.sh parameters: $1=SA password, $2=dacpac path, $3=sql script(s) path + "postCreateCommand": "bash .devcontainer/mssql/postCreateCommand.sh 'P@ssw0rd' './bin/Debug/' './.devcontainer/mssql/'" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000000..17312c6bd8 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,115 @@ +version: '3' + +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + # [Choice] Update 'VARIANT' to pick a .NET Core version: 2.1, 3.1, 5.0 + VARIANT: 5.0 + # Options + INSTALL_NODE: "true" + NODE_VERSION: "lts/*" + INSTALL_AZURE_CLI: "false" + # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. + USER_UID: 1000 + USER_GID: 1000 + + volumes: + - ..:/workspace:cached + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + + # Uncomment the next line to use a non-root user for all processes. + # user: vscode + + # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + # DotNetCore ENV Variables + # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#environment-variables + environment: + - ConnectionStrings__umbracoDbDSN=server=localhost;database=UmbracoUnicore;user id=sa;password='P@ssw0rd' + - Umbraco__CMS__Unattended__InstallUnattended=true + - Umbraco__CMS__Unattended__UnattendedUserName=Admin + - Umbraco__CMS__Unattended__UnattendedUserEmail=test@umbraco.com + - Umbraco__CMS__Unattended__UnattendedUserPassword=password1234 + - Umbraco__CMS__Global__Smtp__Host=smtp4dev + - Umbraco__CMS__Global__Smtp__Port=25 + - Umbraco__CMS__Global__Smtp__From=warren-env@umbraco.com + + db: + image: mcr.microsoft.com/mssql/server:2019-latest + restart: unless-stopped + environment: + SA_PASSWORD: P@ssw0rd + ACCEPT_EULA: Y + + # Add "forwardPorts": ["1433"] to **devcontainer.json** to forward MSSQL locally. + # (Adding the "ports" property to this file will not forward from a Codespace.) + + smtp4dev: + image: rnwood/smtp4dev:v3 + restart: always + ports: + # Change the number before : to the port the web interface should be accessible on + - '5000:80' + # Change the number before : to the port the SMTP server should be accessible on + - '25:25' + # Change the number before : to the port the IMAP server should be accessible on + # - '143:143' + volumes: + # This is where smtp4dev stores the database.. + - smtp4dev-data:/smtp4dev + environment: + # Uncomment to customise these settings + + #Specifies the virtual path from web server root where SMTP4DEV web interface will be hosted. e.g. "/" or "/smtp4dev" + #- ServerOptions__BasePath=/smtp4dev + + #Specifies the server hostname. Used in auto-generated TLS certificate if enabled. + - ServerOptions__HostName=smtp4dev + + #Specifies the path where the database will be stored relative to APPDATA env var on Windows or XDG_CONFIG_HOME on non-Windows. Specify "" to use an in memory database. + #- ServerOptions__Database=database.db + + #Specifies the number of messages to keep + #- ServerOptions__NumberOfMessagesToKeep=100 + + #Specifies the number of sessions to keep + #- ServerOptions__NumberOfSessionsToKeep=100 + + #Specifies the TLS mode to use. None=Off. StartTls=On demand if client supports STARTTLS. ImplicitTls=TLS as soon as connection is established. + #- ServerOptions__TlsMode=None + + #Specifies the TLS certificate to use if TLS is enabled/requested. Specify "" to use an auto-generated self-signed certificate (then see console output on first startup) + #- ServerOptions__TlsCertificate= + + #Sets the name of the SMTP server that will be used to relay messages or "" if messages should not be relayed + #- RelayOptions__SmtpServer= + + #Sets the port number for the SMTP server used to relay messages. + #- RelayOptions__SmtpPort=25 + + #Specifies a comma separated list of recipient addresses for which messages will be relayed. An empty list means that no messages are relayed. + #- RelayOptions__AllowedEmailsString= + + #Specifies the address used in MAIL FROM when relaying messages. (Sender address in message headers is left unmodified). The sender of each message is used if not specified. + #- RelayOptions__SenderAddress= + + #The username for the SMTP server used to relay messages. If "" no authentication is attempted. + #- RelayOptions__Login= + + #The password for the SMTP server used to relay messages + #- RelayOptions__Password= + + #Specifies the port the IMAP server will listen on - allows standard email clients to view/retrieve messages + #"ServerOptions__ImapPort": 143 + +volumes: + smtp4dev-data: diff --git a/.devcontainer/library-scripts/azcli-debian.sh b/.devcontainer/library-scripts/azcli-debian.sh new file mode 100644 index 0000000000..b03dcb0f04 --- /dev/null +++ b/.devcontainer/library-scripts/azcli-debian.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/azcli.md +# +# Syntax: ./azcli-debian.sh + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +export DEBIAN_FRONTEND=noninteractive + +# Install curl, apt-transport-https, lsb-release, or gpg if missing +if ! dpkg -s apt-transport-https curl ca-certificates lsb-release > /dev/null 2>&1 || ! type gpg > /dev/null 2>&1; then + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + apt-get update + fi + apt-get -y install --no-install-recommends apt-transport-https curl ca-certificates lsb-release gnupg2 +fi + +# Install the Azure CLI +echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/azure-cli.list +curl -sL https://packages.microsoft.com/keys/microsoft.asc | (OUT=$(apt-key add - 2>&1) || echo $OUT) +apt-get update +apt-get install -y azure-cli +echo "Done!" \ No newline at end of file diff --git a/.devcontainer/mssql/BlankDb.sql b/.devcontainer/mssql/BlankDb.sql new file mode 100644 index 0000000000..44546efebf --- /dev/null +++ b/.devcontainer/mssql/BlankDb.sql @@ -0,0 +1,11 @@ +/* + This will generate a blank database when the container is spun up + that you can use to connect to for the SQL configuration in the web installer flow + + ---- NOTE ---- + Any .sql files in this folder will be executed + Along with any .dacpac will be restored as databases + See postCreateCommand.sh for specifics +*/ +CREATE DATABASE UmbracoUnicore; +GO \ No newline at end of file diff --git a/.devcontainer/mssql/installSQLtools.sh b/.devcontainer/mssql/installSQLtools.sh new file mode 100644 index 0000000000..3fa6a67a09 --- /dev/null +++ b/.devcontainer/mssql/installSQLtools.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -echo +echo "Installing mssql-tools" +curl -sSL https://packages.microsoft.com/keys/microsoft.asc | (OUT=$(apt-key add - 2>&1) || echo $OUT) +DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]') +CODENAME=$(lsb_release -cs) +echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-${DISTRO}-${CODENAME}-prod ${CODENAME} main" > /etc/apt/sources.list.d/microsoft.list +apt-get update +ACCEPT_EULA=Y apt-get -y install unixodbc-dev msodbcsql17 libunwind8 mssql-tools + +echo "Installing sqlpackage" +curl -sSL -o sqlpackage.zip "https://aka.ms/sqlpackage-linux" +mkdir /opt/sqlpackage +unzip sqlpackage.zip -d /opt/sqlpackage +rm sqlpackage.zip +chmod a+x /opt/sqlpackage/sqlpackage diff --git a/.devcontainer/mssql/postCreateCommand.sh b/.devcontainer/mssql/postCreateCommand.sh new file mode 100644 index 0000000000..e25583e0ff --- /dev/null +++ b/.devcontainer/mssql/postCreateCommand.sh @@ -0,0 +1,64 @@ +#!/bin/bash +dacpac="false" +sqlfiles="false" +SApassword=$1 +dacpath=$2 +sqlpath=$3 + +echo "SELECT * FROM SYS.DATABASES" | dd of=testsqlconnection.sql +for i in {1..60}; +do + /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $SApassword -d master -i testsqlconnection.sql > /dev/null + if [ $? -eq 0 ] + then + echo "SQL server ready" + break + else + echo "Not ready yet..." + sleep 1 + fi +done +rm testsqlconnection.sql + +for f in $dacpath/* +do + if [ $f == $dacpath/*".dacpac" ] + then + dacpac="true" + echo "Found dacpac $f" + fi +done + +for f in $sqlpath/* +do + if [ $f == $sqlpath/*".sql" ] + then + sqlfiles="true" + echo "Found SQL file $f" + fi +done + +if [ $sqlfiles == "true" ] +then + for f in $sqlpath/* + do + if [ $f == $sqlpath/*".sql" ] + then + echo "Executing $f" + /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $SApassword -d master -i $f + fi + done +fi + +if [ $dacpac == "true" ] +then + for f in $dacpath/* + do + if [ $f == $dacpath/*".dacpac" ] + then + dbname=$(basename $f ".dacpac") + echo "Deploying dacpac $f" + /opt/sqlpackage/sqlpackage /Action:Publish /SourceFile:$f /TargetServerName:localhost /TargetDatabaseName:$dbname /TargetUser:sa /TargetPassword:$SApassword + fi + done +fi \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f4e237a1f2..3432ac472a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,6 +16,7 @@ This project and everyone participating in it, is governed by the [our Code of C [Contributing code changes](#contributing-code-changes) * [Guidelines for contributions we welcome](#guidelines-for-contributions-we-welcome) + * [Ownership and copyright](#ownership-and-copyright) * [What can I start with?](#what-can-i-start-with) * [How do I begin?](#how-do-i-begin) * [Pull requests](#pull-requests) @@ -44,6 +45,17 @@ We have [documented what we consider small and large changes](CONTRIBUTION_GUIDE Remember, it is always worth working on an issue from the `Up for grabs` list or even asking for some feedback before you send us a PR. This way, your PR will not be closed as unwanted. +#### Ownership and copyright + +It is your responsibility to make sure that you're allowed to share the code you're providing us. +For example, you should have permission from your employer or customer to share code. + +Similarly, if your contribution is copied or adapted from somewhere else, make sure that the license allows you to reuse that for a contribution to Umbraco-CMS. + +If you're not sure, leave a note on your contribution and we will be happy to guide you. + +When your contribution has been accepted, it will be [MIT licensed](https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/LICENSE.md) from that time onwards. + ### What can I start with? Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+) diff --git a/.github/ISSUE_TEMPLATE/01_bug_report.yml b/.github/ISSUE_TEMPLATE/01_bug_report.yml new file mode 100644 index 0000000000..04d1a0e04c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_bug_report.yml @@ -0,0 +1,52 @@ +--- +name: 🐛 Bug Report +description: "File a bug report, if you've discovered a problem in Umbraco." +labels: "type/bug" +body: +- type: input + id: "version" + attributes: + label: "Which Umbraco version are you using?" + description: "Use the help icon in the Umbraco backoffice to find the version you're using" + validations: + required: true +- type: textarea + id: "summary" + attributes: + label: "Bug summary" + description: "Write a short summary of the bug." + placeholder: > + Try to pinpoint it as much as possible. + + Try to state the actual problem, and not just what you think the solution might be. + validations: + required: true +- type: textarea + attributes: + label: "Specifics" + id: "specifics" + description: "Remember that you can format code and logs nicely with the `<>` button" + placeholder: > + Mention the URL where this bug occurs, if applicable + + Please mention if you've checked it in other browsers as well + + Please include full error messages and screenshots, gifs or mp4 videos if applicable +- type: textarea + attributes: + label: "Steps to reproduce" + id: "reproduction" + description: "How can we reproduce the problem on a clean Umbraco install?" + placeholder: > + Please include screenshots, gifs or mp4 videos if applicable + validations: + required: true +- type: textarea + attributes: + label: "Expected result / actual result" + id: "result" + description: "What did you expect that would happen on your Umbraco site and what is the actual result of the above steps?" + placeholder: > + Describe the intended/desired outcome after you did the steps mentioned. + + Describe the behaviour of the bug diff --git a/.github/ISSUE_TEMPLATE/02_feature_request.yml b/.github/ISSUE_TEMPLATE/02_feature_request.yml new file mode 100644 index 0000000000..5d53b2f12e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_feature_request.yml @@ -0,0 +1,33 @@ +--- +name: 📮 Feature Request +description: Open a feature request, if you want to propose a new feature. +labels: type/feature +body: +- type: dropdown + id: version + attributes: + label: Umbraco version + description: Which major Umbraco version are you proposing a feature for? + options: + - v8 + - v9 + validations: + required: true +- type: textarea + id: summary + attributes: + label: Description + description: Write a brief desciption of your proposed new feature. + validations: + required: true +- type: textarea + attributes: + label: How can you help? + id: help + description: Umbraco''s core team has limited available time, but maybe you can help? + placeholder: > + If we can not work on your suggestion, please don't take it personally. Most likely, it's either: + + - We think your idea is valid, but we can't find the time to work on it. + + - Your idea might be better suited as a package, if it's not suitable for the majority of users. diff --git a/.github/ISSUE_TEMPLATE/1_Bug.md b/.github/ISSUE_TEMPLATE/1_Bug.md deleted file mode 100644 index d388af0d39..0000000000 --- a/.github/ISSUE_TEMPLATE/1_Bug.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -name: 🐛 Bug Report -about: File a bug report, if you've discovered a problem in Umbraco. ---- - -A brief description of the issue goes here. - - - -## Umbraco version - -I am seeing this issue on Umbraco version: - - -Reproduction ------------- - -If you're filing a bug, please describe how to reproduce it. Include as much -relevant information as possible, such as: - -### Bug summary - - - -### Specifics - - - -### Steps to reproduce - - - -### Expected result - - - -### Actual result - - diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.md b/.github/ISSUE_TEMPLATE/2_Feature_request.md deleted file mode 100644 index 16ec2568dd..0000000000 --- a/.github/ISSUE_TEMPLATE/2_Feature_request.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: 📮 Feature Request -about: Open a feature request, if you want to propose a new feature. ---- - -A brief description of your feature request goes here. - - - - -How can you help? -------------------------------- - - diff --git a/.github/ISSUE_TEMPLATE/3_BugNetCore.md b/.github/ISSUE_TEMPLATE/3_BugNetCore.md deleted file mode 100644 index 989904d4d8..0000000000 --- a/.github/ISSUE_TEMPLATE/3_BugNetCore.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: 🌟 .Net Core Bug Report -about: For bugs specifically for the upcoming .NET Core release of Umbraco, don't use this if you're working with Umbraco version 7 or 8 -labels: project/net-core ---- - -ℹ️ If this bug **also** appears on the current version 8 of Umbraco then please [report it as a regular bug](https://github.com/umbraco/Umbraco-CMS/issues/new?template=1_Bug.md), fixes in version 8 will be merged to the .NET Core version. - -A brief description of the issue goes here. - - - - -Reproduction ------------- - -If you're filing a bug, please describe how to reproduce it. Include as much -relevant information as possible, such as: - -### Bug summary - - - -### Specifics - - - -### Steps to reproduce - - - -### Expected result - - - -### Actual result - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 37d1be9158..d5418ad270 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: true +blank_issues_enabled: false contact_links: - name: ⁉️ Support Question url: https://our.umbraco.com @@ -8,4 +8,4 @@ contact_links: about: Documentation issues should be reported on the Umbraco documentation repository. - name: 🔐 Security Issue url: https://umbraco.com/about-us/trust-center/security-and-umbraco/how-to-report-a-vulnerability-in-umbraco/ - about: Discovered a Security Issue in Umbraco? \ No newline at end of file + about: Discovered a Security Issue in Umbraco? diff --git a/.gitignore b/.gitignore index 1c54100176..c792b2c5a9 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/canvasdesigner.*.js src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/navigation.controller.js src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.controller.js src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/utilities.js +src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/installer.app.js src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/ src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js @@ -180,16 +181,19 @@ src/Umbraco.Web.UI/Umbraco/telemetrics-id.umb /src/Umbraco.Web.UI.NetCore/wwwroot/is-cache/* /src/Umbraco.Tests.Integration/App_Data/* /src/Umbraco.Tests.Integration/TEMP/* -/src/Umbraco.Web.UI.NetCore/wwwroot/Umbraco/assets/* -/src/Umbraco.Web.UI.NetCore/wwwroot/Umbraco/js/* -/src/Umbraco.Web.UI.NetCore/wwwroot/Umbraco/lib/* -/src/Umbraco.Web.UI.NetCore/wwwroot/Umbraco/views/* +/src/Umbraco.Web.UI.NetCore/wwwroot/[Uu]mbraco/assets/* +/src/Umbraco.Web.UI.NetCore/wwwroot/[Uu]mbraco/js/* +/src/Umbraco.Web.UI.NetCore/wwwroot/[Uu]mbraco/lib/* +/src/Umbraco.Web.UI.NetCore/wwwroot/[Uu]mbraco/views/* /src/Umbraco.Web.UI.NetCore/wwwroot/App_Data/TEMP/* /src/Umbraco.Web.UI.NetCore/App_Data/Logs/* /src/Umbraco.Web.UI.NetCore/App_Data/TEMP/TypesCache/* /src/Umbraco.Web.UI.NetCore/App_Data/TEMP/* /src/Umbraco.Web.UI.NetCore/App_Data/Smidge/Cache/* -/src/Umbraco.Web.UI.NetCore/umbraco/logs +/src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Ll]ogs +/src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Dd]ata/* +/src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Mm]odels/* + src/Umbraco.Tests.Integration/umbraco/Data/ src/Umbraco.Tests.Integration/umbraco/logs/ @@ -197,10 +201,11 @@ src/Umbraco.Tests.Integration/umbraco/logs/ src/Umbraco.Tests.Integration/Views/ src/Umbraco.Tests/TEMP/ -/src/Umbraco.Web.UI.NetCore/Umbraco/Data/* + /src/Umbraco.Web.UI/config/umbracoSettings.config -/src/Umbraco.Web.UI.NetCore/Umbraco/models/* + src/Umbraco.Tests.UnitTests/umbraco/Data/TEMP/ /src/Umbraco.Web.UI.NetCore/appsettings.Local.json src/Umbraco.Tests.Integration/DatabaseContextTests.sdf + diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..85b28c51c1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "Dotnet build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/Umbraco.Web.UI.NetCore/bin/Debug/net5.0/Umbraco.Web.UI.NetCore.dll", + "args": [], + "cwd": "${workspaceFolder}/src/Umbraco.Web.UI.NetCore", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\\\bNow listening on:\\\\s+(https?://\\\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000..e2b6c01894 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,67 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "detail": "Builds the client and SLN", + "promptOnClose": true, + "group": "build", + "dependsOn": [ + "Client Build", + "Dotnet build" + ], + "problemMatcher": [] + }, + { + "label": "Client Install", + "detail": "install npm for Umbraco.Web.UI.Client", + "promptOnClose": true, + "type": "npm", + "script": "install", + "path": "src/Umbraco.Web.UI.Client/", + "problemMatcher": [] + }, + { + "label": "Client Build", + "detail": "runs npm run build for Umbraco.Web.UI.Client", + "promptOnClose": true, + "group": "build", + "type": "npm", + "script": "build", + "path": "src/Umbraco.Web.UI.Client/", + "problemMatcher": [ + "$gulp-tsc" + ] + }, + { + "label": "Dotnet build", + "detail": "Dotnet build of SLN", + "promptOnClose": true, + "group": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/umbraco-netcore-only.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "Dotnet watch", + "detail": "Dotnet run and watch of Web.UI.NetCore", + "promptOnClose": true, + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/NuGet.Config b/NuGet.Config index 92eaf83792..f8dc68d26a 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -9,7 +9,6 @@ - diff --git a/build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec b/build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec deleted file mode 100644 index 19d60f27a9..0000000000 --- a/build/NuSpecs/UmbracoCms.Examine.Lucene.nuspec +++ /dev/null @@ -1,49 +0,0 @@ - - - - Umbraco.Cms.Examine.Lucene - 9.0.0 - Umbraco CMS Examine Binaries - Umbraco HQ - Umbraco HQ - MIT - https://umbraco.com/ - https://umbraco.com/dist/nuget/logo-small.png - false - Contains the Examine assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET Core project. - Contains dll files required to run Examine. - en-US - umbraco - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/NuSpecs/UmbracoCms.StaticAssets.nuspec b/build/NuSpecs/UmbracoCms.StaticAssets.nuspec index b8109dbfa9..cd8367d8c8 100644 --- a/build/NuSpecs/UmbracoCms.StaticAssets.nuspec +++ b/build/NuSpecs/UmbracoCms.StaticAssets.nuspec @@ -28,6 +28,6 @@ - + diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 1e9fe0fdcd..b2824d8237 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -1,3 +1,4 @@ +name: $(TeamProject)_$(Build.DefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) variables: buildConfiguration: Release SA_PASSWORD: UmbracoIntegration123! @@ -321,8 +322,7 @@ stages: restoreSolution: '*\src\umbraco.sln' feedsToUse: config - task: PowerShell@1 - displayName: Update Version - condition: 'eq(variables[''Umbraco.IsReleaseBuild''], ''false'')' + displayName: Update Version and Artifact Name inputs: scriptType: inlineScript inlineScript: > @@ -333,40 +333,45 @@ stages: $version = $ubuild.GetUmbracoVersion() - if ($version.Comment -ne "") - { - # 8.0.0-beta.33.1234 - $continuous = "$($version.Semver).$(Build.BuildNumber)" + $isRelease = [regex]::matches($env:BUILD_SOURCEBRANCH,"v\d+\/\d+.\d+.*") + + + if ($isRelease.Count -gt 0){ + $continuous = $version.Semver + Write-Host "##vso[build.addbuildtag]Release build" } else { - # 8.0.0-alpha.1234 - $continuous = "$($version.Release)-alpha.$(Build.BuildNumber)" - } - $ubuild.SetUmbracoVersion($continuous) + $date = (Get-Date).ToString("yyyyMMdd") + $continuous = "$($version.release)-preview$date.$(Build.BuildId)" + $ubuild.SetUmbracoVersion($continuous) + + #Update the version in templates also + + $templatePath = + 'build/templates/UmbracoProject/.template.config/template.json' + + $a = Get-Content $templatePath -raw | ConvertFrom-Json + + $a.symbols.version.defaultValue = $continuous + + $a | ConvertTo-Json -depth 32| set-content $templatePath - #Update the version in templates also + $templatePath = + 'build/templates/UmbracoPackage/.template.config/template.json' - $templatePath = - 'build/templates/UmbracoProject/.template.config/template.json' + $a = Get-Content $templatePath -raw | ConvertFrom-Json - $a = Get-Content $templatePath -raw | ConvertFrom-Json + $a.symbols.version.defaultValue = $continuous - $a.symbols.version.defaultValue = $continuous + $a | ConvertTo-Json -depth 32| set-content $templatePath + Write-Host "##vso[build.addbuildtag]Continuous build" - $a | ConvertTo-Json -depth 32| set-content $templatePath + } - $templatePath = - 'build/templates/UmbracoPackage/.template.config/template.json' - - $a = Get-Content $templatePath -raw | ConvertFrom-Json - - $a.symbols.version.defaultValue = $continuous - - $a | ConvertTo-Json -depth 32| set-content $templatePath - + Write-Host "##vso[build.updatebuildnumber]$continuous.$(Build.BuildId)" Write-Host "Building: $continuous" - task: PowerShell@1 @@ -396,7 +401,7 @@ stages: publishJUnitResults: true testResultsFiles: '**\TESTS-*.xml' - task: PowerShell@1 - displayName: Prepare Packages & Zip + displayName: Prepare Packages inputs: scriptType: inlineScript inlineScript: | @@ -405,19 +410,6 @@ stages: $ubuild.CompileUmbraco() $ubuild.PreparePackages() - $ubuild.PackageZip() - - task: CopyFiles@2 - displayName: Copy Zip Files to Staging - inputs: - SourceFolder: build.out - Contents: '*.zip' - TargetFolder: $(build.artifactstagingdirectory) - CleanTargetFolder: true - - task: PublishBuildArtifacts@1 - displayName: Publish Zip Files - inputs: - PathtoPublish: $(build.artifactstagingdirectory) - ArtifactName: zips - task: PowerShell@1 displayName: Verify & Package NuGet inputs: @@ -432,7 +424,7 @@ stages: displayName: Copy NuPkg Files to Staging inputs: SourceFolder: build.out - Contents: '*.nupkg' + Contents: '*.*nupkg' TargetFolder: $(build.artifactstagingdirectory) CleanTargetFolder: true - task: PublishBuildArtifacts@1 diff --git a/build/build.ps1 b/build/build.ps1 index be9a8f0f4f..fb869738f3 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -351,29 +351,6 @@ $this.RemoveDirectory("$tmp\Templates\UmbracoProject\bin") }) - $ubuild.DefineMethod("PackageZip", - { - - Write-Host "Create Zip packages" - - $src = "$($this.SolutionRoot)\src" - $tmp = $this.BuildTemp - $out = $this.BuildOutput - - Write-Host "Zip all binaries" - &$this.BuildEnv.Zip a -r "$out\UmbracoCms.AllBinaries.$($this.Version.Semver).zip" ` - "$tmp\bin\*" ` - "-x!dotless.Core.*" ` - > $null - if (-not $?) { throw "Failed to zip UmbracoCms.AllBinaries." } - - Write-Host "Zip cms" - &$this.BuildEnv.Zip a -r "$out\UmbracoCms.$($this.Version.Semver).zip" ` - "$tmp\WebApp\*" ` - "-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb" ` - > $null - if (-not $?) { throw "Failed to zip UmbracoCms." } - }) $ubuild.DefineMethod("PrepareBuild", { @@ -433,13 +410,6 @@ -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cms.log" if (-not $?) { throw "Failed to pack NuGet UmbracoCms." } - &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Examine.Lucene.nuspec" ` - -Properties BuildTmp="$($this.BuildTemp)" ` - -Version "$($this.Version.Semver.ToString())" ` - -Verbosity detailed ` - -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.examine.lucene.log" - if (-not $?) { throw "Failed to pack Nuget UmbracoCms.Lucene.nuspec"} - &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.SqlCe.nuspec" ` -Properties BuildTmp="$($this.BuildTemp)" ` -Version "$($this.Version.Semver.ToString())" ` @@ -548,8 +518,6 @@ # not running tests $this.PreparePackages() if ($this.OnError()) { return } - $this.PackageZip() - if ($this.OnError()) { return } $this.VerifyNuGet() if ($this.OnError()) { return } $this.PackageNuGet() diff --git a/build/templates/UmbracoPackage/.template.config/template.json b/build/templates/UmbracoPackage/.template.config/template.json index 56989e3e97..0a4c8f2f2b 100644 --- a/build/templates/UmbracoPackage/.template.config/template.json +++ b/build/templates/UmbracoPackage/.template.config/template.json @@ -24,7 +24,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta001", + "defaultValue": "9.0.0-beta003", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/build/templates/UmbracoPackage/UmbracoPackage.csproj b/build/templates/UmbracoPackage/UmbracoPackage.csproj index e688419184..43e0365e9c 100644 --- a/build/templates/UmbracoPackage/UmbracoPackage.csproj +++ b/build/templates/UmbracoPackage/UmbracoPackage.csproj @@ -22,7 +22,7 @@ True - build + buildTransitive diff --git a/build/templates/UmbracoProject/.template.config/dotnetcli.host.json b/build/templates/UmbracoProject/.template.config/dotnetcli.host.json index 2727c1dbcc..fb990c8902 100644 --- a/build/templates/UmbracoProject/.template.config/dotnetcli.host.json +++ b/build/templates/UmbracoProject/.template.config/dotnetcli.host.json @@ -34,6 +34,6 @@ "dotnet new umbraco -n MyNewProject", "dotnet new umbraco -n MyNewProjectWithCE -ce", "dotnet new umbraco -n MyNewProject --no-restore", - "dotnet new umbraco -n MyNewProject --friendly-name "Friendly User" --email user@email.com --password password1234 --connection-string "Server=ConnectionStringHere"" + "dotnet new umbraco -n MyNewProject --friendly-name \"Friendly User\" --email user@email.com --password password1234 --connection-string \"Server=ConnectionStringHere\"" ] } diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index 203c5abd1a..8a8c396dcb 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -15,7 +15,7 @@ }, "primaryOutputs": [ { - "path": "UmbracoProject.csproj" + "path": "UmbracoProject.csproj" } ], "postActions": [ @@ -31,23 +31,36 @@ ], "sourceName": "UmbracoProject", "symbols": { + "namespaceReplacer": { + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "Umbraco.Cms.Web.UI.NetCore", + "parameters": { + "source": "name", + "steps": [ + { + "regex": "\\s", + "replacement": "_" + }, + { + "regex": "-", + "replacement": "_" + }, + { + "regex": "^[^a-zA-Z_]+", + "replacement": "_" + } + ] + } + }, "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-beta001", + "defaultValue": "9.0.0-beta004", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, - "namespaceReplacer": { - "type": "generated", - "generator": "coalesce", - "parameters": { - "sourceVariableName": "name", - "defaultValue": "UmbracoProject", - "fallbackVariableName": "name" - }, - "replaces":"Umbraco.Web.UI.NetCore" - }, "PackageTestSiteName": { "type": "parameter", "datatype":"text", @@ -99,8 +112,8 @@ "generator": "port", "replaces": "HTTPS_PORT_FROM_TEMPLATE", "parameters": { - "high": 65535, - "low": 1024, + "low": 44300, + "high": 44399, "fallback": 5001 } }, @@ -108,30 +121,134 @@ "type": "parameter", "datatype":"text", "description": "The friendly name of the user for Umbraco login when using Unattended install (Without installer wizard UI)", - "replaces": "FRIENDLY_NAME_FROM_TEMPLATE", "defaultValue": "" }, + "FriendlyNameReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "FRIENDLY_NAME_FROM_TEMPLATE", + "parameters": { + "source": "FriendlyName", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "Email":{ "type": "parameter", "datatype":"text", "description": "Email to use for Umbraco login when using Unattended install (Without installer wizard UI)", - "replaces": "EMAIL_FROM_TEMPLATE", "defaultValue": "" }, + "EmailReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "EMAIL_FROM_TEMPLATE", + "parameters": { + "source": "Email", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "Password":{ "type": "parameter", "datatype":"text", "description": "Password to use for Umbraco login when using Unattended install (Without installer wizard UI)", - "replaces": "PASSWORD_FROM_TEMPLATE", "defaultValue": "" }, + "PasswordReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "PASSWORD_FROM_TEMPLATE", + "parameters": { + "source": "Password", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "ConnectionString":{ "type": "parameter", "datatype":"text", "description": "Database connection string when using Unattended install (Without installer wizard UI)", - "replaces": "CONNECTION_FROM_TEMPLATE", "defaultValue": "" }, + "ConnectionStringReplaced":{ + "type": "generated", + "generator": "regex", + "dataType": "string", + "replaces": "CONNECTION_FROM_TEMPLATE", + "parameters": { + "source": "ConnectionString", + "steps": [ + { + "regex": "\\\\", + "replacement": "\\\\" + }, + { + "regex": "\\\"", + "replacement": "\\\"" + }, + { + "regex": "\\\n", + "replacement": "\\\n" + }, + { + "regex": "\\\t", + "replacement": "\\\t" + } + ] + } + }, "UsingUnattenedInstall":{ "type": "computed", "value": "(FriendlyName != \"\" && Email != \"\" && Password != \"\" && ConnectionString != \"\")" diff --git a/build/templates/UmbracoProject/Properties/launchSettings.json b/build/templates/UmbracoProject/Properties/launchSettings.json index 3819fb7c86..f40916f4d1 100644 --- a/build/templates/UmbracoProject/Properties/launchSettings.json +++ b/build/templates/UmbracoProject/Properties/launchSettings.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, @@ -17,10 +18,12 @@ }, "Umbraco.Web.UI.NetCore": { "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:HTTPS_PORT_FROM_TEMPLATE;http://localhost:HTTP_PORT_FROM_TEMPLATE", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:HTTPS_PORT_FROM_TEMPLATE;http://localhost:HTTP_PORT_FROM_TEMPLATE" + } } } } diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj index 08643bb150..6850940edd 100644 --- a/build/templates/UmbracoProject/UmbracoProject.csproj +++ b/build/templates/UmbracoProject/UmbracoProject.csproj @@ -1,6 +1,7 @@ net5.0 + Umbraco.Cms.Web.UI.NetCore @@ -18,16 +19,13 @@ - - - @@ -44,10 +42,9 @@ - - + false diff --git a/build/templates/UmbracoProject/appsettings.Development.json b/build/templates/UmbracoProject/appsettings.Development.json index 0823d89494..a02bad241d 100644 --- a/build/templates/UmbracoProject/appsettings.Development.json +++ b/build/templates/UmbracoProject/appsettings.Development.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/appsettings.json", "Serilog": { "MinimumLevel": { "Default": "Information" @@ -23,6 +24,9 @@ //#endif "Umbraco": { "CMS": { + "Content": { + "MacroErrors": "Throw" + }, //#if (UsingUnattenedInstall) "Unattended": { "InstallUnattended": true, diff --git a/build/templates/UmbracoProject/appsettings.json b/build/templates/UmbracoProject/appsettings.json index 140c970ebb..324b99f42c 100644 --- a/build/templates/UmbracoProject/appsettings.json +++ b/build/templates/UmbracoProject/appsettings.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/appsettings.json", "Serilog": { "MinimumLevel": { "Default": "Information", diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b16405633f..a1220f9b3f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ 9.0.0 9.0.0 - 9.0.0-beta001 + 9.0.0-beta004 9.0.0 9.0 en-US @@ -17,5 +17,25 @@ git https://github.com/umbraco/umbraco-cms + + + + true + + + true + + + true + snupkg + + + + + + + true + + diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs index 8106da11e6..582915fb2e 100644 --- a/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ApplicationCacheRefresher.cs @@ -1,5 +1,6 @@ using System; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/CacheRefresherBase.cs b/src/Umbraco.Core/Cache/CacheRefresherBase.cs index 323fa6aeca..7b962065c5 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherBase.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs index fc9cdefe27..bd41ee9d9b 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs @@ -1,4 +1,5 @@ using System; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; using Umbraco.Cms.Core.Sync; diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs index 7b9b2994a9..7fa77feb94 100644 --- a/src/Umbraco.Core/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentCacheRefresher.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs index b093df85b8..9a709e9a9f 100644 --- a/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/ContentTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs index dfbf9230e2..ef2a3738e5 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs @@ -2,6 +2,8 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; @@ -61,6 +63,11 @@ namespace Umbraco.Cms.Core.Cache foreach (var payload in payloads) { _idKeyMap.ClearCache(payload.Id); + + if (dataTypeCache.Success) + { + dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); + } } // TODO: not sure I like these? diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs index c812a4aadc..dbe84b114e 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs index 228baf4b9a..28e62c854d 100644 --- a/src/Umbraco.Core/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DomainCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs b/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs index c79f7579a7..04b91e43d8 100644 --- a/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs +++ b/src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Core.Sync; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs index f0946e0e52..92669159f1 100644 --- a/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/JsonCacheRefresherBase.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs index fb65aaa58d..414c51c186 100644 --- a/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/LanguageCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 3c1ad46f2c..77550b81d1 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs index 5d301fc353..c192296375 100644 --- a/src/Umbraco.Core/Cache/MediaCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MediaCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs index 121dbea738..4f199389d6 100644 --- a/src/Umbraco.Core/Cache/MemberCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberCacheRefresher.cs @@ -3,6 +3,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs index 1f019e8f30..0866f7b39a 100644 --- a/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MemberGroupCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs index f7867ae3fe..379e04af7c 100644 --- a/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs +++ b/src/Umbraco.Core/Cache/PayloadCacheRefresherBase.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Sync; diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs index d833a0aea5..5c9eb20b4c 100644 --- a/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/PublicAccessCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Cache { diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs index a9694c92f5..aadae52ce6 100644 --- a/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/RelationTypeCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs index c098ccb967..0bc2c6c5ef 100644 --- a/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/TemplateCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Services; diff --git a/src/Umbraco.Core/Cache/UserCacheRefresher.cs b/src/Umbraco.Core/Cache/UserCacheRefresher.cs index 706ed8ae45..b8d271bbb3 100644 --- a/src/Umbraco.Core/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs index 3a7c8d12b1..3ec88b1ea5 100644 --- a/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/UserGroupCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 7d0a8fdb09..1979f33d10 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Core.Composing private readonly ITypeFinderConfig _typeFinderConfig; // used for benchmark tests - internal bool QueryWithReferencingAssemblies = true; + internal bool QueryWithReferencingAssemblies { get; set; } = true; public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, IRuntimeHash runtimeHash, ITypeFinderConfig typeFinderConfig = null) { diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index c53e6ee4d2..39b70b831b 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.Composing /// public sealed class TypeLoader { - private const string CacheKey = "umbraco-types.list"; + internal const string CacheKey = "umbraco-types.list"; private readonly IAppPolicyCache _runtimeCache; private readonly ILogger _logger; @@ -45,6 +45,7 @@ namespace Umbraco.Cms.Core.Composing private bool _reportedChange; private readonly DirectoryInfo _localTempPath; private readonly Lazy _fileBasePath; + private readonly Dictionary<(string, string), IEnumerable> EmptyCache = new Dictionary<(string, string), IEnumerable>(); /// /// Initializes a new instance of the class. @@ -78,8 +79,6 @@ namespace Umbraco.Cms.Core.Composing _fileBasePath = new Lazy(GetFileBasePath); - _fileBasePath = new Lazy(GetFileBasePath); - if (detectChanges) { //first check if the cached hash is string.Empty, if it is then we need @@ -92,7 +91,10 @@ namespace Umbraco.Cms.Core.Composing // rescanning of all types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 var typesListFilePath = GetTypesListFilePath(); - DeleteFile(typesListFilePath, FileDeleteTimeout); + if (typesListFilePath != null) + { + DeleteFile(typesListFilePath, FileDeleteTimeout); + } WriteCacheTypesHash(); } @@ -103,7 +105,10 @@ namespace Umbraco.Cms.Core.Composing // rescanning of all types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 var typesListFilePath = GetTypesListFilePath(); - DeleteFile(typesListFilePath, FileDeleteTimeout); + if (typesListFilePath != null) + { + DeleteFile(typesListFilePath, FileDeleteTimeout); + } // always set to true if we're not detecting (generally only for testing) RequiresRescanning = true; @@ -162,11 +167,20 @@ namespace Umbraco.Cms.Core.Composing get { if (_cachedAssembliesHash != null) + { return _cachedAssembliesHash; + } var typesHashFilePath = GetTypesHashFilePath(); - if (!File.Exists(typesHashFilePath)) + if (typesHashFilePath == null) + { return string.Empty; + } + + if (!File.Exists(typesHashFilePath)) + { + return string.Empty; + } var hash = File.ReadAllText(typesHashFilePath, Encoding.UTF8); @@ -198,7 +212,10 @@ namespace Umbraco.Cms.Core.Composing private void WriteCacheTypesHash() { var typesHashFilePath = GetTypesHashFilePath(); - File.WriteAllText(typesHashFilePath, CurrentAssembliesHash, Encoding.UTF8); + if (typesHashFilePath != null) + { + File.WriteAllText(typesHashFilePath, CurrentAssembliesHash, Encoding.UTF8); + } } #endregion @@ -214,15 +231,18 @@ namespace Umbraco.Cms.Core.Composing // internal for tests public Attempt> TryGetCached(Type baseType, Type attributeType) { - var cache = _runtimeCache.GetCacheItem, IEnumerable>>(CacheKey, ReadCacheSafe, TimeSpan.FromSeconds(ListFileCacheDuration)); + var cache = _runtimeCache.GetCacheItem(CacheKey, ReadCacheSafe, TimeSpan.FromSeconds(ListFileCacheDuration)); + + cache.TryGetValue( + (baseType == null ? string.Empty : baseType.FullName, attributeType == null ? string.Empty : attributeType.FullName), + out IEnumerable types); - cache.TryGetValue(Tuple.Create(baseType == null ? string.Empty : baseType.FullName, attributeType == null ? string.Empty : attributeType.FullName), out IEnumerable types); return types == null ? Attempt>.Fail() : Attempt.Succeed(types); } - private Dictionary, IEnumerable> ReadCacheSafe() + private Dictionary<(string, string), IEnumerable> ReadCacheSafe() { try { @@ -233,7 +253,10 @@ namespace Umbraco.Cms.Core.Composing try { var typesListFilePath = GetTypesListFilePath(); - DeleteFile(typesListFilePath, FileDeleteTimeout); + if (typesListFilePath != null) + { + DeleteFile(typesListFilePath, FileDeleteTimeout); + } } catch { @@ -241,20 +264,19 @@ namespace Umbraco.Cms.Core.Composing } } - return new Dictionary, IEnumerable>(); + return EmptyCache; } // internal for tests - public Dictionary, IEnumerable> ReadCache() + public Dictionary<(string, string), IEnumerable> ReadCache() { - var cache = new Dictionary, IEnumerable>(); - var typesListFilePath = GetTypesListFilePath(); - if (File.Exists(typesListFilePath) == false) + if (typesListFilePath == null || File.Exists(typesListFilePath) == false) { - return cache; + return EmptyCache; } + var cache = new Dictionary<(string, string), IEnumerable>(); using (var stream = GetFileStream(typesListFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) using (var reader = new StreamReader(stream)) { @@ -288,7 +310,7 @@ namespace Umbraco.Cms.Core.Composing } if (type == string.Empty) { - cache[Tuple.Create(baseType, attributeType)] = types; + cache[(baseType, attributeType)] = types; break; } types.Add(type); @@ -306,9 +328,9 @@ namespace Umbraco.Cms.Core.Composing } // internal for tests - public string GetTypesListFilePath() => _fileBasePath.Value + ".list"; + public string GetTypesListFilePath() => _fileBasePath.Value == null ? null : _fileBasePath.Value + ".list"; - private string GetTypesHashFilePath() => _fileBasePath.Value + ".hash"; + private string GetTypesHashFilePath() => _fileBasePath.Value == null ? null : _fileBasePath.Value + ".hash"; /// /// Used to produce the Lazy value of _fileBasePath @@ -316,7 +338,12 @@ namespace Umbraco.Cms.Core.Composing /// private string GetFileBasePath() { - var fileBasePath = Path.Combine(_localTempPath.FullName, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName); + if (_localTempPath == null) + { + return null; + } + + var fileBasePath = Path.Combine(_localTempPath.FullName, "TypesCache", "umbraco-types." + EnvironmentHelper.FileSafeMachineName); // ensure that the folder exists var directory = Path.GetDirectoryName(fileBasePath); @@ -338,6 +365,11 @@ namespace Umbraco.Cms.Core.Composing { _logger.LogDebug("Writing cache file."); var typesListFilePath = GetTypesListFilePath(); + if (typesListFilePath == null) + { + return; + } + using (var stream = GetFileStream(typesListFilePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) { @@ -394,10 +426,18 @@ namespace Umbraco.Cms.Core.Composing public void ClearTypesCache() { var typesListFilePath = GetTypesListFilePath(); + if (typesListFilePath == null) + { + return; + } + DeleteFile(typesListFilePath, FileDeleteTimeout); var typesHashFilePath = GetTypesHashFilePath(); - DeleteFile(typesHashFilePath, FileDeleteTimeout); + if (typesHashFilePath != null) + { + DeleteFile(typesHashFilePath, FileDeleteTimeout); + } _runtimeCache.Clear(CacheKey); } @@ -634,7 +674,8 @@ namespace Umbraco.Cms.Core.Composing } private IEnumerable GetTypesInternal( - Type baseType, Type attributeType, + Type baseType, + Type attributeType, Func> finder, string action, bool cache) @@ -692,7 +733,7 @@ namespace Umbraco.Cms.Core.Composing typeList = new TypeList(baseType, attributeType); var typesListFilePath = GetTypesListFilePath(); - var scan = RequiresRescanning || File.Exists(typesListFilePath) == false; + var scan = RequiresRescanning || typesListFilePath == null || File.Exists(typesListFilePath) == false; if (scan) { diff --git a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs index e15df52039..1eafcce9e0 100644 --- a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs @@ -16,6 +16,8 @@ namespace Umbraco.Cms.Core.Composing { protected abstract TBuilder This { get; } + private readonly Dictionary _customWeights = new Dictionary(); + /// /// Clears all types in the collection. /// @@ -107,6 +109,18 @@ namespace Umbraco.Cms.Core.Composing return This; } + /// + /// Changes the default weight of an item + /// + /// The type of item + /// The new weight + /// + public TBuilder SetWeight(int weight) where T : TItem + { + _customWeights[typeof(T)] = weight; + return This; + } + protected override IEnumerable GetRegisteringTypes(IEnumerable types) { var list = types.ToList(); @@ -118,6 +132,8 @@ namespace Umbraco.Cms.Core.Composing protected virtual int GetWeight(Type type) { + if (_customWeights.ContainsKey(type)) + return _customWeights[type]; var attr = type.GetCustomAttributes(typeof(WeightAttribute), false).OfType().SingleOrDefault(); return attr?.Weight ?? DefaultWeight; } diff --git a/src/Umbraco.Core/Configuration/Models/GlobalSettings.cs b/src/Umbraco.Core/Configuration/Models/GlobalSettings.cs index b20ac84700..b27b60ec8e 100644 --- a/src/Umbraco.Core/Configuration/Models/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/GlobalSettings.cs @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// Gets or sets a value indicating whether to hide the top level node from the path. /// - public bool HideTopLevelNodeFromPath { get; set; } = false; + public bool HideTopLevelNodeFromPath { get; set; } = true; /// /// Gets or sets a value indicating whether HTTPS should be used. diff --git a/src/Umbraco.Core/Configuration/Models/IndexCreatorSettings.cs b/src/Umbraco.Core/Configuration/Models/IndexCreatorSettings.cs index 5ae43f8d39..52a40b0ee4 100644 --- a/src/Umbraco.Core/Configuration/Models/IndexCreatorSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/IndexCreatorSettings.cs @@ -1,6 +1,8 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; + namespace Umbraco.Cms.Core.Configuration.Models { /// @@ -11,6 +13,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// Gets or sets a value for lucene directory factory type. /// - public string LuceneDirectoryFactory { get; set; } + public LuceneDirectoryFactory LuceneDirectoryFactory { get; set; } + } } diff --git a/src/Umbraco.Core/Configuration/Models/LuceneDirectoryFactory.cs b/src/Umbraco.Core/Configuration/Models/LuceneDirectoryFactory.cs new file mode 100644 index 0000000000..5f06a850f1 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/LuceneDirectoryFactory.cs @@ -0,0 +1,24 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Configuration.Models +{ + public enum LuceneDirectoryFactory + { + /// + /// The index will operate from the default location: Umbraco/Data/Temp/ExamineIndexes + /// + Default, + + /// + /// The index will operate on a local index created in the processes %temp% location and + /// will replicate back to main storage in Umbraco/Data/Temp/ExamineIndexes + /// + SyncedTempFileSystemDirectoryFactory, + + /// + /// The index will operate only in the processes %temp% directory location + /// + TempFileSystemDirectoryFactory + } +} diff --git a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs index 192c3966f6..76e7adc4b4 100644 --- a/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ModelsBuilderSettings.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// Gets or sets a value for the models mode. /// - public ModelsMode ModelsMode { get; set; } = ModelsMode.PureLive; + public ModelsMode ModelsMode { get; set; } = ModelsMode.InMemoryAuto; /// /// Gets or sets a value for models namespace. @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// /// Models become out-of-date when data types or content types are updated. When this - /// setting is activated the ~/App_Data/Models/ood.txt file is then created. When models are + /// setting is activated the ~/umbraco/models/PureLive/ood.txt file is then created. When models are /// generated through the dashboard, the files is cleared. Default value is false. /// public bool FlagOutOfDateModels @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Core.Configuration.Models set { - if (!ModelsMode.IsLive()) + if (!ModelsMode.IsAuto()) { _flagOutOfDateModels = false; } @@ -51,9 +51,10 @@ namespace Umbraco.Cms.Core.Configuration.Models /// /// Gets or sets a value for the models directory. /// - /// Default is ~/App_Data/Models but that can be changed. + /// Default is ~/umbraco/models but that can be changed. public string ModelsDirectory { get; set; } = DefaultModelsDirectory; + /// /// Gets or sets a value indicating whether to accept an unsafe value for ModelsDirectory. /// diff --git a/src/Umbraco.Core/Configuration/ModelsMode.cs b/src/Umbraco.Core/Configuration/ModelsMode.cs index 1917f2b4cd..064e035892 100644 --- a/src/Umbraco.Core/Configuration/ModelsMode.cs +++ b/src/Umbraco.Core/Configuration/ModelsMode.cs @@ -18,22 +18,22 @@ namespace Umbraco.Cms.Core.Configuration /// When: a content type change occurs. /// /// The app does not restart. Models are available in views exclusively. - PureLive, + InMemoryAuto, /// - /// Generate models in AppData. + /// Generate models as *.cs files. /// When: generation is triggered. /// /// Generation can be triggered from the dashboard. The app does not restart. /// Models are not compiled and thus are not available to the project. - AppData, + SourceCodeManual, /// - /// Generate models in AppData. + /// Generate models as *.cs files. /// When: a content type change occurs, or generation is triggered. /// /// Generation can be triggered from the dashboard. The app does not restart. /// Models are not compiled and thus are not available to the project. - LiveAppData + SourceCodeAuto } } diff --git a/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs b/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs index f5f4e9e09c..f27d54b55d 100644 --- a/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs +++ b/src/Umbraco.Core/Configuration/ModelsModeExtensions.cs @@ -8,21 +8,21 @@ namespace Umbraco.Extensions public static class ModelsModeExtensions { /// - /// Gets a value indicating whether the mode is LiveAnything or PureLive. + /// Gets a value indicating whether the mode is *Auto. /// - public static bool IsLive(this ModelsMode modelsMode) - => modelsMode == ModelsMode.PureLive || modelsMode == ModelsMode.LiveAppData; + public static bool IsAuto(this ModelsMode modelsMode) + => modelsMode == ModelsMode.InMemoryAuto || modelsMode == ModelsMode.SourceCodeAuto; /// - /// Gets a value indicating whether the mode is LiveAnything but not PureLive. + /// Gets a value indicating whether the mode is *Auto but not InMemory. /// - public static bool IsLiveNotPure(this ModelsMode modelsMode) - => modelsMode == ModelsMode.LiveAppData; + public static bool IsAutoNotInMemory(this ModelsMode modelsMode) + => modelsMode == ModelsMode.SourceCodeAuto; /// - /// Gets a value indicating whether the mode supports explicit generation (as opposed to pure live). + /// Gets a value indicating whether the mode supports explicit manual generation. /// public static bool SupportsExplicitGeneration(this ModelsMode modelsMode) - => modelsMode == ModelsMode.AppData || modelsMode == ModelsMode.LiveAppData; + => modelsMode == ModelsMode.SourceCodeManual || modelsMode == ModelsMode.SourceCodeAuto; } } diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 20d790b1d5..9ebbf21369 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -114,6 +114,26 @@ namespace Umbraco.Cms.Core /// public const string Image = "Image"; + /// + /// MediaType alias for a video. + /// + public const string Video = "Video"; + + /// + /// MediaType alias for an audio. + /// + public const string Audio = "Audio"; + + /// + /// MediaType alias for an article. + /// + public const string Article = "Article"; + + /// + /// MediaType alias for vector graphics. + /// + public const string VectorGraphics = "VectorGraphics"; + /// /// MediaType alias indicating allowing auto-selection. /// diff --git a/src/Umbraco.Core/Constants-DataTypes.cs b/src/Umbraco.Core/Constants-DataTypes.cs index 2cbc254e28..12445ea589 100644 --- a/src/Umbraco.Core/Constants-DataTypes.cs +++ b/src/Umbraco.Core/Constants-DataTypes.cs @@ -25,6 +25,10 @@ namespace Umbraco.Cms.Core public const int DropDownSingle = -39; public const int DropDownMultiple = -42; public const int Upload = -90; + public const int UploadVideo = -100; + public const int UploadAudio = -101; + public const int UploadArticle = -102; + public const int UploadVectorGraphics = -103; public const int DefaultContentListView = -95; public const int DefaultMediaListView = -96; @@ -88,6 +92,49 @@ namespace Umbraco.Cms.Core public static readonly Guid MultipleMediaPickerGuid = new Guid(MultipleMediaPicker); + /// + /// Guid for Media Picker v3 as string + /// + public const string MediaPicker3 = "4309A3EA-0D78-4329-A06C-C80B036AF19A"; + + /// + /// Guid for Media Picker v3 + /// + public static readonly Guid MediaPicker3Guid = new Guid(MediaPicker3); + + /// + /// Guid for Media Picker v3 multiple as string + /// + public const string MediaPicker3Multiple = "1B661F40-2242-4B44-B9CB-3990EE2B13C0"; + + /// + /// Guid for Media Picker v3 multiple + /// + public static readonly Guid MediaPicker3MultipleGuid = new Guid(MediaPicker3Multiple); + + + /// + /// Guid for Media Picker v3 single-image as string + /// + public const string MediaPicker3SingleImage = "AD9F0CF2-BDA2-45D5-9EA1-A63CFC873FD3"; + + /// + /// Guid for Media Picker v3 single-image + /// + public static readonly Guid MediaPicker3SingleImageGuid = new Guid(MediaPicker3SingleImage); + + + /// + /// Guid for Media Picker v3 multi-image as string + /// + public const string MediaPicker3MultipleImages = "0E63D883-B62B-4799-88C3-157F82E83ECC"; + + /// + /// Guid for Media Picker v3 multi-image + /// + public static readonly Guid MediaPicker3MultipleImagesGuid = new Guid(MediaPicker3MultipleImages); + + /// /// Guid for Related Links as string /// @@ -307,6 +354,46 @@ namespace Umbraco.Cms.Core /// public static readonly Guid UploadGuid = new Guid(Upload); + /// + /// Guid for UploadVideo as string + /// + public const string UploadVideo = "70575fe7-9812-4396-bbe1-c81a76db71b5"; + + /// + /// Guid for UploadVideo + /// + public static readonly Guid UploadVideoGuid = new Guid(UploadVideo); + + /// + /// Guid for UploadAudio as string + /// + public const string UploadAudio = "8f430dd6-4e96-447e-9dc0-cb552c8cd1f3"; + + /// + /// Guid for UploadAudio + /// + public static readonly Guid UploadAudioGuid = new Guid(UploadAudio); + + /// + /// Guid for UploadArticle as string + /// + public const string UploadArticle = "bc1e266c-dac4-4164-bf08-8a1ec6a7143d"; + + /// + /// Guid for UploadArticle + /// + public static readonly Guid UploadArticleGuid = new Guid(UploadArticle); + + /// + /// Guid for UploadVectorGraphics as string + /// + public const string UploadVectorGraphics = "215cb418-2153-4429-9aef-8c0f0041191b"; + + /// + /// Guid for UploadVectorGraphics + /// + public static readonly Guid UploadVectorGraphicsGuid = new Guid(UploadVectorGraphics); + /// /// Guid for Label as string diff --git a/src/Umbraco.Core/Constants-Icons.cs b/src/Umbraco.Core/Constants-Icons.cs index 7885d89679..62e19008dd 100644 --- a/src/Umbraco.Core/Constants-Icons.cs +++ b/src/Umbraco.Core/Constants-Icons.cs @@ -59,6 +59,26 @@ /// public const string MediaFile = "icon-document"; + /// + /// System media video icon + /// + public const string MediaVideo = "icon-video"; + + /// + /// System media audio icon + /// + public const string MediaAudio = "icon-sound-waves"; + + /// + /// System media article icon + /// + public const string MediaArticle = "icon-article"; + + /// + /// System media vector icon + /// + public const string MediaVectorGraphics = "icon-picture"; + /// /// System media folder icon /// @@ -93,7 +113,7 @@ /// System packages icon /// public const string Packages = "icon-box"; - + /// /// System property editor icon /// diff --git a/src/Umbraco.Core/Constants-Indexes.cs b/src/Umbraco.Core/Constants-Indexes.cs index 8384faa08d..fcf2e7ed14 100644 --- a/src/Umbraco.Core/Constants-Indexes.cs +++ b/src/Umbraco.Core/Constants-Indexes.cs @@ -1,16 +1,12 @@ -namespace Umbraco.Cms.Core +namespace Umbraco.Cms.Core { public static partial class Constants { public static class UmbracoIndexes { - public const string InternalIndexName = InternalIndexPath + "Index"; - public const string ExternalIndexName = ExternalIndexPath + "Index"; - public const string MembersIndexName = MembersIndexPath + "Index"; - - public const string InternalIndexPath = "Internal"; - public const string ExternalIndexPath = "External"; - public const string MembersIndexPath = "Members"; + public const string InternalIndexName = "InternalIndex"; + public const string ExternalIndexName = "ExternalIndex"; + public const string MembersIndexName = "MembersIndex"; } } } diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index 4254e1791e..fa785f152f 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -101,6 +101,11 @@ namespace Umbraco.Cms.Core /// public const string MediaPicker = "Umbraco.MediaPicker"; + /// + /// Media Picker v.3. + /// + public const string MediaPicker3 = "Umbraco.MediaPicker3"; + /// /// Multiple Media Picker. /// diff --git a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs index a8dc3c84f8..46b41ea233 100644 --- a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs +++ b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs @@ -8,7 +8,7 @@ public static class PropertyTypeGroups { /// - /// Guid for a Image PropertyTypeGroup object. + /// Guid for an Image PropertyTypeGroup object. /// public const string Image = "79ED4D07-254A-42CF-8FA9-EBE1C116A596"; @@ -18,7 +18,27 @@ public const string File = "50899F9C-023A-4466-B623-ABA9049885FE"; /// - /// Guid for a Image PropertyTypeGroup object. + /// Guid for a Video PropertyTypeGroup object. + /// + public const string Video = "2F0A61B6-CF92-4FF4-B437-751AB35EB254"; + + /// + /// Guid for an Audio PropertyTypeGroup object. + /// + public const string Audio = "335FB495-0A87-4E82-B902-30EB367B767C"; + + /// + /// Guid for an Article PropertyTypeGroup object. + /// + public const string Article = "9AF3BD65-F687-4453-9518-5F180D1898EC"; + + /// + /// Guid for a VectorGraphics PropertyTypeGroup object. + /// + public const string VectorGraphics = "F199B4D7-9E84-439F-8531-F87D9AF37711"; + + /// + /// Guid for a Membership PropertyTypeGroup object. /// public const string Membership = "0756729D-D665-46E3-B84A-37ACEAA614F8"; } diff --git a/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs index 9fc60ce111..55e840ad8e 100644 --- a/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs +++ b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs @@ -10,22 +10,8 @@ namespace Umbraco.Cms.Core.Dashboards { public class DashboardCollectionBuilder : WeightedCollectionBuilderBase { - private Dictionary _customWeights = new Dictionary(); - protected override DashboardCollectionBuilder This => this; - /// - /// Changes the default weight of a dashboard - /// - /// The type of dashboard - /// The new dashboard weight - /// - public DashboardCollectionBuilder SetWeight(int weight) where T : IDashboard - { - _customWeights[typeof(T)] = weight; - return this; - } - protected override IEnumerable CreateItems(IServiceProvider factory) { // get the manifest parser just-in-time - injecting it in the ctor would mean that @@ -47,20 +33,13 @@ namespace Umbraco.Cms.Core.Dashboards private int GetWeight(IDashboard dashboard) { - var typeOfDashboard = dashboard.GetType(); - if(_customWeights.ContainsKey(typeOfDashboard)) - { - return _customWeights[typeOfDashboard]; - } - switch (dashboard) { case ManifestDashboard manifestDashboardDefinition: return manifestDashboardDefinition.Weight; default: - var weightAttribute = dashboard.GetType().GetCustomAttribute(false); - return weightAttribute?.Weight ?? DefaultWeight; + return GetWeight(dashboard.GetType()); } } } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs index 61802eaddd..db45fab96f 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs @@ -16,6 +16,7 @@ using Umbraco.Cms.Core.Sections; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Tour; using Umbraco.Cms.Core.Trees; +using Umbraco.Cms.Core.WebAssets; using Umbraco.Extensions; namespace Umbraco.Cms.Core.DependencyInjection @@ -114,6 +115,7 @@ namespace Umbraco.Cms.Core.DependencyInjection .Append() .Append(); builder.SearchableTrees().Add(() => builder.TypeLoader.GetTypes()); + builder.BackOfficeAssets(); } /// @@ -269,5 +271,11 @@ namespace Umbraco.Cms.Core.DependencyInjection /// public static SearchableTreeCollectionBuilder SearchableTrees(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); + + /// + /// Gets the back office custom assets collection builder + /// + public static CustomBackOfficeAssetsCollectionBuilder BackOfficeAssets(this IUmbracoBuilder builder) + => builder.WithCollectionBuilder(); } } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs index 6af917078c..441bc836da 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Events.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.DependencyInjection { diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 366c411ade..c513e9910b 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -27,12 +27,12 @@ using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Mail; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; @@ -141,11 +141,11 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(); this.AddAllCoreCollectionBuilders(); - this.AddNotificationHandler(); + this.AddNotificationHandler(); Services.AddSingleton(); Services.AddSingleton(); - this.AddNotificationAsyncHandler(); + this.AddNotificationAsyncHandler(); Services.AddUnique(); @@ -189,6 +189,8 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(); Services.AddUnique(); + Services.AddUnique(); + // register distributed cache Services.AddUnique(f => new DistributedCache(f.GetRequiredService(), f.GetRequiredService())); Services.AddUnique(); @@ -225,6 +227,8 @@ namespace Umbraco.Cms.Core.DependencyInjection Services .AddNotificationHandler() .AddNotificationHandler(); + + Services.AddSingleton(); } } } diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs index 7c6f4c1814..61bf7b2abf 100644 --- a/src/Umbraco.Core/Deploy/ArtifactBase.cs +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -1,22 +1,18 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; namespace Umbraco.Cms.Core.Deploy { /// /// Provides a base class to all artifacts. /// - [DataContract] public abstract class ArtifactBase : IArtifact where TUdi : Udi { protected ArtifactBase(TUdi udi, IEnumerable dependencies = null) { - if (udi == null) - throw new ArgumentNullException("udi"); - Udi = udi; + Udi = udi ?? throw new ArgumentNullException("udi"); Name = Udi.ToString(); Dependencies = dependencies ?? Enumerable.Empty(); @@ -24,38 +20,39 @@ namespace Umbraco.Cms.Core.Deploy } private readonly Lazy _checksum; + private IEnumerable _dependencies; protected abstract string GetChecksum(); #region Abstract implementation of IArtifactSignature - Udi IArtifactSignature.Udi - { - get { return Udi; } - } + Udi IArtifactSignature.Udi => Udi; - [DataMember] public TUdi Udi { get; set; } - [IgnoreDataMember] - public string Checksum - { - get { return _checksum.Value; } - } + public string Checksum => _checksum.Value; + + /// + /// Prevents the property from being serialized. + /// + /// + /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] + /// as that would require an external dependency in Umbraco.Cms.Core. + /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm + /// + public bool ShouldSerializeChecksum() => false; - [DataMember] public IEnumerable Dependencies { - get { return _dependencies; } - set { _dependencies = value.OrderBy(x => x.Udi); } + get => _dependencies; + set => _dependencies = value.OrderBy(x => x.Udi); } #endregion - [DataMember] public string Name { get; set; } - [DataMember] + public string Alias { get; set; } } } diff --git a/src/Umbraco.Core/EnvironmentHelper.cs b/src/Umbraco.Core/EnvironmentHelper.cs new file mode 100644 index 0000000000..097ffc9629 --- /dev/null +++ b/src/Umbraco.Core/EnvironmentHelper.cs @@ -0,0 +1,17 @@ +using System; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core +{ + /// + /// Currently just used to get the machine name for use with file names + /// + internal class EnvironmentHelper + { + /// + /// Returns the machine name that is safe to use in file paths. + /// + public static string FileSafeMachineName => Environment.MachineName.ReplaceNonAlphanumericChars('-'); + + } +} diff --git a/src/Umbraco.Core/Events/EventAggregator.Notifications.cs b/src/Umbraco.Core/Events/EventAggregator.Notifications.cs index 859edd4997..58ccb06ed0 100644 --- a/src/Umbraco.Core/Events/EventAggregator.Notifications.cs +++ b/src/Umbraco.Core/Events/EventAggregator.Notifications.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/EventAggregator.cs b/src/Umbraco.Core/Events/EventAggregator.cs index f4602039dd..f762d62109 100644 --- a/src/Umbraco.Core/Events/EventAggregator.cs +++ b/src/Umbraco.Core/Events/EventAggregator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/IEventAggregator.cs b/src/Umbraco.Core/Events/IEventAggregator.cs index d59f75b040..c654bb6c86 100644 --- a/src/Umbraco.Core/Events/IEventAggregator.cs +++ b/src/Umbraco.Core/Events/IEventAggregator.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/INotificationHandler.cs b/src/Umbraco.Core/Events/INotificationHandler.cs index 25aea986c6..d362477733 100644 --- a/src/Umbraco.Core/Events/INotificationHandler.cs +++ b/src/Umbraco.Core/Events/INotificationHandler.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs index 96f8e771c0..7df9167ce6 100644 --- a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs index de82fc1f24..e79cd67cd8 100644 --- a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs +++ b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs @@ -11,11 +11,11 @@ namespace Umbraco.Cms.Core.Events /// public class QueuingEventDispatcher : QueuingEventDispatcherBase { - private readonly IMediaFileSystem _mediaFileSystem; - public QueuingEventDispatcher(IMediaFileSystem mediaFileSystem) + private readonly MediaFileManager _mediaFileManager; + public QueuingEventDispatcher(MediaFileManager mediaFileManager) : base(true) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; } protected override void ScopeExitCompleted() @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.Events // but then where should it be (without making things too complicated)? var delete = e.Args as IDeletingMediaFilesEventArgs; if (delete != null && delete.MediaFilesToDelete.Count > 0) - _mediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete); + _mediaFileManager.DeleteMediaFiles(delete.MediaFilesToDelete); } } diff --git a/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs b/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs index caa3777d39..0a6518a1ca 100644 --- a/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs +++ b/src/Umbraco.Core/Events/RelateOnCopyNotificationHandler.cs @@ -2,8 +2,8 @@ // See LICENSE for more details. using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs index 206706716c..c9b0080218 100644 --- a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Events { diff --git a/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs b/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs deleted file mode 100644 index 2e7d8c768c..0000000000 --- a/src/Umbraco.Core/Events/UmbracoApplicationStarting.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -namespace Umbraco.Cms.Core.Events -{ - - public class UmbracoApplicationStarting : INotification - { - /// - /// Initializes a new instance of the class. - /// - /// The runtime level - public UmbracoApplicationStarting(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; - - /// - /// Gets the runtime level of execution. - /// - public RuntimeLevel RuntimeLevel { get; } - } -} diff --git a/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs b/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs deleted file mode 100644 index 54ce079012..0000000000 --- a/src/Umbraco.Core/Events/UmbracoApplicationStopping.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Umbraco.Cms.Core.Events -{ - public class UmbracoApplicationStopping : INotification { } -} diff --git a/src/Umbraco.Core/Events/UserNotificationsHandler.cs b/src/Umbraco.Core/Events/UserNotificationsHandler.cs index 1a9a457636..f5172d3e28 100644 --- a/src/Umbraco.Core/Events/UserNotificationsHandler.cs +++ b/src/Umbraco.Core/Events/UserNotificationsHandler.cs @@ -13,9 +13,9 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Events diff --git a/src/Umbraco.Core/Extensions/ContentExtensions.cs b/src/Umbraco.Core/Extensions/ContentExtensions.cs index 7794d60b15..5aaad5e303 100644 --- a/src/Umbraco.Core/Extensions/ContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentExtensions.cs @@ -204,7 +204,7 @@ namespace Umbraco.Extensions /// /// Sets the posted file value of a property. /// - public static void SetValue(this IContentBase content, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + public static void SetValue(this IContentBase content, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { if (filename == null || filestream == null) return; @@ -212,10 +212,10 @@ namespace Umbraco.Extensions if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); - SetUploadFile(content, mediaFileSystem, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); + SetUploadFile(content, mediaFileManager, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); } - private static void SetUploadFile(this IContentBase content, IMediaFileSystem mediaFileSystem, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + private static void SetUploadFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { var property = GetProperty(content, contentTypeBaseServiceProvider, propertyTypeAlias); @@ -229,11 +229,11 @@ namespace Umbraco.Extensions // the property value is a JSON serialized image crop data set - grab the "src" property as the file source svalue = serializer.DeserializeSubset(svalue, "src"); } - oldpath = mediaFileSystem.GetRelativePath(svalue); + oldpath = mediaFileManager.FileSystem.GetRelativePath(svalue); } - var filepath = mediaFileSystem.StoreFile(content, property.PropertyType, filename, filestream, oldpath); - property.SetValue(mediaFileSystem.GetUrl(filepath), culture, segment); + var filepath = mediaFileManager.StoreFile(content, property.PropertyType, filename, filestream, oldpath); + property.SetValue(mediaFileManager.FileSystem.GetUrl(filepath), culture, segment); } // gets or creates a property for a content item. @@ -269,13 +269,13 @@ namespace Umbraco.Extensions /// the "folder number" that was assigned to the previous file referenced by the property, /// if any. /// - public static string StoreFile(this IContentBase content, IMediaFileSystem mediaFileSystem, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string filepath) + public static string StoreFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string filepath) { var contentType = contentTypeBaseServiceProvider.GetContentTypeOf(content); var propertyType = contentType .CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + "."); - return mediaFileSystem.StoreFile(content, propertyType, filename, filestream, filepath); + return mediaFileManager.StoreFile(content, propertyType, filename, filestream, filepath); } #endregion diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index 433278b8c0..8c4e572442 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -262,7 +262,7 @@ namespace Umbraco.Extensions // else... if we have a property, at least let the converter return its own // vision of 'no value' (could be an empty enumerable) - otherwise, default - return property == null ? default : property.Value(publishedValueFallback, culture, segment, fallback, defaultValue); + return property == null ? default : property.Value(publishedValueFallback, culture, segment); } #endregion diff --git a/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs b/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs index f10c22ef08..b714caf744 100644 --- a/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedModelFactoryExtensions.cs @@ -16,7 +16,7 @@ namespace Umbraco.Extensions /// public static bool IsLiveFactoryEnabled(this IPublishedModelFactory factory) { - if (factory is ILivePublishedModelFactory liveFactory) + if (factory is IAutoPublishedModelFactory liveFactory) { return liveFactory.Enabled; } @@ -26,14 +26,14 @@ namespace Umbraco.Extensions } /// - /// Sets a flag to reset the ModelsBuilder models if the is + /// Sets a flag to reset the ModelsBuilder models if the is /// /// - /// This does not recompile the pure live models, only sets a flag to tell models builder to recompile when they are requested. + /// This does not recompile the InMemory models, only sets a flag to tell models builder to recompile when they are requested. /// internal static void WithSafeLiveFactoryReset(this IPublishedModelFactory factory, Action action) { - if (factory is ILivePublishedModelFactory liveFactory) + if (factory is IAutoPublishedModelFactory liveFactory) { lock (liveFactory.SyncRoot) { diff --git a/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs b/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs index 46000ab3aa..c559fc9a74 100644 --- a/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs +++ b/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs @@ -32,16 +32,9 @@ namespace Umbraco.Extensions // we have a value // try to cast or convert it var value = property.GetValue(culture, segment); - if (value is T valueAsT) - { - return valueAsT; - } - + if (value is T valueAsT) return valueAsT; var valueConverted = value.TryConvertTo(); - if (valueConverted) - { - return valueConverted.Result; - } + if (valueConverted) return valueConverted.Result; // cannot cast nor convert the value, nothing we can return but 'default' // note: we don't want to fallback in that case - would make little sense @@ -50,28 +43,14 @@ namespace Umbraco.Extensions // we don't have a value, try fallback if (publishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue)) - { return fallbackValue; - } // we don't have a value - neither direct nor fallback // give a chance to the converter to return something (eg empty enumerable) var noValue = property.GetValue(culture, segment); - if (noValue == null) - { - return default; - } - - if (noValue is T noValueAsT) - { - return noValueAsT; - } - + if (noValue is T noValueAsT) return noValueAsT; var noValueConverted = noValue.TryConvertTo(); - if (noValueConverted) - { - return noValueConverted.Result; - } + if (noValueConverted) return noValueConverted.Result; // cannot cast noValue nor convert it, nothing we can return but 'default' return default; diff --git a/src/Umbraco.Core/Extensions/SemVersionExtensions.cs b/src/Umbraco.Core/Extensions/SemVersionExtensions.cs index 85e4892a09..e8b2a2534b 100644 --- a/src/Umbraco.Core/Extensions/SemVersionExtensions.cs +++ b/src/Umbraco.Core/Extensions/SemVersionExtensions.cs @@ -11,5 +11,12 @@ namespace Umbraco.Extensions { return semVersion.ToString().Replace("--", "-").Replace("-+", "+"); } + + public static string ToSemanticStringWithoutBuild(this SemVersion semVersion) + { + var version = semVersion.ToSemanticString(); + var indexOfBuild = version.IndexOf('+'); + return indexOfBuild >= 0 ? version.Substring(0, indexOfBuild) : version; + } } } diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs index 3b42426cd6..e815f219ca 100644 --- a/src/Umbraco.Core/Extensions/StringExtensions.cs +++ b/src/Umbraco.Core/Extensions/StringExtensions.cs @@ -616,12 +616,7 @@ namespace Umbraco.Extensions /// /// Refers to itself /// The hashed string - public static string GenerateHash(this string str) - { - return CryptoConfig.AllowOnlyFipsAlgorithms - ? str.ToSHA1() - : str.ToMd5(); - } + public static string GenerateHash(this string str) => str.ToSHA1(); /// /// Generate a hash of a string based on the specified hash algorithm. @@ -632,30 +627,14 @@ namespace Umbraco.Extensions /// The hashed string. /// public static string GenerateHash(this string str) - where T : HashAlgorithm - { - return str.GenerateHash(typeof(T).FullName); - } - - /// - /// Converts the string to MD5 - /// - /// Refers to itself - /// The MD5 hashed string - public static string ToMd5(this string stringToConvert) - { - return stringToConvert.GenerateHash("MD5"); - } + where T : HashAlgorithm => str.GenerateHash(typeof(T).FullName); /// /// Converts the string to SHA1 /// /// refers to itself /// The SHA1 hashed string - public static string ToSHA1(this string stringToConvert) - { - return stringToConvert.GenerateHash("SHA1"); - } + public static string ToSHA1(this string stringToConvert) => stringToConvert.GenerateHash("SHA1"); /// Generate a hash of a string based on the hashType passed in /// diff --git a/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs b/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs index 2a1322f925..ed7fdb971f 100644 --- a/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs +++ b/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs @@ -7,9 +7,9 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Net; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Handlers diff --git a/src/Umbraco.Core/Handlers/PublicAccessHandler.cs b/src/Umbraco.Core/Handlers/PublicAccessHandler.cs index 9645e9a88c..b50375adfe 100644 --- a/src/Umbraco.Core/Handlers/PublicAccessHandler.cs +++ b/src/Umbraco.Core/Handlers/PublicAccessHandler.cs @@ -2,8 +2,8 @@ using System; using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Handlers diff --git a/src/Umbraco.Core/HealthChecks/Checks/Configuration/MacroErrorsCheck.cs b/src/Umbraco.Core/HealthChecks/Checks/Configuration/MacroErrorsCheck.cs index 3237879a03..7359a60341 100644 --- a/src/Umbraco.Core/HealthChecks/Checks/Configuration/MacroErrorsCheck.cs +++ b/src/Umbraco.Core/HealthChecks/Checks/Configuration/MacroErrorsCheck.cs @@ -1,10 +1,11 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; @@ -56,12 +57,12 @@ namespace Umbraco.Cms.Core.HealthChecks.Checks.Configuration new AcceptableConfiguration { IsRecommended = true, - Value = "inline" + Value = MacroErrorBehaviour.Inline.ToString() }, new AcceptableConfiguration { IsRecommended = false, - Value = "silent" + Value = MacroErrorBehaviour.Silent.ToString() } }; diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs index 311d7559d0..dedf809230 100644 --- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs +++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs @@ -6,6 +6,21 @@ namespace Umbraco.Cms.Core.Hosting { string SiteName { get; } + /// + /// The unique application ID for this Umbraco website. + /// + /// + /// + /// The returned value will be the same consistent value for an Umbraco website on a specific server and will the same + /// between restarts of that Umbraco website/application on that specific server. + /// + /// + /// The value of this does not necesarily distinguish between unique workers/servers for this Umbraco application. + /// Usage of this must take into account that the same may be returned for the same + /// Umbraco website hosted on different servers. Similarly the usage of this must take into account that a different + /// may be returned for the same Umbraco website hosted on different servers. + /// + /// string ApplicationId { get; } /// diff --git a/src/Umbraco.Core/HybridAccessorBase.cs b/src/Umbraco.Core/HybridAccessorBase.cs index 530d1d5689..1bb7635dcc 100644 --- a/src/Umbraco.Core/HybridAccessorBase.cs +++ b/src/Umbraco.Core/HybridAccessorBase.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Scoping; @@ -16,9 +17,10 @@ namespace Umbraco.Cms.Core public abstract class HybridAccessorBase where T : class { + private static readonly AsyncLocal s_ambientContext = new AsyncLocal(); + private readonly IRequestCache _requestCache; private string _itemKey; - protected string ItemKey => _itemKey ??= GetType().FullName; // read @@ -38,8 +40,8 @@ namespace Umbraco.Cms.Core // yes! flows with async! private T NonContextValue { - get => CallContext.GetData(ItemKey); - set => CallContext.SetData(ItemKey, value); + get => s_ambientContext.Value ?? default; + set => s_ambientContext.Value = value; } protected HybridAccessorBase(IRequestCache requestCache) diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index e688ca22da..6f1f47007f 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -66,33 +66,5 @@ namespace Umbraco.Extensions } fs.DeleteFile(tempFile); } - - /// - /// Unwraps a filesystem. - /// - /// - /// A filesystem can be wrapped in a (public) or a (internal), - /// and this method deals with the various wrappers and - /// - public static IFileSystem Unwrap(this IFileSystem filesystem) - { - var unwrapping = true; - while (unwrapping) - { - switch (filesystem) - { - case FileSystemWrapper wrapper: - filesystem = wrapper.InnerFileSystem; - break; - case ShadowWrapper shadow: - filesystem = shadow.InnerFileSystem; - break; - default: - unwrapping = false; - break; - } - } - return filesystem; - } } } diff --git a/src/Umbraco.Core/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs deleted file mode 100644 index 34276131c7..0000000000 --- a/src/Umbraco.Core/IO/FileSystemWrapper.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// All custom file systems that are based upon another IFileSystem should inherit from FileSystemWrapper - /// - /// - /// An IFileSystem is generally used as a base file system, for example like a PhysicalFileSystem or an S3FileSystem. - /// Then, other custom file systems are wrapped upon these files systems like MediaFileSystem, etc... All of the custom - /// file systems must inherit from FileSystemWrapper. - /// - /// This abstract class just wraps the 'real' IFileSystem object passed in to its constructor. - /// - public abstract class FileSystemWrapper : IFileSystem - { - protected FileSystemWrapper(IFileSystem innerFileSystem) - { - InnerFileSystem = innerFileSystem; - } - - internal IFileSystem InnerFileSystem { get; set; } - - public virtual IEnumerable GetDirectories(string path) - { - return InnerFileSystem.GetDirectories(path); - } - - public virtual void DeleteDirectory(string path) - { - InnerFileSystem.DeleteDirectory(path); - } - - public virtual void DeleteDirectory(string path, bool recursive) - { - InnerFileSystem.DeleteDirectory(path, recursive); - } - - public virtual bool DirectoryExists(string path) - { - return InnerFileSystem.DirectoryExists(path); - } - - public virtual void AddFile(string path, Stream stream) - { - InnerFileSystem.AddFile(path, stream); - } - - public virtual void AddFile(string path, Stream stream, bool overrideExisting) - { - InnerFileSystem.AddFile(path, stream, overrideExisting); - } - - public virtual IEnumerable GetFiles(string path) - { - return InnerFileSystem.GetFiles(path); - } - - public virtual IEnumerable GetFiles(string path, string filter) - { - return InnerFileSystem.GetFiles(path, filter); - } - - public virtual Stream OpenFile(string path) - { - return InnerFileSystem.OpenFile(path); - } - - public virtual void DeleteFile(string path) - { - InnerFileSystem.DeleteFile(path); - } - - public virtual bool FileExists(string path) - { - return InnerFileSystem.FileExists(path); - } - - public virtual string GetRelativePath(string fullPathOrUrl) - { - return InnerFileSystem.GetRelativePath(fullPathOrUrl); - } - - public virtual string GetFullPath(string path) - { - return InnerFileSystem.GetFullPath(path); - } - - public virtual string GetUrl(string path) - { - return InnerFileSystem.GetUrl(path); - } - - public virtual DateTimeOffset GetLastModified(string path) - { - return InnerFileSystem.GetLastModified(path); - } - - public virtual DateTimeOffset GetCreated(string path) - { - return InnerFileSystem.GetCreated(path); - } - - public virtual long GetSize(string path) - { - return InnerFileSystem.GetSize(path); - } - - public virtual bool CanAddPhysical => InnerFileSystem.CanAddPhysical; - - public virtual void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false) - { - InnerFileSystem.AddFile(path, physicalPath, overrideIfExists, copy); - } - } -} diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 52ee2fe0d3..1b4db6acec 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -1,23 +1,25 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.ComponentModel; using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; -using Umbraco.Extensions; namespace Umbraco.Cms.Core.IO { - public class FileSystems : IFileSystems + /// + /// Provides the system filesystems. + /// + public sealed class FileSystems { - private readonly IServiceProvider _container; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly IIOHelper _ioHelper; + private GlobalSettings _globalSettings; + private readonly IHostingEnvironment _hostingEnvironment; - private readonly ConcurrentDictionary> _filesystems = new ConcurrentDictionary>(); // wrappers for shadow support private ShadowWrapper _macroPartialFileSystem; @@ -38,93 +40,175 @@ namespace Umbraco.Cms.Core.IO #region Constructor // DI wants a public ctor - public FileSystems(IServiceProvider container, ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, IOptions globalSettings, IHostingEnvironment hostingEnvironment) + public FileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment) { - _container = container; - _logger = logger; + _logger = loggerFactory.CreateLogger(); _loggerFactory = loggerFactory; _ioHelper = ioHelper; _globalSettings = globalSettings.Value; _hostingEnvironment = hostingEnvironment; } - // for tests only, totally unsafe - internal void Reset() + // Ctor for tests, allows you to set the various filesystems + internal FileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment, + IFileSystem macroPartialFileSystem, + IFileSystem partialViewsFileSystem, + IFileSystem stylesheetFileSystem, + IFileSystem scriptsFileSystem, + IFileSystem mvcViewFileSystem) : this(loggerFactory, ioHelper, globalSettings, hostingEnvironment) { - _shadowWrappers.Clear(); - _filesystems.Clear(); - Volatile.Write(ref _wkfsInitialized, false); - _shadowCurrentId = null; + _macroPartialFileSystem = CreateShadowWrapperInternal(macroPartialFileSystem, "macro-partials"); + _partialViewsFileSystem = CreateShadowWrapperInternal(partialViewsFileSystem, "partials"); + _stylesheetsFileSystem = CreateShadowWrapperInternal(stylesheetFileSystem, "css"); + _scriptsFileSystem = CreateShadowWrapperInternal(scriptsFileSystem, "scripts"); + _mvcViewsFileSystem = CreateShadowWrapperInternal(mvcViewFileSystem, "view"); + // Set initialized to true so the filesystems doesn't get overwritten. + _wkfsInitialized = true; + } - // for tests only, totally unsafe - internal static void ResetShadowId() - { - _shadowCurrentId = null; - } - - // set by the scope provider when taking control of filesystems + /// + /// Used be Scope provider to take control over the filesystems, should never be used for anything else. + /// + [EditorBrowsable(EditorBrowsableState.Never)] public Func IsScoped { get; set; } = () => false; #endregion #region Well-Known FileSystems - /// + /// + /// Gets the macro partials filesystem. + /// public IFileSystem MacroPartialsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _macroPartialFileSystem; } } - /// + /// + /// Gets the partial views filesystem. + /// public IFileSystem PartialViewsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _partialViewsFileSystem; } } - /// + /// + /// Gets the stylesheets filesystem. + /// public IFileSystem StylesheetsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _stylesheetsFileSystem; } } - /// + /// + /// Gets the scripts filesystem. + /// public IFileSystem ScriptsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _scriptsFileSystem; } } - /// + /// + /// Gets the MVC views filesystem. + /// public IFileSystem MvcViewsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _mvcViewsFileSystem; } } - private void EnsureWellKnownFileSystems() + /// + /// Sets the stylesheet filesystem. + /// + /// + /// Be careful when using this, the root path and root url must be correct for this to work. + /// + /// The . + /// If the is null + /// Throws exception if the StylesheetFileSystem has already been initialized. + /// Throws exception if full path can't be resolved successfully. + public void SetStylesheetFilesystem(IFileSystem fileSystem) { - LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + if (fileSystem == null) + { + throw new ArgumentNullException(nameof(fileSystem)); + } + + if (_stylesheetsFileSystem != null) + { + throw new InvalidOperationException( + "The StylesheetFileSystem cannot be changed when it's already been initialized."); + } + + // Verify that _rootUrl/_rootPath is correct + // We have to do this because there's a tight coupling + // to the VirtualPath we get with CodeFileDisplay from the frontend. + try + { + var rootPath = fileSystem.GetFullPath("/css/"); + } + catch (UnauthorizedAccessException exception) + { + throw new UnauthorizedAccessException( + "Can't register the stylesheet filesystem, " + + "this is most likely caused by using a PhysicalFileSystem with an incorrect " + + "rootPath/rootUrl. RootPath must be \\wwwroot\\css" + + " and rootUrl must be /css", exception); + } + + _stylesheetsFileSystem = CreateShadowWrapperInternal(fileSystem, "css"); } + private void EnsureWellKnownFileSystems() => LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + // need to return something to LazyInitializer.EnsureInitialized // but it does not really matter what we return - here, null private object CreateWellKnownFileSystems() @@ -134,20 +218,28 @@ namespace Umbraco.Cms.Core.IO //TODO this is fucked, why do PhysicalFileSystem has a root url? Mvc views cannot be accessed by url! var macroPartialFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MacroPartials)); var partialViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.PartialViews)); - var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); var scriptsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoScriptsPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoScriptsPath)); var mvcViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MvcViews)); _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "macro-partials", IsScoped); _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "partials", IsScoped); - _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "css", IsScoped); _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "scripts", IsScoped); _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "views", IsScoped); + if (_stylesheetsFileSystem == null) + { + var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, + _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), + _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); + + _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "css", IsScoped); + + _shadowWrappers.Add(_stylesheetsFileSystem); + } + // TODO: do we need a lock here? _shadowWrappers.Add(_macroPartialFileSystem); _shadowWrappers.Add(_partialViewsFileSystem); - _shadowWrappers.Add(_stylesheetsFileSystem); _shadowWrappers.Add(_scriptsFileSystem); _shadowWrappers.Add(_mvcViewsFileSystem); @@ -156,64 +248,6 @@ namespace Umbraco.Cms.Core.IO #endregion - #region Providers - - private readonly Dictionary _paths = new Dictionary(); - - // internal for tests - internal IReadOnlyDictionary Paths => _paths; - private GlobalSettings _globalSettings; - private readonly IHostingEnvironment _hostingEnvironment; - - /// - /// Gets a strongly-typed filesystem. - /// - /// The type of the filesystem. - /// A strongly-typed filesystem of the specified type. - /// - /// Note that any filesystem created by this method *after* shadowing begins, will *not* be - /// shadowing (and an exception will be thrown by the ShadowWrapper). - /// - public TFileSystem GetFileSystem(IFileSystem supporting) - where TFileSystem : FileSystemWrapper - { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - - return (TFileSystem) _filesystems.GetOrAdd(typeof(TFileSystem), _ => new Lazy(() => - { - var typeofTFileSystem = typeof(TFileSystem); - - // path must be unique and not collide with paths used in CreateWellKnownFileSystems - // for our well-known 'media' filesystem we can use the short 'media' path - // for others, put them under 'x/' and use ... something - string path; - if (typeofTFileSystem == typeof(MediaFileSystem)) - { - path = "media"; - } - else - { - lock (_paths) - { - if (!_paths.TryGetValue(typeofTFileSystem, out path)) - { - path = Guid.NewGuid().ToString("N").Substring(0, 6); - while (_paths.ContainsValue(path)) // this can't loop forever, right? - path = Guid.NewGuid().ToString("N").Substring(0, 6); - _paths[typeofTFileSystem] = path; - } - } - - path = "x/" + path; - } - - var shadowWrapper = CreateShadowWrapper(supporting, path); - return _container.CreateInstance(shadowWrapper); - })).Value; - } - - #endregion - #region Shadow // note @@ -221,9 +255,17 @@ namespace Umbraco.Cms.Core.IO // global shadow for the entire application, so great care should be taken to ensure that the // application is *not* doing anything else when using a shadow. + /// + /// Shadows the filesystem, should never be used outside the Scope class. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] public ICompletable Shadow() { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } var id = ShadowWrapper.CreateShadowId(_hostingEnvironment); return new ShadowFileSystems(this, id); // will invoke BeginShadow and EndShadow @@ -235,14 +277,18 @@ namespace Umbraco.Cms.Core.IO { // if we throw here, it means that something very wrong happened. if (_shadowCurrentId != null) + { throw new InvalidOperationException("Already shadowing."); + } _shadowCurrentId = id; _logger.LogDebug("Shadow '{ShadowId}'", _shadowCurrentId); - foreach (var wrapper in _shadowWrappers) + foreach (ShadowWrapper wrapper in _shadowWrappers) + { wrapper.Shadow(_shadowCurrentId); + } } } @@ -252,14 +298,19 @@ namespace Umbraco.Cms.Core.IO { // if we throw here, it means that something very wrong happened. if (_shadowCurrentId == null) + { throw new InvalidOperationException("Not shadowing."); + } + if (id != _shadowCurrentId) + { throw new InvalidOperationException("Not the current shadow."); + } _logger.LogDebug("UnShadow '{ShadowId}' {Status}", id, completed ? "complete" : "abort"); var exceptions = new List(); - foreach (var wrapper in _shadowWrappers) + foreach (ShadowWrapper wrapper in _shadowWrappers) { try { @@ -275,17 +326,31 @@ namespace Umbraco.Cms.Core.IO _shadowCurrentId = null; if (exceptions.Count > 0) + { throw new AggregateException(completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions); + } } } - private ShadowWrapper CreateShadowWrapper(IFileSystem filesystem, string shadowPath) + /// + /// Creates a shadow wrapper for a filesystem, should never be used outside UmbracoBuilder or testing + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public IFileSystem CreateShadowWrapper(IFileSystem filesystem, string shadowPath) => CreateShadowWrapperInternal(filesystem, shadowPath); + + private ShadowWrapper CreateShadowWrapperInternal(IFileSystem filesystem, string shadowPath) { lock (_shadowLocker) { var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath,() => IsScoped()); if (_shadowCurrentId != null) + { wrapper.Shadow(_shadowCurrentId); + } + _shadowWrappers.Add(wrapper); return wrapper; } diff --git a/src/Umbraco.Core/IO/IFileSystems.cs b/src/Umbraco.Core/IO/IFileSystems.cs deleted file mode 100644 index 3a169e33a3..0000000000 --- a/src/Umbraco.Core/IO/IFileSystems.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Umbraco.Cms.Core.IO -{ - /// - /// Provides the system filesystems. - /// - public interface IFileSystems - { - /// - /// Gets the macro partials filesystem. - /// - IFileSystem MacroPartialsFileSystem { get; } - - /// - /// Gets the partial views filesystem. - /// - IFileSystem PartialViewsFileSystem { get; } - - /// - /// Gets the stylesheets filesystem. - /// - IFileSystem StylesheetsFileSystem { get; } - - /// - /// Gets the scripts filesystem. - /// - IFileSystem ScriptsFileSystem { get; } - - /// - /// Gets the MVC views filesystem. - /// - IFileSystem MvcViewsFileSystem { get; } - } -} diff --git a/src/Umbraco.Core/IO/IMediaFileSystem.cs b/src/Umbraco.Core/IO/IMediaFileSystem.cs deleted file mode 100644 index eced74482e..0000000000 --- a/src/Umbraco.Core/IO/IMediaFileSystem.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// Provides methods allowing the manipulation of media files. - /// - public interface IMediaFileSystem : IFileSystem - { - /// - /// Delete media files. - /// - /// Files to delete (filesystem-relative paths). - void DeleteMediaFiles(IEnumerable files); - - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// With the old media path scheme, this CREATES a new media path each time it is invoked. - string GetMediaPath(string filename, Guid cuid, Guid puid); - - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// A previous file path. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// In the old, legacy, number-based scheme, we try to re-use the media folder - /// specified by . Else, we CREATE a new one. Each time we are invoked. - string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid); - - /// - /// Stores a media file associated to a property of a content item. - /// - /// The content item owning the media file. - /// The property type owning the media file. - /// The media file name. - /// A stream containing the media bytes. - /// An optional filesystem-relative filepath to the previous media file. - /// The filesystem-relative filepath to the media file. - /// - /// The file is considered "owned" by the content/propertyType. - /// If an is provided then that file (and associated thumbnails if any) is deleted - /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. - /// - string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath); - - /// - /// Copies a media file as a new media file, associated to a property of a content item. - /// - /// The content item owning the copy of the media file. - /// The property type owning the copy of the media file. - /// The filesystem-relative path to the source media file. - /// The filesystem-relative path to the copy of the media file. - string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath); - } -} diff --git a/src/Umbraco.Core/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs index bd48b21a16..2b2696ec5d 100644 --- a/src/Umbraco.Core/IO/IMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/IMediaPathScheme.cs @@ -10,13 +10,13 @@ namespace Umbraco.Cms.Core.IO /// /// Gets a media file path. /// - /// The media filesystem. + /// The media filesystem. /// The (content, media) item unique identifier. /// The property type unique identifier. /// The file name. /// A previous filename. /// The filesystem-relative complete file path. - string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); + string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); /// /// Gets the directory that can be deleted when the file is deleted. @@ -28,6 +28,6 @@ namespace Umbraco.Cms.Core.IO /// The directory, and anything below it, will be deleted. /// Can return null (or empty) when no directory should be deleted. /// - string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath); + string GetDeleteDirectory(MediaFileManager fileSystem, string filepath); } } diff --git a/src/Umbraco.Core/IO/MediaFileManager.cs b/src/Umbraco.Core/IO/MediaFileManager.cs new file mode 100644 index 0000000000..de89d7ca87 --- /dev/null +++ b/src/Umbraco.Core/IO/MediaFileManager.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Strings; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.IO +{ + public sealed class MediaFileManager + { + private readonly IMediaPathScheme _mediaPathScheme; + private readonly ILogger _logger; + private readonly IShortStringHelper _shortStringHelper; + + /// + /// Gets the media filesystem. + /// + public IFileSystem FileSystem { get; } + + public MediaFileManager( + IFileSystem fileSystem, + IMediaPathScheme mediaPathScheme, + ILogger logger, + IShortStringHelper shortStringHelper) + { + _mediaPathScheme = mediaPathScheme; + _logger = logger; + _shortStringHelper = shortStringHelper; + FileSystem = fileSystem; + } + + /// + /// Delete media files. + /// + /// Files to delete (filesystem-relative paths). + public void DeleteMediaFiles(IEnumerable files) + { + files = files.Distinct(); + + // kinda try to keep things under control + var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; + + Parallel.ForEach(files, options, file => + { + try + { + if (file.IsNullOrWhiteSpace()) + { + return; + } + + if (FileSystem.FileExists(file) == false) + { + return; + } + + FileSystem.DeleteFile(file); + + var directory = _mediaPathScheme.GetDeleteDirectory(this, file); + if (!directory.IsNullOrWhiteSpace()) + { + FileSystem.DeleteDirectory(directory, true); + } + } + catch (Exception e) + { + _logger.LogError(e, "Failed to delete media file '{File}'.", file); + } + }); + } + + #region Media Path + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// With the old media path scheme, this CREATES a new media path each time it is invoked. + public string GetMediaPath(string filename, Guid cuid, Guid puid) + { + filename = Path.GetFileName(filename); + if (filename == null) + { + throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); + } + + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); + + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); + } + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// A previous file path. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// In the old, legacy, number-based scheme, we try to re-use the media folder + /// specified by . Else, we CREATE a new one. Each time we are invoked. + public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) + { + filename = Path.GetFileName(filename); + if (filename == null) + { + throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); + } + + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); + + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); + } + + #endregion + + #region Associated Media Files + + /// + /// Stores a media file associated to a property of a content item. + /// + /// The content item owning the media file. + /// The property type owning the media file. + /// The media file name. + /// A stream containing the media bytes. + /// An optional filesystem-relative filepath to the previous media file. + /// The filesystem-relative filepath to the media file. + /// + /// The file is considered "owned" by the content/propertyType. + /// If an is provided then that file (and associated thumbnails if any) is deleted + /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. + /// + public string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (filename == null) + { + throw new ArgumentNullException(nameof(filename)); + } + + if (string.IsNullOrWhiteSpace(filename)) + { + throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); + } + + if (filestream == null) + { + throw new ArgumentNullException(nameof(filestream)); + } + + // clear the old file, if any + if (string.IsNullOrWhiteSpace(oldpath) == false) + { + FileSystem.DeleteFile(oldpath); + } + + // get the filepath, store the data + // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme + var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); + FileSystem.AddFile(filepath, filestream); + return filepath; + } + + /// + /// Copies a media file as a new media file, associated to a property of a content item. + /// + /// The content item owning the copy of the media file. + /// The property type owning the copy of the media file. + /// The filesystem-relative path to the source media file. + /// The filesystem-relative path to the copy of the media file. + public string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (sourcepath == null) + { + throw new ArgumentNullException(nameof(sourcepath)); + } + + if (string.IsNullOrWhiteSpace(sourcepath)) + { + throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); + } + + // ensure we have a file to copy + if (FileSystem.FileExists(sourcepath) == false) + { + return null; + } + + // get the filepath + var filename = Path.GetFileName(sourcepath); + var filepath = GetMediaPath(filename, content.Key, propertyType.Key); + FileSystem.CopyFile(sourcepath, filepath); + return filepath; + } + + #endregion + } +} diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs deleted file mode 100644 index 6d598941c5..0000000000 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Strings; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// A custom file system provider for media - /// - public class MediaFileSystem : FileSystemWrapper, IMediaFileSystem - { - private readonly IMediaPathScheme _mediaPathScheme; - private readonly ILogger _logger; - private readonly IShortStringHelper _shortStringHelper; - - /// - /// Initializes a new instance of the class. - /// - public MediaFileSystem(IFileSystem innerFileSystem, IMediaPathScheme mediaPathScheme, ILogger logger, IShortStringHelper shortStringHelper) - : base(innerFileSystem) - { - _mediaPathScheme = mediaPathScheme; - _logger = logger; - _shortStringHelper = shortStringHelper; - } - - /// - public void DeleteMediaFiles(IEnumerable files) - { - files = files.Distinct(); - - // kinda try to keep things under control - var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; - - Parallel.ForEach(files, options, file => - { - try - { - if (file.IsNullOrWhiteSpace()) return; - if (FileExists(file) == false) return; - DeleteFile(file); - - var directory = _mediaPathScheme.GetDeleteDirectory(this, file); - if (!directory.IsNullOrWhiteSpace()) - DeleteDirectory(directory, true); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to delete media file '{File}'.", file); - } - }); - } - - #region Media Path - - /// - public string GetMediaPath(string filename, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); - } - - /// - public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); - } - - #endregion - - #region Associated Media Files - - /// - public string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (filename == null) throw new ArgumentNullException(nameof(filename)); - if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); - if (filestream == null) throw new ArgumentNullException(nameof(filestream)); - - // clear the old file, if any - if (string.IsNullOrWhiteSpace(oldpath) == false) - DeleteFile(oldpath); - - // get the filepath, store the data - // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme - var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); - AddFile(filepath, filestream); - return filepath; - } - - /// - public string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (sourcepath == null) throw new ArgumentNullException(nameof(sourcepath)); - if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); - - // ensure we have a file to copy - if (FileExists(sourcepath) == false) return null; - - // get the filepath - var filename = Path.GetFileName(sourcepath); - var filepath = GetMediaPath(filename, content.Key, propertyType.Key); - this.CopyFile(sourcepath, filepath); - return filepath; - } - - #endregion - } -} diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index a23468d5ac..f3c15a13d6 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class CombinedGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name @@ -23,6 +23,6 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => Path.GetDirectoryName(filepath); + public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) => Path.GetDirectoryName(filepath); } } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs index ea23bf0145..d1cea0c46b 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private bool _folderCounterInitialized; /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { string directory; if (previous != null) @@ -33,11 +33,11 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes var pos = previous.IndexOf(sep, StringComparison.Ordinal); var s = pos > 0 ? previous.Substring(0, pos) : null; - directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileSystem); + directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileManager.FileSystem); } else { - directory = GetNextDirectory(fileSystem); + directory = GetNextDirectory(fileManager.FileSystem); } if (directory == null) @@ -47,7 +47,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) + public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs index 2fffd4e7d6..203c8aaed8 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs @@ -12,13 +12,13 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class TwoGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { return Path.Combine(itemGuid.ToString("N"), propertyGuid.ToString("N"), filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) + public string GetDeleteDirectory(MediaFileManager fileManager, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs index faf94fb6e6..55a9cd4574 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private const int DirectoryLength = 8; /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid); var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength); @@ -32,6 +32,6 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes /// race conditions. We'd need to implement locks in for /// this. /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => null; + public string GetDeleteDirectory(MediaFileManager fileManager, string filepath) => null; } } diff --git a/src/Umbraco.Core/Logging/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs index 50bfd537cd..58e951ab3b 100644 --- a/src/Umbraco.Core/Logging/DisposableTimer.cs +++ b/src/Umbraco.Core/Logging/DisposableTimer.cs @@ -16,13 +16,24 @@ namespace Umbraco.Cms.Core.Logging private readonly IDisposable _profilerStep; private readonly string _endMessage; private string _failMessage; + private readonly object[] _endMessageArgs; + private readonly object[] _failMessageArgs; private Exception _failException; private bool _failed; private readonly string _timingId; // internal - created by profiling logger - internal DisposableTimer(ILogger logger, LogLevel level, IProfiler profiler, Type loggerType, - string startMessage, string endMessage, string failMessage = null, + internal DisposableTimer( + ILogger logger, + LogLevel level, + IProfiler profiler, + Type loggerType, + string startMessage, + string endMessage, + string failMessage = null, + object[] startMessageArgs = null, + object[] endMessageArgs = null, + object[] failMessageArgs = null, int thresholdMilliseconds = 0) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -30,6 +41,8 @@ namespace Umbraco.Cms.Core.Logging _loggerType = loggerType ?? throw new ArgumentNullException(nameof(loggerType)); _endMessage = endMessage; _failMessage = failMessage; + _endMessageArgs = endMessageArgs; + _failMessageArgs = failMessageArgs; _thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds; _timingId = Guid.NewGuid().ToString("N").Substring(0, 7); // keep it short-ish @@ -38,10 +51,30 @@ namespace Umbraco.Cms.Core.Logging switch (_level) { case LogLevel.Debug: - logger.LogDebug("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + if (startMessageArgs == null) + { + logger.LogDebug("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + } + else + { + var args = new object[startMessageArgs.Length + 1]; + startMessageArgs.CopyTo(args, 0); + args[startMessageArgs.Length] = _timingId; + logger.LogDebug(startMessage + " [Timing {TimingId}]", args); + } break; case LogLevel.Information: - logger.LogInformation("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + if (startMessageArgs == null) + { + logger.LogInformation("{StartMessage} [Timing {TimingId}]", startMessage, _timingId); + } + else + { + var args = new object[startMessageArgs.Length + 1]; + startMessageArgs.CopyTo(args, 0); + args[startMessageArgs.Length] = _timingId; + logger.LogDebug(startMessage + " [Timing {TimingId}]", args); + } break; default: throw new ArgumentOutOfRangeException(nameof(level)); @@ -85,19 +118,55 @@ namespace Umbraco.Cms.Core.Logging { if (_failed) { - _logger.LogError(_failException, "{FailMessage} ({Duration}ms) [Timing {TimingId}]", _failMessage, Stopwatch.ElapsedMilliseconds, _timingId); + if (_failMessageArgs is null) + { + _logger.LogError(_failException, "{FailMessage} ({Duration}ms) [Timing {TimingId}]", _failMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } + else + { + var args = new object[_failMessageArgs.Length + 2]; + _failMessageArgs.CopyTo(args, 0); + args[_failMessageArgs.Length - 1] = Stopwatch.ElapsedMilliseconds; + args[_failMessageArgs.Length] = _timingId; + _logger.LogError(_failException, _failMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } } - else switch (_level) + else { - case LogLevel.Debug: - _logger.LogDebug("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); - break; - case LogLevel.Information: - _logger.LogInformation("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); - break; - // filtered in the ctor - //default: - // throw new Exception(); + switch (_level) + { + case LogLevel.Debug: + if (_endMessageArgs == null) + { + _logger.LogDebug("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } + else + { + var args = new object[_endMessageArgs.Length + 2]; + _endMessageArgs.CopyTo(args, 0); + args[args.Length - 1] = Stopwatch.ElapsedMilliseconds; + args[args.Length] = _timingId; + _logger.LogDebug(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } + break; + case LogLevel.Information: + if (_endMessageArgs == null) + { + _logger.LogInformation("{EndMessage} ({Duration}ms) [Timing {TimingId}]", _endMessage, Stopwatch.ElapsedMilliseconds, _timingId); + } + else + { + var args = new object[_endMessageArgs.Length + 2]; + _endMessageArgs.CopyTo(args, 0); + args[_endMessageArgs.Length - 1] = Stopwatch.ElapsedMilliseconds; + args[_endMessageArgs.Length] = _timingId; + _logger.LogInformation(_endMessage + " ({Duration}ms) [Timing {TimingId}]", args); + } + break; + // filtered in the ctor + //default: + // throw new Exception(); + } } } } diff --git a/src/Umbraco.Core/Logging/IProfilingLogger.cs b/src/Umbraco.Core/Logging/IProfilingLogger.cs index fd51bd2da3..4a0714fd0c 100644 --- a/src/Umbraco.Core/Logging/IProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/IProfilingLogger.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Umbraco.Cms.Core.Logging { @@ -10,31 +10,31 @@ namespace Umbraco.Cms.Core.Logging /// /// Profiles an action and log as information messages. /// - DisposableTimer TraceDuration(string startMessage); + DisposableTimer TraceDuration(string startMessage, object[] startMessageArgs = null); /// /// Profiles an action and log as information messages. /// - DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null); + DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); /// /// Profiles an action and log as information messages. /// - DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null); + DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); /// /// Profiles an action and log as debug messages. /// - DisposableTimer DebugDuration(string startMessage); + DisposableTimer DebugDuration(string startMessage, object[] startMessageArgs = null); /// /// Profiles an action and log as debug messages. /// - DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0); + DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); /// /// Profiles an action and log as debug messages. /// - DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0); + DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null); } } diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 6a6bcfd850..9c66c95d24 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Logging; namespace Umbraco.Cms.Core.Logging @@ -27,41 +27,29 @@ namespace Umbraco.Cms.Core.Logging Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); } - public DisposableTimer TraceDuration(string startMessage) - { - return TraceDuration(startMessage, "Completed."); - } + public DisposableTimer TraceDuration(string startMessage, object[] startMessageArgs = null) + => TraceDuration(startMessage, "Completed.", startMessageArgs: startMessageArgs); - public DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null) - { - return new DisposableTimer(Logger, LogLevel.Information, Profiler, typeof(T), startMessage, completeMessage, failMessage); - } + public DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => new DisposableTimer(Logger, LogLevel.Information, Profiler, typeof(T), startMessage, completeMessage, failMessage); - public DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null) - { - return new DisposableTimer(Logger, LogLevel.Information, Profiler, loggerType, startMessage, completeMessage, failMessage); - } + public DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => new DisposableTimer(Logger, LogLevel.Information, Profiler, loggerType, startMessage, completeMessage, failMessage); - public DisposableTimer DebugDuration(string startMessage) - { - return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) - ? DebugDuration(startMessage, "Completed.") + public DisposableTimer DebugDuration(string startMessage, object[] startMessageArgs = null) + => Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) + ? DebugDuration(startMessage, "Completed.", startMessageArgs: startMessageArgs) : null; - } - public DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0) - { - return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) - ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, typeof(T), startMessage, completeMessage, failMessage, thresholdMilliseconds) + public DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) + ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, typeof(T), startMessage, completeMessage, failMessage, startMessageArgs, endMessageArgs, failMessageArgs, thresholdMilliseconds) : null; - } - public DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0) - { - return Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) - ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, thresholdMilliseconds) + public DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0, object[] startMessageArgs = null, object[] endMessageArgs = null, object[] failMessageArgs = null) + => Logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug) + ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, startMessageArgs, endMessageArgs, failMessageArgs, thresholdMilliseconds) : null; - } #region ILogger diff --git a/src/Umbraco.Core/Mail/IEmailSender.cs b/src/Umbraco.Core/Mail/IEmailSender.cs index 0b4b135d03..5b5adf71eb 100644 --- a/src/Umbraco.Core/Mail/IEmailSender.cs +++ b/src/Umbraco.Core/Mail/IEmailSender.cs @@ -10,6 +10,8 @@ namespace Umbraco.Cms.Core.Mail { Task SendAsync(EmailMessage message); + Task SendAsync(EmailMessage message, bool enableNotification); + bool CanSendRequiredEmail(); } } diff --git a/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs b/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs index b7c7d43fb6..3632e79c23 100644 --- a/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs +++ b/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs @@ -9,6 +9,10 @@ namespace Umbraco.Cms.Core.Mail public Task SendAsync(EmailMessage message) => throw new NotImplementedException("To send an Email ensure IEmailSender is implemented with a custom implementation"); + public Task SendAsync(EmailMessage message, bool enableNotification) => + throw new NotImplementedException( + "To send an Email ensure IEmailSender is implemented with a custom implementation"); + public bool CanSendRequiredEmail() => throw new NotImplementedException("To send an Email ensure IEmailSender is implemented with a custom implementation"); } diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index dc5529d25f..32c8ddd98d 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -13,18 +13,18 @@ namespace Umbraco.Cms.Core.Media /// public class UploadAutoFillProperties { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ILogger _logger; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IImageDimensionExtractor _imageDimensionExtractor; public UploadAutoFillProperties( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, ILogger logger, IImageUrlGenerator imageUrlGenerator, IImageDimensionExtractor imageDimensionExtractor) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _imageUrlGenerator = imageUrlGenerator ?? throw new ArgumentNullException(nameof(imageUrlGenerator)); _imageDimensionExtractor = imageDimensionExtractor ?? throw new ArgumentNullException(nameof(imageDimensionExtractor)); @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Core.Media // if anything goes wrong, just reset the properties try { - using (var filestream = _mediaFileSystem.OpenFile(filepath)) + using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath)) { var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period); var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null; diff --git a/src/Umbraco.Core/Models/DataTypeExtensions.cs b/src/Umbraco.Core/Models/DataTypeExtensions.cs index a7bef7b9a9..4601330cac 100644 --- a/src/Umbraco.Core/Models/DataTypeExtensions.cs +++ b/src/Umbraco.Core/Models/DataTypeExtensions.cs @@ -61,6 +61,10 @@ namespace Umbraco.Extensions Constants.DataTypes.Guids.TextstringGuid, Constants.DataTypes.Guids.TextareaGuid, Constants.DataTypes.Guids.UploadGuid, + Constants.DataTypes.Guids.UploadArticleGuid, + Constants.DataTypes.Guids.UploadAudioGuid, + Constants.DataTypes.Guids.UploadVectorGraphicsGuid, + Constants.DataTypes.Guids.UploadVideoGuid, Constants.DataTypes.Guids.LabelStringGuid, Constants.DataTypes.Guids.LabelDecimalGuid, Constants.DataTypes.Guids.LabelDateTimeGuid, diff --git a/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs b/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs index 45f1ad1a4f..976627bf23 100644 --- a/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs +++ b/src/Umbraco.Core/Models/Entities/BeingDirtyBase.cs @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Core.Models.Entities /// public virtual bool IsPropertyDirty(string propertyName) { - return _currentChanges != null && _currentChanges.Any(x => x.Key == propertyName); + return _currentChanges != null && _currentChanges.ContainsKey(propertyName); } /// @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Core.Models.Entities /// public virtual bool WasPropertyDirty(string propertyName) { - return _savedChanges != null && _savedChanges.Any(x => x.Key == propertyName); + return _savedChanges != null && _savedChanges.ContainsKey(propertyName); } /// diff --git a/src/Umbraco.Core/Models/IDataValueEditor.cs b/src/Umbraco.Core/Models/IDataValueEditor.cs index aef9fcf94b..012766e75d 100644 --- a/src/Umbraco.Core/Models/IDataValueEditor.cs +++ b/src/Umbraco.Core/Models/IDataValueEditor.cs @@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations; using System.Xml.Linq; using Umbraco.Cms.Core.Models.Editors; using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Core.Models { @@ -69,21 +68,18 @@ namespace Umbraco.Cms.Core.Models /// Used for serializing an item for packaging /// /// - /// - /// /// /// - IEnumerable ConvertDbToXml(IProperty property, IDataTypeService dataTypeService, ILocalizationService localizationService, bool published); + IEnumerable ConvertDbToXml(IProperty property, bool published); /// /// Used for serializing an item for packaging /// /// /// - /// /// - XNode ConvertDbToXml(IPropertyType propertyType, object value, IDataTypeService dataTypeService); + XNode ConvertDbToXml(IPropertyType propertyType, object value); - string ConvertDbToString(IPropertyType propertyType, object value, IDataTypeService dataTypeService); + string ConvertDbToString(IPropertyType propertyType, object value); } } diff --git a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs index 76f9e7f64f..4a19fbe530 100644 --- a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs @@ -28,12 +28,12 @@ namespace Umbraco.Cms.Core.Models.Mapping private readonly ActionCollection _actions; private readonly AppCaches _appCaches; private readonly GlobalSettings _globalSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IShortStringHelper _shortStringHelper; private readonly IImageUrlGenerator _imageUrlGenerator; public UserMapDefinition(ILocalizedTextService textService, IUserService userService, IEntityService entityService, ISectionService sectionService, - AppCaches appCaches, ActionCollection actions, IOptions globalSettings, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, + AppCaches appCaches, ActionCollection actions, IOptions globalSettings, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IImageUrlGenerator imageUrlGenerator) { _sectionService = sectionService; @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Core.Models.Mapping _actions = actions; _appCaches = appCaches; _globalSettings = globalSettings.Value; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _shortStringHelper = shortStringHelper; _imageUrlGenerator = imageUrlGenerator; } @@ -104,6 +104,7 @@ namespace Umbraco.Cms.Core.Models.Mapping // Umbraco.Code.MapAll -SecurityStamp -Avatar -ProviderUserKey -RawPasswordValue // Umbraco.Code.MapAll -RawPasswordAnswerValue -Comments -IsApproved -IsLockedOut -LastLoginDate // Umbraco.Code.MapAll -LastPasswordChangeDate -LastLockoutDate -FailedPasswordAttempts + // Umbraco.Code.MapAll -PasswordConfiguration private void Map(UserInvite source, IUser target, MapperContext context) { target.Email = source.Email; @@ -122,6 +123,7 @@ namespace Umbraco.Cms.Core.Models.Mapping // Umbraco.Code.MapAll -ProviderUserKey -RawPasswordValue -RawPasswordAnswerValue -PasswordQuestion -Comments // Umbraco.Code.MapAll -IsApproved -IsLockedOut -LastLoginDate -LastPasswordChangeDate -LastLockoutDate // Umbraco.Code.MapAll -FailedPasswordAttempts + // Umbraco.Code.MapAll -PasswordConfiguration private void Map(UserSave source, IUser target, MapperContext context) { target.Name = source.Name; @@ -281,7 +283,7 @@ namespace Umbraco.Cms.Core.Models.Mapping private void Map(IUser source, UserDisplay target, MapperContext context) { target.AvailableCultures = _textService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName); - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.CalculatedStartContentIds = GetStartNodes(source.CalculateContentStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Document, "content/contentRoot", context); target.CalculatedStartMediaIds = GetStartNodes(source.CalculateMediaStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Media, "media/mediaRoot", context); target.CreateDate = source.CreateDate; @@ -312,7 +314,7 @@ namespace Umbraco.Cms.Core.Models.Mapping //Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost //Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look //like the load time is waiting. - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); @@ -331,7 +333,7 @@ namespace Umbraco.Cms.Core.Models.Mapping private void Map(IUser source, UserDetail target, MapperContext context) { target.AllowedSections = source.AllowedSections; - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); diff --git a/src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IAutoPublishedModelFactory.cs similarity index 90% rename from src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs rename to src/Umbraco.Core/Models/PublishedContent/IAutoPublishedModelFactory.cs index f16fe0d3f5..2838297a8e 100644 --- a/src/Umbraco.Core/Models/PublishedContent/ILivePublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IAutoPublishedModelFactory.cs @@ -4,7 +4,7 @@ namespace Umbraco.Cms.Core.Models.PublishedContent /// /// Provides a live published model creation service. /// - public interface ILivePublishedModelFactory : IPublishedModelFactory + public interface IAutoPublishedModelFactory : IPublishedModelFactory { /// /// Gets an object that can be used to synchronize access to the factory. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs index ee92ae2d39..249c2cb465 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentModel.cs @@ -12,8 +12,10 @@ /// an original instance. /// /// The original content. - protected PublishedContentModel(IPublishedContent content) - : base(content) + protected PublishedContentModel(IPublishedContent content, IPublishedValueFallback publishedValueFallback) + : base(content, publishedValueFallback) { } + + } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index 763c9e438f..22a537da0f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -21,15 +21,18 @@ namespace Umbraco.Cms.Core.Models.PublishedContent public abstract class PublishedContentWrapped : IPublishedContent { private readonly IPublishedContent _content; + private readonly IPublishedValueFallback _publishedValueFallback; /// /// Initialize a new instance of the class /// with an IPublishedContent instance to wrap. /// /// The content to wrap. - protected PublishedContentWrapped(IPublishedContent content) + /// The published value fallback. + protected PublishedContentWrapped(IPublishedContent content, IPublishedValueFallback publishedValueFallback) { _content = content; + _publishedValueFallback = publishedValueFallback; } /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs index 2de60c5705..f093e7b20c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedElementModel.cs @@ -14,8 +14,9 @@ /// an original instance. /// /// The original content. - protected PublishedElementModel(IPublishedElement content) - : base(content) + /// The published value fallback. + protected PublishedElementModel(IPublishedElement content, IPublishedValueFallback publishedValueFallback) + : base(content, publishedValueFallback) { } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs index 3feb4835e7..5f9564d176 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedElementWrapped.cs @@ -10,15 +10,18 @@ namespace Umbraco.Cms.Core.Models.PublishedContent public abstract class PublishedElementWrapped : IPublishedElement { private readonly IPublishedElement _content; + private readonly IPublishedValueFallback _publishedValueFallback; /// /// Initializes a new instance of the class /// with an IPublishedElement instance to wrap. /// /// The content to wrap. - protected PublishedElementWrapped(IPublishedElement content) + /// The published value fallback. + protected PublishedElementWrapped(IPublishedElement content, IPublishedValueFallback publishedValueFallback) { _content = content; + _publishedValueFallback = publishedValueFallback; } /// diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 6fb74c3b86..dd96faf298 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -21,11 +21,11 @@ namespace Umbraco.Cms.Core.Models /// /// /// - /// + /// /// /// A list of 5 different sized avatar URLs /// - public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, IMediaFileSystem mediaFileSystem, IImageUrlGenerator imageUrlGenerator) + public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) { // If FIPS is required, never check the Gravatar service as it only supports MD5 hashing. // Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Core.Models } //use the custom avatar - var avatarUrl = mediaFileSystem.GetUrl(user.Avatar); + var avatarUrl = mediaFileManager.FileSystem.GetUrl(user.Avatar); return new[] { imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 30, Height = 30 }), diff --git a/src/Umbraco.Core/NetworkHelper.cs b/src/Umbraco.Core/NetworkHelper.cs deleted file mode 100644 index 8e1bfaea92..0000000000 --- a/src/Umbraco.Core/NetworkHelper.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core -{ - /// - /// Currently just used to get the machine name in med trust and to format a machine name for use with file names - /// - public class NetworkHelper - { - /// - /// Returns the machine name that is safe to use in file paths. - /// - public static string FileSafeMachineName - { - get { return MachineName.ReplaceNonAlphanumericChars('-'); } - } - - /// - /// Returns the current machine name - /// - /// - /// Tries to resolve the machine name, if it cannot it uses the config section. - /// - public static string MachineName - { - get - { - try - { - return Environment.MachineName; - } - catch - { - try - { - return System.Net.Dns.GetHostName(); - } - catch - { - //if we get here it means we cannot access the machine name - throw new ApplicationException("Cannot resolve the current machine name either by Environment.MachineName or by Dns.GetHostname()"); - } - } - } - } - } -} diff --git a/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/ApplicationCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/ApplicationCacheRefresherNotification.cs index 3602a1488f..eb596a3a0b 100644 --- a/src/Umbraco.Core/Cache/ApplicationCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/ApplicationCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class ApplicationCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/AssignedMemberRolesNotification.cs b/src/Umbraco.Core/Notifications/AssignedMemberRolesNotification.cs similarity index 81% rename from src/Umbraco.Core/Services/Notifications/AssignedMemberRolesNotification.cs rename to src/Umbraco.Core/Notifications/AssignedMemberRolesNotification.cs index edd1738a7d..adcf14d636 100644 --- a/src/Umbraco.Core/Services/Notifications/AssignedMemberRolesNotification.cs +++ b/src/Umbraco.Core/Notifications/AssignedMemberRolesNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class AssignedMemberRolesNotification : MemberRolesNotification { diff --git a/src/Umbraco.Core/Services/Notifications/AssignedUserGroupPermissionsNotification.cs b/src/Umbraco.Core/Notifications/AssignedUserGroupPermissionsNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/AssignedUserGroupPermissionsNotification.cs rename to src/Umbraco.Core/Notifications/AssignedUserGroupPermissionsNotification.cs index d11cd01e07..18425f2393 100644 --- a/src/Umbraco.Core/Services/Notifications/AssignedUserGroupPermissionsNotification.cs +++ b/src/Umbraco.Core/Notifications/AssignedUserGroupPermissionsNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class AssignedUserGroupPermissionsNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Cache/CacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/CacheRefresherNotification.cs similarity index 86% rename from src/Umbraco.Core/Cache/CacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/CacheRefresherNotification.cs index 442fad0147..bd110ad878 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/CacheRefresherNotification.cs @@ -1,9 +1,7 @@ using System; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Sync; -using Umbraco.Extensions; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { /// /// Base class for cache refresher notifications diff --git a/src/Umbraco.Core/Events/CancelableEnumerableObjectNotification.cs b/src/Umbraco.Core/Notifications/CancelableEnumerableObjectNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/CancelableEnumerableObjectNotification.cs rename to src/Umbraco.Core/Notifications/CancelableEnumerableObjectNotification.cs index 1db0380af5..ea7476cd3f 100644 --- a/src/Umbraco.Core/Events/CancelableEnumerableObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/CancelableEnumerableObjectNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class CancelableEnumerableObjectNotification : CancelableObjectNotification> { diff --git a/src/Umbraco.Core/Events/CancelableNotification.cs b/src/Umbraco.Core/Notifications/CancelableNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/CancelableNotification.cs rename to src/Umbraco.Core/Notifications/CancelableNotification.cs index 173304017a..13989d50da 100644 --- a/src/Umbraco.Core/Events/CancelableNotification.cs +++ b/src/Umbraco.Core/Notifications/CancelableNotification.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public class CancelableNotification : StatefulNotification, ICancelableNotification { diff --git a/src/Umbraco.Core/Events/CancelableObjectNotification.cs b/src/Umbraco.Core/Notifications/CancelableObjectNotification.cs similarity index 89% rename from src/Umbraco.Core/Events/CancelableObjectNotification.cs rename to src/Umbraco.Core/Notifications/CancelableObjectNotification.cs index a976ff51d9..25f6a4474f 100644 --- a/src/Umbraco.Core/Events/CancelableObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/CancelableObjectNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CancelableObjectNotification : ObjectNotification, ICancelableNotification where T : class { diff --git a/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/ContentCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/ContentCacheRefresherNotification.cs index dd76786393..35b4f472c7 100644 --- a/src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class ContentCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentCopiedNotification.cs b/src/Umbraco.Core/Notifications/ContentCopiedNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentCopiedNotification.cs rename to src/Umbraco.Core/Notifications/ContentCopiedNotification.cs index 8589e14778..6399fb714d 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentCopiedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentCopiedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentCopiedNotification : CopiedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentCopyingNotification.cs b/src/Umbraco.Core/Notifications/ContentCopyingNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentCopyingNotification.cs rename to src/Umbraco.Core/Notifications/ContentCopyingNotification.cs index 9c3220e8e2..d30d49efeb 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentCopyingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentCopyingNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentCopyingNotification : CopyingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletedBlueprintNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletedBlueprintNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentDeletedBlueprintNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletedBlueprintNotification.cs index e2c49535a7..1c516a295f 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletedBlueprintNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletedBlueprintNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletedBlueprintNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletedNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ContentDeletedNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletedNotification.cs index 77f6e0cc51..6398c4f28e 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletedVersionsNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletedVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentDeletedVersionsNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletedVersionsNotification.cs index 34040135ba..30f00b52bf 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletedVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletedVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletedVersionsNotification : DeletedVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletingNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentDeletingNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletingNotification.cs index befb932861..ee02c6f339 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentDeletingVersionsNotification.cs b/src/Umbraco.Core/Notifications/ContentDeletingVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentDeletingVersionsNotification.cs rename to src/Umbraco.Core/Notifications/ContentDeletingVersionsNotification.cs index 4929046ad5..340aaaa559 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentDeletingVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentDeletingVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentDeletingVersionsNotification : DeletingVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentEmptiedRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentEmptiedRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentEmptiedRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentEmptiedRecycleBinNotification.cs index ad305bd7ab..1453553efa 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentEmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentEmptiedRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentEmptiedRecycleBinNotification : EmptiedRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentEmptyingRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentEmptyingRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentEmptyingRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentEmptyingRecycleBinNotification.cs index 312d375dd4..f66400a9a8 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentEmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentEmptyingRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentEmptyingRecycleBinNotification : EmptyingRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovedNotification.cs b/src/Umbraco.Core/Notifications/ContentMovedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentMovedNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovedNotification.cs index fb44887d93..607d678049 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovedToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentMovedToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentMovedToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovedToRecycleBinNotification.cs index c3f2950fef..3b736b1409 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovedToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovedToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovedToRecycleBinNotification : MovedToRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovingNotification.cs b/src/Umbraco.Core/Notifications/ContentMovingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentMovingNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovingNotification.cs index b066c6aad5..01c04eb226 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentMovingToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/ContentMovingToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentMovingToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/ContentMovingToRecycleBinNotification.cs index 33e2bab8bc..88aa48c7b8 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentMovingToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentMovingToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentMovingToRecycleBinNotification : MovingToRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentNotificationExtensions.cs b/src/Umbraco.Core/Notifications/ContentNotificationExtensions.cs similarity index 97% rename from src/Umbraco.Core/Services/Notifications/ContentNotificationExtensions.cs rename to src/Umbraco.Core/Notifications/ContentNotificationExtensions.cs index 27afb52c30..1b9464dcb4 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentNotificationExtensions.cs +++ b/src/Umbraco.Core/Notifications/ContentNotificationExtensions.cs @@ -1,10 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public static class ContentNotificationExtensions { diff --git a/src/Umbraco.Core/Services/Notifications/ContentPublishedNotification.cs b/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentPublishedNotification.cs rename to src/Umbraco.Core/Notifications/ContentPublishedNotification.cs index f6c25cb800..69d1751e58 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentPublishedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentPublishedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentPublishedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentPublishingNotification.cs b/src/Umbraco.Core/Notifications/ContentPublishingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentPublishingNotification.cs rename to src/Umbraco.Core/Notifications/ContentPublishingNotification.cs index d0937019d9..65a8efdadf 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentPublishingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentPublishingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentPublishingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/ContentRefreshNotification.cs b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentRefreshNotification.cs rename to src/Umbraco.Core/Notifications/ContentRefreshNotification.cs index 0132f4de8c..6957da7f70 100644 --- a/src/Umbraco.Core/Events/ContentRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use saved notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/ContentRolledBackNotification.cs b/src/Umbraco.Core/Notifications/ContentRolledBackNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ContentRolledBackNotification.cs rename to src/Umbraco.Core/Notifications/ContentRolledBackNotification.cs index 0bb7a10cf1..a1f370bd94 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentRolledBackNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRolledBackNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentRolledBackNotification : RolledBackNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentRollingBackNotification.cs b/src/Umbraco.Core/Notifications/ContentRollingBackNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ContentRollingBackNotification.cs rename to src/Umbraco.Core/Notifications/ContentRollingBackNotification.cs index 9163e4b61c..e12bfa1631 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentRollingBackNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRollingBackNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentRollingBackNotification : RollingBackNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSavedBlueprintNotification.cs b/src/Umbraco.Core/Notifications/ContentSavedBlueprintNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentSavedBlueprintNotification.cs rename to src/Umbraco.Core/Notifications/ContentSavedBlueprintNotification.cs index bbf85fca17..6addde88c1 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSavedBlueprintNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSavedBlueprintNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSavedBlueprintNotification : ObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSavedNotification.cs b/src/Umbraco.Core/Notifications/ContentSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentSavedNotification.cs rename to src/Umbraco.Core/Notifications/ContentSavedNotification.cs index c0ad60aa33..b58a366368 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSavingNotification.cs b/src/Umbraco.Core/Notifications/ContentSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ContentSavingNotification.cs rename to src/Umbraco.Core/Notifications/ContentSavingNotification.cs index 61a9b14861..afe21bf870 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSendingToPublishNotification.cs b/src/Umbraco.Core/Notifications/ContentSendingToPublishNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/ContentSendingToPublishNotification.cs rename to src/Umbraco.Core/Notifications/ContentSendingToPublishNotification.cs index 6aa367dd70..0a5c018883 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSendingToPublishNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSendingToPublishNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSendingToPublishNotification : CancelableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSentToPublishNotification.cs b/src/Umbraco.Core/Notifications/ContentSentToPublishNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/ContentSentToPublishNotification.cs rename to src/Umbraco.Core/Notifications/ContentSentToPublishNotification.cs index 47088e01b9..c5e2e5dc3b 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSentToPublishNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSentToPublishNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSentToPublishNotification : ObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSortedNotification.cs b/src/Umbraco.Core/Notifications/ContentSortedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/ContentSortedNotification.cs rename to src/Umbraco.Core/Notifications/ContentSortedNotification.cs index 1646b5cf55..0a299e3c0a 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSortedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSortedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSortedNotification : SortedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentSortingNotification.cs b/src/Umbraco.Core/Notifications/ContentSortingNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/ContentSortingNotification.cs rename to src/Umbraco.Core/Notifications/ContentSortingNotification.cs index 375bbceead..1d6cd31c5a 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentSortingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentSortingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentSortingNotification : SortingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentTreeChangeNotification.cs b/src/Umbraco.Core/Notifications/ContentTreeChangeNotification.cs similarity index 95% rename from src/Umbraco.Core/Services/Notifications/ContentTreeChangeNotification.cs rename to src/Umbraco.Core/Notifications/ContentTreeChangeNotification.cs index 2161b4782a..b5b100038b 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentTreeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTreeChangeNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ContentTreeChangeNotification : TreeChangeNotification { diff --git a/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeCacheRefresherNotification.cs index 17bd955d03..8bd06a4c46 100644 --- a/src/Umbraco.Core/Cache/ContentTypeCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeChangeNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeChangeNotification.cs similarity index 90% rename from src/Umbraco.Core/Events/ContentTypeChangeNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeChangeNotification.cs index 80a0f333a3..e03f381318 100644 --- a/src/Umbraco.Core/Events/ContentTypeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeChangeNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class ContentTypeChangeNotification : EnumerableObjectNotification> where T : class, IContentTypeComposition { diff --git a/src/Umbraco.Core/Events/ContentTypeChangedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeChangedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/ContentTypeChangedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeChangedNotification.cs index 37e79c576d..e0aca73cd2 100644 --- a/src/Umbraco.Core/Events/ContentTypeChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeChangedNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeChangedNotification : ContentTypeChangeNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeDeletedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeDeletedNotification.cs index c8a704ceb7..d5b2b3e28e 100644 --- a/src/Umbraco.Core/Events/ContentTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeDeletedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeDeletingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeDeletingNotification.cs index 031dd9805f..56863b93fb 100644 --- a/src/Umbraco.Core/Events/ContentTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeDeletingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeMovedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeMovedNotification.cs index c7d9334098..d4794329cf 100644 --- a/src/Umbraco.Core/Events/ContentTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeMovedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeMovingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeMovingNotification.cs index ec1ecd530c..a888150097 100644 --- a/src/Umbraco.Core/Events/ContentTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeMovingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeRefreshNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeRefreshNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/ContentTypeRefreshNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeRefreshNotification.cs index 47a74c4a45..717225db2d 100644 --- a/src/Umbraco.Core/Events/ContentTypeRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeRefreshNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class ContentTypeRefreshNotification : ContentTypeChangeNotification where T: class, IContentTypeComposition { diff --git a/src/Umbraco.Core/Events/ContentTypeRefreshedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeRefreshedNotification.cs similarity index 91% rename from src/Umbraco.Core/Events/ContentTypeRefreshedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeRefreshedNotification.cs index bd5c3ee95f..72d111bb67 100644 --- a/src/Umbraco.Core/Events/ContentTypeRefreshedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeRefreshedNotification.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use saved notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Events/ContentTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeSavedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeSavedNotification.cs index c92666a7ae..5b9a231d60 100644 --- a/src/Umbraco.Core/Events/ContentTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/ContentTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/ContentTypeSavingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/ContentTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/ContentTypeSavingNotification.cs index 28ba53d3c0..85deb91418 100644 --- a/src/Umbraco.Core/Events/ContentTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ContentTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentUnpublishedNotification.cs b/src/Umbraco.Core/Notifications/ContentUnpublishedNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentUnpublishedNotification.cs rename to src/Umbraco.Core/Notifications/ContentUnpublishedNotification.cs index 03bedec921..c08d79ac59 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentUnpublishedNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentUnpublishedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentUnpublishedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ContentUnpublishingNotification.cs b/src/Umbraco.Core/Notifications/ContentUnpublishingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/ContentUnpublishingNotification.cs rename to src/Umbraco.Core/Notifications/ContentUnpublishingNotification.cs index bb7b499ab7..5fb5034515 100644 --- a/src/Umbraco.Core/Services/Notifications/ContentUnpublishingNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentUnpublishingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class ContentUnpublishingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/CopiedNotification.cs b/src/Umbraco.Core/Notifications/CopiedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/CopiedNotification.cs rename to src/Umbraco.Core/Notifications/CopiedNotification.cs index 807ffa1282..c7d6c88bcd 100644 --- a/src/Umbraco.Core/Events/CopiedNotification.cs +++ b/src/Umbraco.Core/Notifications/CopiedNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CopiedNotification : ObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/CopyingNotification.cs b/src/Umbraco.Core/Notifications/CopyingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/CopyingNotification.cs rename to src/Umbraco.Core/Notifications/CopyingNotification.cs index c89146b494..99f46f8b43 100644 --- a/src/Umbraco.Core/Events/CopyingNotification.cs +++ b/src/Umbraco.Core/Notifications/CopyingNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CopyingNotification : CancelableObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/CreatedNotification.cs b/src/Umbraco.Core/Notifications/CreatedNotification.cs similarity index 81% rename from src/Umbraco.Core/Events/CreatedNotification.cs rename to src/Umbraco.Core/Notifications/CreatedNotification.cs index 3334fb8227..2108b5fb5c 100644 --- a/src/Umbraco.Core/Events/CreatedNotification.cs +++ b/src/Umbraco.Core/Notifications/CreatedNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CreatedNotification : ObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/CreatingNotification.cs b/src/Umbraco.Core/Notifications/CreatingNotification.cs similarity index 82% rename from src/Umbraco.Core/Events/CreatingNotification.cs rename to src/Umbraco.Core/Notifications/CreatingNotification.cs index 2cace11cd5..da4fbfe742 100644 --- a/src/Umbraco.Core/Events/CreatingNotification.cs +++ b/src/Umbraco.Core/Notifications/CreatingNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class CreatingNotification : CancelableObjectNotification where T : class { diff --git a/src/Umbraco.Core/Routing/CreatingRequestNotification.cs b/src/Umbraco.Core/Notifications/CreatingRequestNotification.cs similarity index 89% rename from src/Umbraco.Core/Routing/CreatingRequestNotification.cs rename to src/Umbraco.Core/Notifications/CreatingRequestNotification.cs index 7b7122b332..aacca17afb 100644 --- a/src/Umbraco.Core/Routing/CreatingRequestNotification.cs +++ b/src/Umbraco.Core/Notifications/CreatingRequestNotification.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Routing +namespace Umbraco.Cms.Core.Notifications { /// /// Used for notifying when an Umbraco request is being created diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/DataTypeCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeCacheRefresherNotification.cs index d64dd53431..f59de3ebd0 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class DataTypeCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Events/DataTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/DataTypeDeletedNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeDeletedNotification.cs index b3461e27f1..405af74c1c 100644 --- a/src/Umbraco.Core/Events/DataTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeDeletedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/DataTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/DataTypeDeletingNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeDeletingNotification.cs index 16b2ee68ac..ab997a0def 100644 --- a/src/Umbraco.Core/Events/DataTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeDeletingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/DataTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/DataTypeMovedNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeMovedNotification.cs index 10e42c7a82..150582547b 100644 --- a/src/Umbraco.Core/Events/DataTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeMovedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/DataTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/DataTypeMovingNotification.cs similarity index 78% rename from src/Umbraco.Core/Events/DataTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeMovingNotification.cs index 4b48f51a00..ae8fb4be6e 100644 --- a/src/Umbraco.Core/Events/DataTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeMovingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/DataTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/DataTypeSavedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/DataTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeSavedNotification.cs index 09c42d32a9..6c1a806069 100644 --- a/src/Umbraco.Core/Events/DataTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/DataTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/DataTypeSavingNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/DataTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/DataTypeSavingNotification.cs index dd110df5a3..3538950b12 100644 --- a/src/Umbraco.Core/Events/DataTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/DataTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class DataTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/DeletedNotification.cs b/src/Umbraco.Core/Notifications/DeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/DeletedNotification.cs rename to src/Umbraco.Core/Notifications/DeletedNotification.cs index 3036b53746..3b2a370388 100644 --- a/src/Umbraco.Core/Events/DeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotification.cs b/src/Umbraco.Core/Notifications/DeletedVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DeletedVersionsNotification.cs rename to src/Umbraco.Core/Notifications/DeletedVersionsNotification.cs index 836a9832a5..420323afaf 100644 --- a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletedVersionsNotification.cs @@ -4,7 +4,7 @@ using System; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletedVersionsNotification : DeletedVersionsNotificationBase where T : class { diff --git a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotificationBase.cs b/src/Umbraco.Core/Notifications/DeletedVersionsNotificationBase.cs similarity index 94% rename from src/Umbraco.Core/Services/Notifications/DeletedVersionsNotificationBase.cs rename to src/Umbraco.Core/Notifications/DeletedVersionsNotificationBase.cs index 2a3c78ee21..352eee8cae 100644 --- a/src/Umbraco.Core/Services/Notifications/DeletedVersionsNotificationBase.cs +++ b/src/Umbraco.Core/Notifications/DeletedVersionsNotificationBase.cs @@ -4,7 +4,7 @@ using System; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletedVersionsNotificationBase : StatefulNotification where T : class { diff --git a/src/Umbraco.Core/Events/DeletingNotification.cs b/src/Umbraco.Core/Notifications/DeletingNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/DeletingNotification.cs rename to src/Umbraco.Core/Notifications/DeletingNotification.cs index d88345b427..b4090a5b83 100644 --- a/src/Umbraco.Core/Events/DeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletingNotification.cs @@ -1,8 +1,10 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +using System.Collections.Generic; +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DeletingVersionsNotification.cs b/src/Umbraco.Core/Notifications/DeletingVersionsNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/DeletingVersionsNotification.cs rename to src/Umbraco.Core/Notifications/DeletingVersionsNotification.cs index cc8b06d839..ca8f1f2514 100644 --- a/src/Umbraco.Core/Services/Notifications/DeletingVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/DeletingVersionsNotification.cs @@ -4,7 +4,7 @@ using System; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class DeletingVersionsNotification : DeletedVersionsNotificationBase, ICancelableNotification where T : class { diff --git a/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/DictionaryCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryCacheRefresherNotification.cs index 57474466d0..b36939800e 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class DictionaryCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletedNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemDeletedNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs index 7c2b702888..c151e7ec60 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletingNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemDeletingNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs index b468e4ca1d..5be95c478b 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavedNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemSavedNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs index 596bb20a65..dc5194b847 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavingNotification.cs b/src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DictionaryItemSavingNotification.cs rename to src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs index 71c5cf8cac..79fef15aef 100644 --- a/src/Umbraco.Core/Services/Notifications/DictionaryItemSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DictionaryItemSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/DomainCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/DomainCacheRefresherNotification.cs index 53495ec6cc..326a7d2b64 100644 --- a/src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class DomainCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainDeletedNotification.cs b/src/Umbraco.Core/Notifications/DomainDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/DomainDeletedNotification.cs rename to src/Umbraco.Core/Notifications/DomainDeletedNotification.cs index 150850e3d2..b1b3a80ba1 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainDeletingNotification.cs b/src/Umbraco.Core/Notifications/DomainDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DomainDeletingNotification.cs rename to src/Umbraco.Core/Notifications/DomainDeletingNotification.cs index 822d45cd5b..cd678d3689 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainSavedNotification.cs b/src/Umbraco.Core/Notifications/DomainSavedNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/DomainSavedNotification.cs rename to src/Umbraco.Core/Notifications/DomainSavedNotification.cs index 4854c41f23..61bd8ba3a4 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/DomainSavingNotification.cs b/src/Umbraco.Core/Notifications/DomainSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/DomainSavingNotification.cs rename to src/Umbraco.Core/Notifications/DomainSavingNotification.cs index 3cc8567d3d..32a2d71a73 100644 --- a/src/Umbraco.Core/Services/Notifications/DomainSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/DomainSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class DomainSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/EmptiedRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/EmptiedRecycleBinNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/EmptiedRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/EmptiedRecycleBinNotification.cs index d42619b2c3..2773f3140f 100644 --- a/src/Umbraco.Core/Services/Notifications/EmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/EmptiedRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class EmptiedRecycleBinNotification : StatefulNotification where T : class { diff --git a/src/Umbraco.Core/Services/Notifications/EmptyingRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/EmptyingRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/EmptyingRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/EmptyingRecycleBinNotification.cs index 620be5ab15..3d1732acfa 100644 --- a/src/Umbraco.Core/Services/Notifications/EmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/EmptyingRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class EmptyingRecycleBinNotification : StatefulNotification, ICancelableNotification where T : class { diff --git a/src/Umbraco.Core/Events/EntityContainerDeletedNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerDeletedNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerDeletedNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerDeletedNotification.cs index eca96962be..66c55e94ad 100644 --- a/src/Umbraco.Core/Events/EntityContainerDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerDeletedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerDeletingNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerDeletingNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerDeletingNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerDeletingNotification.cs index 58d0830cf9..45a7a5b6c8 100644 --- a/src/Umbraco.Core/Events/EntityContainerDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerDeletingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerRenamedNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerRenamedNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerRenamedNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerRenamedNotification.cs index dc1b858bd9..e6046c9a58 100644 --- a/src/Umbraco.Core/Events/EntityContainerRenamedNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerRenamedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerRenamedNotification : RenamedNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerRenamingNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerRenamingNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerRenamingNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerRenamingNotification.cs index f2408a1faf..c03d5f5ee3 100644 --- a/src/Umbraco.Core/Events/EntityContainerRenamingNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerRenamingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerRenamingNotification : RenamingNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerSavedNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerSavedNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerSavedNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerSavedNotification.cs index 108d6649df..33cac9effd 100644 --- a/src/Umbraco.Core/Events/EntityContainerSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerSavedNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/EntityContainerSavingNotification.cs b/src/Umbraco.Core/Notifications/EntityContainerSavingNotification.cs similarity index 79% rename from src/Umbraco.Core/Events/EntityContainerSavingNotification.cs rename to src/Umbraco.Core/Notifications/EntityContainerSavingNotification.cs index 98a8a34926..25cbfc9311 100644 --- a/src/Umbraco.Core/Events/EntityContainerSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityContainerSavingNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityContainerSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/EntityRefreshNotification.cs b/src/Umbraco.Core/Notifications/EntityRefreshNotification.cs similarity index 80% rename from src/Umbraco.Core/Events/EntityRefreshNotification.cs rename to src/Umbraco.Core/Notifications/EntityRefreshNotification.cs index b69900d92b..1afc1fa078 100644 --- a/src/Umbraco.Core/Events/EntityRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/EntityRefreshNotification.cs @@ -1,6 +1,7 @@ +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class EntityRefreshNotification : ObjectNotification where T : class, IContentBase { diff --git a/src/Umbraco.Core/Events/EnumerableObjectNotification.cs b/src/Umbraco.Core/Notifications/EnumerableObjectNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/EnumerableObjectNotification.cs rename to src/Umbraco.Core/Notifications/EnumerableObjectNotification.cs index a80b8bf7ee..fde93f0139 100644 --- a/src/Umbraco.Core/Events/EnumerableObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/EnumerableObjectNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class EnumerableObjectNotification : ObjectNotification> { diff --git a/src/Umbraco.Core/Services/Notifications/ExportedMemberNotification.cs b/src/Umbraco.Core/Notifications/ExportedMemberNotification.cs similarity index 83% rename from src/Umbraco.Core/Services/Notifications/ExportedMemberNotification.cs rename to src/Umbraco.Core/Notifications/ExportedMemberNotification.cs index 0218710010..9cf69032e6 100644 --- a/src/Umbraco.Core/Services/Notifications/ExportedMemberNotification.cs +++ b/src/Umbraco.Core/Notifications/ExportedMemberNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ExportedMemberNotification : INotification { diff --git a/src/Umbraco.Core/Events/ICancelableNotification.cs b/src/Umbraco.Core/Notifications/ICancelableNotification.cs similarity index 80% rename from src/Umbraco.Core/Events/ICancelableNotification.cs rename to src/Umbraco.Core/Notifications/ICancelableNotification.cs index df1abc672e..c30e6613fe 100644 --- a/src/Umbraco.Core/Events/ICancelableNotification.cs +++ b/src/Umbraco.Core/Notifications/ICancelableNotification.cs @@ -1,7 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public interface ICancelableNotification : INotification { diff --git a/src/Umbraco.Core/Events/INotification.cs b/src/Umbraco.Core/Notifications/INotification.cs similarity index 83% rename from src/Umbraco.Core/Events/INotification.cs rename to src/Umbraco.Core/Notifications/INotification.cs index 734e9343cf..2427da1454 100644 --- a/src/Umbraco.Core/Events/INotification.cs +++ b/src/Umbraco.Core/Notifications/INotification.cs @@ -1,7 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// A marker interface to represent a notification. diff --git a/src/Umbraco.Core/Events/IStatefulNotification.cs b/src/Umbraco.Core/Notifications/IStatefulNotification.cs similarity index 80% rename from src/Umbraco.Core/Events/IStatefulNotification.cs rename to src/Umbraco.Core/Notifications/IStatefulNotification.cs index dafebe6173..7fa2382038 100644 --- a/src/Umbraco.Core/Events/IStatefulNotification.cs +++ b/src/Umbraco.Core/Notifications/IStatefulNotification.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public interface IStatefulNotification : INotification { diff --git a/src/Umbraco.Core/Events/ImportedPackageNotification.cs b/src/Umbraco.Core/Notifications/ImportedPackageNotification.cs similarity index 92% rename from src/Umbraco.Core/Events/ImportedPackageNotification.cs rename to src/Umbraco.Core/Notifications/ImportedPackageNotification.cs index f4f0bac1b4..ada7a0aef9 100644 --- a/src/Umbraco.Core/Events/ImportedPackageNotification.cs +++ b/src/Umbraco.Core/Notifications/ImportedPackageNotification.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Models.Packaging; using Umbraco.Cms.Core.Packaging; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ImportedPackageNotification : StatefulNotification { diff --git a/src/Umbraco.Core/Events/ImportingPackageNotification.cs b/src/Umbraco.Core/Notifications/ImportingPackageNotification.cs similarity index 92% rename from src/Umbraco.Core/Events/ImportingPackageNotification.cs rename to src/Umbraco.Core/Notifications/ImportingPackageNotification.cs index 143fad3ff4..d9a8763e3a 100644 --- a/src/Umbraco.Core/Events/ImportingPackageNotification.cs +++ b/src/Umbraco.Core/Notifications/ImportingPackageNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Models.Packaging; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class ImportingPackageNotification : StatefulNotification, ICancelableNotification { diff --git a/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/LanguageCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/LanguageCacheRefresherNotification.cs index fde0090c28..436d8a4fbf 100644 --- a/src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class LanguageCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageDeletedNotification.cs b/src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/LanguageDeletedNotification.cs rename to src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs index a8242f4ced..ccc17c8a90 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageDeletingNotification.cs b/src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/LanguageDeletingNotification.cs rename to src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs index 52897714c3..c4e4682500 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageSavedNotification.cs b/src/Umbraco.Core/Notifications/LanguageSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/LanguageSavedNotification.cs rename to src/Umbraco.Core/Notifications/LanguageSavedNotification.cs index 39b3008eaa..29265c86ca 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/LanguageSavingNotification.cs b/src/Umbraco.Core/Notifications/LanguageSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/LanguageSavingNotification.cs rename to src/Umbraco.Core/Notifications/LanguageSavingNotification.cs index 49620634df..5fcb892e25 100644 --- a/src/Umbraco.Core/Services/Notifications/LanguageSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/LanguageSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class LanguageSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MacroCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MacroCacheRefresherNotification.cs index f5ca7985c8..5fb5554b1b 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MacroCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroDeletedNotification.cs b/src/Umbraco.Core/Notifications/MacroDeletedNotification.cs similarity index 84% rename from src/Umbraco.Core/Services/Notifications/MacroDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MacroDeletedNotification.cs index e5c56c8bcf..237cce38fe 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroDeletedNotification.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroDeletingNotification.cs b/src/Umbraco.Core/Notifications/MacroDeletingNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MacroDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MacroDeletingNotification.cs index bd64c33ff6..d36a9896bc 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroDeletingNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroSavedNotification.cs b/src/Umbraco.Core/Notifications/MacroSavedNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MacroSavedNotification.cs rename to src/Umbraco.Core/Notifications/MacroSavedNotification.cs index d7b85246be..8aa776dcc6 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroSavedNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MacroSavingNotification.cs b/src/Umbraco.Core/Notifications/MacroSavingNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MacroSavingNotification.cs rename to src/Umbraco.Core/Notifications/MacroSavingNotification.cs index c310951d23..965ee6b22e 100644 --- a/src/Umbraco.Core/Services/Notifications/MacroSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MacroSavingNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MacroSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MediaCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MediaCacheRefresherNotification.cs index afdd60bb4a..079475232d 100644 --- a/src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MediaCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletedNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/MediaDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletedNotification.cs index 9ad1e75a41..b8cce7e747 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletedVersionsNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletedVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaDeletedVersionsNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletedVersionsNotification.cs index 7efa10f317..6bbdb3c098 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletedVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletedVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletedVersionsNotification : DeletedVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletingNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletingNotification.cs index 027e0f7b50..358a553b28 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaDeletingVersionsNotification.cs b/src/Umbraco.Core/Notifications/MediaDeletingVersionsNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaDeletingVersionsNotification.cs rename to src/Umbraco.Core/Notifications/MediaDeletingVersionsNotification.cs index 361739cee3..fa7b3ba8e0 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaDeletingVersionsNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaDeletingVersionsNotification.cs @@ -5,7 +5,7 @@ using System; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaDeletingVersionsNotification : DeletingVersionsNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaEmptiedRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaEmptiedRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MediaEmptiedRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaEmptiedRecycleBinNotification.cs index 9e0ef67d7d..0862296925 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaEmptiedRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaEmptiedRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaEmptiedRecycleBinNotification : EmptiedRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaEmptyingRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaEmptyingRecycleBinNotification.cs similarity index 89% rename from src/Umbraco.Core/Services/Notifications/MediaEmptyingRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaEmptyingRecycleBinNotification.cs index a7aebd79fe..4e257cfb38 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaEmptyingRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaEmptyingRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaEmptyingRecycleBinNotification : EmptyingRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovedNotification.cs b/src/Umbraco.Core/Notifications/MediaMovedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaMovedNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovedNotification.cs index fed8da869f..2012f16f4b 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovedToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaMovedToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MediaMovedToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovedToRecycleBinNotification.cs index 599ea97ce4..44120674bd 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovedToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovedToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovedToRecycleBinNotification : MovedToRecycleBinNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovingNotification.cs b/src/Umbraco.Core/Notifications/MediaMovingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaMovingNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovingNotification.cs index 30cac1fd87..fcfb50787b 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaMovingToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MediaMovingToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MediaMovingToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MediaMovingToRecycleBinNotification.cs index 9895c4296b..856b66c0c4 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaMovingToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaMovingToRecycleBinNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaMovingToRecycleBinNotification : MovingToRecycleBinNotification { diff --git a/src/Umbraco.Core/Events/MediaRefreshNotification.cs b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaRefreshNotification.cs rename to src/Umbraco.Core/Notifications/MediaRefreshNotification.cs index b05eaeebb5..1c8b8b9bea 100644 --- a/src/Umbraco.Core/Events/MediaRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/MediaSavedNotification.cs b/src/Umbraco.Core/Notifications/MediaSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaSavedNotification.cs rename to src/Umbraco.Core/Notifications/MediaSavedNotification.cs index e23e95c27d..addeda617e 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaSavingNotification.cs b/src/Umbraco.Core/Notifications/MediaSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MediaSavingNotification.cs rename to src/Umbraco.Core/Notifications/MediaSavingNotification.cs index 50deaffaed..638d27c968 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MediaSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MediaTreeChangeNotification.cs b/src/Umbraco.Core/Notifications/MediaTreeChangeNotification.cs similarity index 95% rename from src/Umbraco.Core/Services/Notifications/MediaTreeChangeNotification.cs rename to src/Umbraco.Core/Notifications/MediaTreeChangeNotification.cs index 264bdb6bb2..00e0e6b42c 100644 --- a/src/Umbraco.Core/Services/Notifications/MediaTreeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTreeChangeNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MediaTreeChangeNotification : TreeChangeNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeChangedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeChangedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MediaTypeChangedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeChangedNotification.cs index 9018ab3186..322a6bb1ab 100644 --- a/src/Umbraco.Core/Events/MediaTypeChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeChangedNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeChangedNotification : ContentTypeChangeNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeDeletedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MediaTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeDeletedNotification.cs index 77ff9ce720..59c7114ca0 100644 --- a/src/Umbraco.Core/Events/MediaTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeDeletedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeDeletingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeDeletingNotification.cs index 4aa45b0dee..1cb4f7c99d 100644 --- a/src/Umbraco.Core/Events/MediaTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeDeletingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeMovedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeMovedNotification.cs index ab53cc6cce..c17aa222de 100644 --- a/src/Umbraco.Core/Events/MediaTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeMovedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeMovingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MediaTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeMovingNotification.cs index f9bdd78ab1..43499430b0 100644 --- a/src/Umbraco.Core/Events/MediaTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeMovingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeRefreshedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeRefreshedNotification.cs similarity index 91% rename from src/Umbraco.Core/Events/MediaTypeRefreshedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeRefreshedNotification.cs index 91d04811bb..6b59e3220e 100644 --- a/src/Umbraco.Core/Events/MediaTypeRefreshedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeRefreshedNotification.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Events/MediaTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeSavedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MediaTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeSavedNotification.cs index 212b8e0933..b4b2372b7f 100644 --- a/src/Umbraco.Core/Events/MediaTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/MediaTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/MediaTypeSavingNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MediaTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/MediaTypeSavingNotification.cs index 8868b19991..0a93f08671 100644 --- a/src/Umbraco.Core/Events/MediaTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MediaTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MemberCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MemberCacheRefresherNotification.cs index 6154505947..c2d920843d 100644 --- a/src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MemberCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberDeletedNotification.cs b/src/Umbraco.Core/Notifications/MemberDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/MemberDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MemberDeletedNotification.cs index 09e27e3c43..7539d6b133 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberDeletingNotification.cs b/src/Umbraco.Core/Notifications/MemberDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MemberDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MemberDeletingNotification.cs index dd0fbcb70c..9d09d40e15 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupCacheRefresherNotification.cs index 643e9bd51e..f882b61167 100644 --- a/src/Umbraco.Core/Cache/MemberGroupCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletedNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupDeletedNotification.cs similarity index 85% rename from src/Umbraco.Core/Services/Notifications/MemberGroupDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupDeletedNotification.cs index 59a827ee20..8665cc5f71 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupDeletedNotification.cs @@ -1,7 +1,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletingNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupDeletingNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MemberGroupDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupDeletingNotification.cs index 4f3d06dbc7..2b0f94af64 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupDeletingNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupSavedNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupSavedNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MemberGroupSavedNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupSavedNotification.cs index 745660d8c7..e5beffe76b 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupSavedNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberGroupSavingNotification.cs b/src/Umbraco.Core/Notifications/MemberGroupSavingNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/MemberGroupSavingNotification.cs rename to src/Umbraco.Core/Notifications/MemberGroupSavingNotification.cs index 49d88ea92a..a0341ab2ef 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberGroupSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberGroupSavingNotification.cs @@ -2,7 +2,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class MemberGroupSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/MemberRefreshNotification.cs b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberRefreshNotification.cs rename to src/Umbraco.Core/Notifications/MemberRefreshNotification.cs index a5ea0911bf..a22c48348f 100644 --- a/src/Umbraco.Core/Events/MemberRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/MemberRolesNotification.cs b/src/Umbraco.Core/Notifications/MemberRolesNotification.cs similarity index 79% rename from src/Umbraco.Core/Services/Notifications/MemberRolesNotification.cs rename to src/Umbraco.Core/Notifications/MemberRolesNotification.cs index bfc576fbb0..9ea6548833 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberRolesNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberRolesNotification.cs @@ -1,6 +1,4 @@ -using Umbraco.Cms.Core.Events; - -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class MemberRolesNotification : INotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberSavedNotification.cs b/src/Umbraco.Core/Notifications/MemberSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MemberSavedNotification.cs rename to src/Umbraco.Core/Notifications/MemberSavedNotification.cs index 9f245502bd..2c4f4755eb 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/MemberSavingNotification.cs b/src/Umbraco.Core/Notifications/MemberSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/MemberSavingNotification.cs rename to src/Umbraco.Core/Notifications/MemberSavingNotification.cs index 37c90a9127..fc8198c6f9 100644 --- a/src/Umbraco.Core/Services/Notifications/MemberSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class MemberSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeChangedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeChangedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MemberTypeChangedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeChangedNotification.cs index 576f52cf09..c22908c108 100644 --- a/src/Umbraco.Core/Events/MemberTypeChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeChangedNotification.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeChangedNotification : ContentTypeChangeNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeDeletedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeDeletedNotification.cs index bdd6108dd0..490db24cf3 100644 --- a/src/Umbraco.Core/Events/MemberTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeDeletedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeDeletingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeDeletingNotification.cs index 8c17f31a26..04821eb0c2 100644 --- a/src/Umbraco.Core/Events/MemberTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeDeletingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeMovedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeMovedNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeMovedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeMovedNotification.cs index 325c765451..8e74076119 100644 --- a/src/Umbraco.Core/Events/MemberTypeMovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeMovedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeMovedNotification : MovedNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeMovingNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeMovingNotification.cs similarity index 86% rename from src/Umbraco.Core/Events/MemberTypeMovingNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeMovingNotification.cs index eff8f8b9ef..b4627aaf30 100644 --- a/src/Umbraco.Core/Events/MemberTypeMovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeMovingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeMovingNotification : MovingNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeRefreshedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeRefreshedNotification.cs similarity index 91% rename from src/Umbraco.Core/Events/MemberTypeRefreshedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeRefreshedNotification.cs index 5cefd64946..89147a523f 100644 --- a/src/Umbraco.Core/Events/MemberTypeRefreshedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeRefreshedNotification.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Events/MemberTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeSavedNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MemberTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeSavedNotification.cs index 5d877dea3f..768f9e8bb0 100644 --- a/src/Umbraco.Core/Events/MemberTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeSavedNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Events/MemberTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/MemberTypeSavingNotification.cs similarity index 85% rename from src/Umbraco.Core/Events/MemberTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/MemberTypeSavingNotification.cs index de06be6c8d..598aadffa4 100644 --- a/src/Umbraco.Core/Events/MemberTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberTypeSavingNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class MemberTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs b/src/Umbraco.Core/Notifications/ModelBindingErrorNotification.cs similarity index 78% rename from src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs rename to src/Umbraco.Core/Notifications/ModelBindingErrorNotification.cs index f32d1273a4..e4adadcd52 100644 --- a/src/Umbraco.Web.Common/ModelBinders/ModelBindingError.cs +++ b/src/Umbraco.Core/Notifications/ModelBindingErrorNotification.cs @@ -1,18 +1,17 @@ using System; using System.Text; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Web.Common.ModelBinders +namespace Umbraco.Cms.Core.Notifications { /// /// Contains event data for the event. /// - public class ModelBindingError : INotification + public class ModelBindingErrorNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ModelBindingError(Type sourceType, Type modelType, StringBuilder message) + public ModelBindingErrorNotification(Type sourceType, Type modelType, StringBuilder message) { SourceType = sourceType; ModelType = modelType; diff --git a/src/Umbraco.Core/Events/MovedNotification.cs b/src/Umbraco.Core/Notifications/MovedNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MovedNotification.cs rename to src/Umbraco.Core/Notifications/MovedNotification.cs index af96e70feb..4573d5e45a 100644 --- a/src/Umbraco.Core/Events/MovedNotification.cs +++ b/src/Umbraco.Core/Notifications/MovedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class MovedNotification : ObjectNotification>> { diff --git a/src/Umbraco.Core/Services/Notifications/MovedToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MovedToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MovedToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MovedToRecycleBinNotification.cs index 8470edd311..1e02d30eb7 100644 --- a/src/Umbraco.Core/Services/Notifications/MovedToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MovedToRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class MovedToRecycleBinNotification : ObjectNotification>> { diff --git a/src/Umbraco.Core/Events/MovingNotification.cs b/src/Umbraco.Core/Notifications/MovingNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/MovingNotification.cs rename to src/Umbraco.Core/Notifications/MovingNotification.cs index 917cc283aa..6bf493fc1b 100644 --- a/src/Umbraco.Core/Events/MovingNotification.cs +++ b/src/Umbraco.Core/Notifications/MovingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class MovingNotification : CancelableObjectNotification>> { diff --git a/src/Umbraco.Core/Services/Notifications/MovingToRecycleBinNotification.cs b/src/Umbraco.Core/Notifications/MovingToRecycleBinNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/MovingToRecycleBinNotification.cs rename to src/Umbraco.Core/Notifications/MovingToRecycleBinNotification.cs index b62e6cfa94..ef8c36ce6f 100644 --- a/src/Umbraco.Core/Services/Notifications/MovingToRecycleBinNotification.cs +++ b/src/Umbraco.Core/Notifications/MovingToRecycleBinNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public abstract class MovingToRecycleBinNotification : CancelableObjectNotification>> { diff --git a/src/Umbraco.Core/Events/NotificationExtensions.cs b/src/Umbraco.Core/Notifications/NotificationExtensions.cs similarity index 92% rename from src/Umbraco.Core/Events/NotificationExtensions.cs rename to src/Umbraco.Core/Notifications/NotificationExtensions.cs index de51f016e4..53677c05a1 100644 --- a/src/Umbraco.Core/Events/NotificationExtensions.cs +++ b/src/Umbraco.Core/Notifications/NotificationExtensions.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public static class NotificationExtensions { diff --git a/src/Umbraco.Core/Events/ObjectNotification.cs b/src/Umbraco.Core/Notifications/ObjectNotification.cs similarity index 84% rename from src/Umbraco.Core/Events/ObjectNotification.cs rename to src/Umbraco.Core/Notifications/ObjectNotification.cs index f1d957d42b..a550754d32 100644 --- a/src/Umbraco.Core/Events/ObjectNotification.cs +++ b/src/Umbraco.Core/Notifications/ObjectNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class ObjectNotification : StatefulNotification where T : class { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewCreatedNotification.cs b/src/Umbraco.Core/Notifications/PartialViewCreatedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/PartialViewCreatedNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewCreatedNotification.cs index 92f5738e71..3f34c4b1c6 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewCreatedNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewCreatedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewCreatedNotification : CreatedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewCreatingNotification.cs b/src/Umbraco.Core/Notifications/PartialViewCreatingNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/PartialViewCreatingNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewCreatingNotification.cs index 1439db3af1..425879fb06 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewCreatingNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewCreatingNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewCreatingNotification : CreatingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewDeletedNotification.cs b/src/Umbraco.Core/Notifications/PartialViewDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/PartialViewDeletedNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewDeletedNotification.cs index 60d6d1aae4..4ef4058b5c 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewDeletingNotification.cs b/src/Umbraco.Core/Notifications/PartialViewDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/PartialViewDeletingNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewDeletingNotification.cs index 67ee8314c7..6473713408 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewSavedNotification.cs b/src/Umbraco.Core/Notifications/PartialViewSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/PartialViewSavedNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewSavedNotification.cs index 9ecde239a6..d50ed08faf 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PartialViewSavingNotification.cs b/src/Umbraco.Core/Notifications/PartialViewSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/PartialViewSavingNotification.cs rename to src/Umbraco.Core/Notifications/PartialViewSavingNotification.cs index 98140b1c8a..fd2e0ee34a 100644 --- a/src/Umbraco.Core/Services/Notifications/PartialViewSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/PartialViewSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class PartialViewSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessCacheRefresherNotification.cs index d3334571b3..1e753217ab 100644 --- a/src/Umbraco.Core/Cache/PublicAccessCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class PublicAccessCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletedNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletedNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntryDeletedNotification.cs index 1dd8fb6bca..f6aa16500a 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntryDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletingNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletingNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntryDeletingNotification.cs index a02388c7b4..42c4c1bdb9 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntryDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntryDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntryDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavedNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntrySavedNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavedNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntrySavedNotification.cs index 6f72bc6ed3..8c0d253500 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavedNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntrySavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntrySavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavingNotification.cs b/src/Umbraco.Core/Notifications/PublicAccessEntrySavingNotification.cs similarity index 92% rename from src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavingNotification.cs rename to src/Umbraco.Core/Notifications/PublicAccessEntrySavingNotification.cs index 57a6709b13..3fbd666b8d 100644 --- a/src/Umbraco.Core/Services/Notifications/PublicAccessEntrySavingNotification.cs +++ b/src/Umbraco.Core/Notifications/PublicAccessEntrySavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class PublicAccessEntrySavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationDeletedNotification.cs b/src/Umbraco.Core/Notifications/RelationDeletedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationDeletedNotification.cs rename to src/Umbraco.Core/Notifications/RelationDeletedNotification.cs index ca613c59c9..f7af0e9b29 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationDeletedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationDeletingNotification.cs b/src/Umbraco.Core/Notifications/RelationDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationDeletingNotification.cs rename to src/Umbraco.Core/Notifications/RelationDeletingNotification.cs index 7684d24c3d..8873d95226 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationSavedNotification.cs b/src/Umbraco.Core/Notifications/RelationSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationSavedNotification.cs rename to src/Umbraco.Core/Notifications/RelationSavedNotification.cs index c24e71f4bb..8b0313f87c 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationSavingNotification.cs b/src/Umbraco.Core/Notifications/RelationSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationSavingNotification.cs rename to src/Umbraco.Core/Notifications/RelationSavingNotification.cs index f88d42c10a..5afe71da53 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeCacheRefresherNotification.cs index 851eba915d..ff8cf52891 100644 --- a/src/Umbraco.Core/Cache/RelationTypeCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletedNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/RelationTypeDeletedNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeDeletedNotification.cs index eee42da028..8534edcb49 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletingNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationTypeDeletingNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeDeletingNotification.cs index 1749817677..904a82c08b 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeSavedNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationTypeSavedNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeSavedNotification.cs index 489f53dc65..e2e69475d7 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RelationTypeSavingNotification.cs b/src/Umbraco.Core/Notifications/RelationTypeSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/RelationTypeSavingNotification.cs rename to src/Umbraco.Core/Notifications/RelationTypeSavingNotification.cs index 402f6c6eeb..2fdebe97e7 100644 --- a/src/Umbraco.Core/Services/Notifications/RelationTypeSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/RelationTypeSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RelationTypeSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/RemovedMemberRolesNotification.cs b/src/Umbraco.Core/Notifications/RemovedMemberRolesNotification.cs similarity index 81% rename from src/Umbraco.Core/Services/Notifications/RemovedMemberRolesNotification.cs rename to src/Umbraco.Core/Notifications/RemovedMemberRolesNotification.cs index 1e7d149d45..ed76cfbf69 100644 --- a/src/Umbraco.Core/Services/Notifications/RemovedMemberRolesNotification.cs +++ b/src/Umbraco.Core/Notifications/RemovedMemberRolesNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class RemovedMemberRolesNotification : MemberRolesNotification { diff --git a/src/Umbraco.Core/Events/RenamedNotification.cs b/src/Umbraco.Core/Notifications/RenamedNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/RenamedNotification.cs rename to src/Umbraco.Core/Notifications/RenamedNotification.cs index 81a568b6df..724069aba7 100644 --- a/src/Umbraco.Core/Events/RenamedNotification.cs +++ b/src/Umbraco.Core/Notifications/RenamedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class RenamedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/RenamingNotification.cs b/src/Umbraco.Core/Notifications/RenamingNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/RenamingNotification.cs rename to src/Umbraco.Core/Notifications/RenamingNotification.cs index f215a8ea54..1e4184bc3d 100644 --- a/src/Umbraco.Core/Events/RenamingNotification.cs +++ b/src/Umbraco.Core/Notifications/RenamingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class RenamingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/RolledBackNotification.cs b/src/Umbraco.Core/Notifications/RolledBackNotification.cs similarity index 81% rename from src/Umbraco.Core/Events/RolledBackNotification.cs rename to src/Umbraco.Core/Notifications/RolledBackNotification.cs index 596cb29520..fded45c6b1 100644 --- a/src/Umbraco.Core/Events/RolledBackNotification.cs +++ b/src/Umbraco.Core/Notifications/RolledBackNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class RolledBackNotification : ObjectNotification where T : class { diff --git a/src/Umbraco.Core/Events/RollingBackNotification.cs b/src/Umbraco.Core/Notifications/RollingBackNotification.cs similarity index 82% rename from src/Umbraco.Core/Events/RollingBackNotification.cs rename to src/Umbraco.Core/Notifications/RollingBackNotification.cs index 265669bcdf..1064a7897c 100644 --- a/src/Umbraco.Core/Events/RollingBackNotification.cs +++ b/src/Umbraco.Core/Notifications/RollingBackNotification.cs @@ -1,7 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -namespace Umbraco.Cms.Core.Events +using Umbraco.Cms.Core.Events; + +namespace Umbraco.Cms.Core.Notifications { public abstract class RollingBackNotification : CancelableObjectNotification where T : class { diff --git a/src/Umbraco.Core/Routing/RoutingRequestNotification.cs b/src/Umbraco.Core/Notifications/RoutingRequestNotification.cs similarity index 89% rename from src/Umbraco.Core/Routing/RoutingRequestNotification.cs rename to src/Umbraco.Core/Notifications/RoutingRequestNotification.cs index 7104274e39..c8b2d8e0d6 100644 --- a/src/Umbraco.Core/Routing/RoutingRequestNotification.cs +++ b/src/Umbraco.Core/Notifications/RoutingRequestNotification.cs @@ -1,6 +1,6 @@ -using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Routing; -namespace Umbraco.Cms.Core.Routing +namespace Umbraco.Cms.Core.Notifications { /// /// Used for notifying when an Umbraco request is being built diff --git a/src/Umbraco.Core/Events/SavedNotification.cs b/src/Umbraco.Core/Notifications/SavedNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/SavedNotification.cs rename to src/Umbraco.Core/Notifications/SavedNotification.cs index 9ed7afd933..0a9af8c1ff 100644 --- a/src/Umbraco.Core/Events/SavedNotification.cs +++ b/src/Umbraco.Core/Notifications/SavedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SavedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/SavingNotification.cs b/src/Umbraco.Core/Notifications/SavingNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/SavingNotification.cs rename to src/Umbraco.Core/Notifications/SavingNotification.cs index d58607be10..34962f5396 100644 --- a/src/Umbraco.Core/Events/SavingNotification.cs +++ b/src/Umbraco.Core/Notifications/SavingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SavingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/ScopedEntityRemoveNotification.cs b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs similarity index 87% rename from src/Umbraco.Core/Events/ScopedEntityRemoveNotification.cs rename to src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs index c2c7b9d3f2..307ae2103c 100644 --- a/src/Umbraco.Core/Events/ScopedEntityRemoveNotification.cs +++ b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs @@ -1,8 +1,9 @@ using System; using System.ComponentModel; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Umbraco.Core/Services/Notifications/ScriptDeletedNotification.cs b/src/Umbraco.Core/Notifications/ScriptDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/ScriptDeletedNotification.cs rename to src/Umbraco.Core/Notifications/ScriptDeletedNotification.cs index 27ea080204..650f2d0564 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ScriptDeletingNotification.cs b/src/Umbraco.Core/Notifications/ScriptDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ScriptDeletingNotification.cs rename to src/Umbraco.Core/Notifications/ScriptDeletingNotification.cs index 8ee6db4406..085c98d600 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ScriptSavedNotification.cs b/src/Umbraco.Core/Notifications/ScriptSavedNotification.cs similarity index 90% rename from src/Umbraco.Core/Services/Notifications/ScriptSavedNotification.cs rename to src/Umbraco.Core/Notifications/ScriptSavedNotification.cs index 86cb55426c..6ccb9f1446 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/ScriptSavingNotification.cs b/src/Umbraco.Core/Notifications/ScriptSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/ScriptSavingNotification.cs rename to src/Umbraco.Core/Notifications/ScriptSavingNotification.cs index dc8c5a8990..92ad0ded4e 100644 --- a/src/Umbraco.Core/Services/Notifications/ScriptSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/ScriptSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class ScriptSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Notifications/SendEmailNotification.cs b/src/Umbraco.Core/Notifications/SendEmailNotification.cs new file mode 100644 index 0000000000..194ee68edc --- /dev/null +++ b/src/Umbraco.Core/Notifications/SendEmailNotification.cs @@ -0,0 +1,11 @@ +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.Notifications +{ + public class SendEmailNotification : INotification + { + public SendEmailNotification(EmailMessage message) => Message = message; + + public EmailMessage Message { get; set; } + } +} diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingContentNotification.cs b/src/Umbraco.Core/Notifications/SendingContentNotification.cs similarity index 85% rename from src/Umbraco.Web.BackOffice/Filters/SendingContentNotification.cs rename to src/Umbraco.Core/Notifications/SendingContentNotification.cs index 3955c95e54..4d8d93ce75 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingContentNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingContentNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingContentNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingDashboardsNotification.cs b/src/Umbraco.Core/Notifications/SendingDashboardsNotification.cs similarity index 88% rename from src/Umbraco.Web.BackOffice/Filters/SendingDashboardsNotification.cs rename to src/Umbraco.Core/Notifications/SendingDashboardsNotification.cs index cfaa91fc2b..b81339fcbf 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingDashboardsNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingDashboardsNotification.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Dashboards; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingDashboardsNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingMediaNotification.cs b/src/Umbraco.Core/Notifications/SendingMediaNotification.cs similarity index 85% rename from src/Umbraco.Web.BackOffice/Filters/SendingMediaNotification.cs rename to src/Umbraco.Core/Notifications/SendingMediaNotification.cs index cc5d7e7620..2fd8f65a4d 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingMediaNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingMediaNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingMediaNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingMemberNotification.cs b/src/Umbraco.Core/Notifications/SendingMemberNotification.cs similarity index 85% rename from src/Umbraco.Web.BackOffice/Filters/SendingMemberNotification.cs rename to src/Umbraco.Core/Notifications/SendingMemberNotification.cs index ea2a1d8927..cc868836f9 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingMemberNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingMemberNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingMemberNotification : INotification { diff --git a/src/Umbraco.Web.BackOffice/Filters/SendingUserNotification.cs b/src/Umbraco.Core/Notifications/SendingUserNotification.cs similarity index 84% rename from src/Umbraco.Web.BackOffice/Filters/SendingUserNotification.cs rename to src/Umbraco.Core/Notifications/SendingUserNotification.cs index 91bbe2f0db..9e3422f1d9 100644 --- a/src/Umbraco.Web.BackOffice/Filters/SendingUserNotification.cs +++ b/src/Umbraco.Core/Notifications/SendingUserNotification.cs @@ -1,8 +1,7 @@ -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Web.BackOffice.Filters +namespace Umbraco.Cms.Core.Notifications { public class SendingUserNotification : INotification { diff --git a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs b/src/Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs similarity index 60% rename from src/Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs rename to src/Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs index ffa8713add..7fa83a5a6d 100644 --- a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParsing.cs +++ b/src/Umbraco.Core/Notifications/ServerVariablesParsingNotification.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Infrastructure.WebAssets +namespace Umbraco.Cms.Core.Notifications { /// /// A notification for when server variables are parsing /// - public class ServerVariablesParsing : INotification + public class ServerVariablesParsingNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ServerVariablesParsing(IDictionary serverVariables) => ServerVariables = serverVariables; + public ServerVariablesParsingNotification(IDictionary serverVariables) => ServerVariables = serverVariables; /// /// Gets a mutable dictionary of server variables diff --git a/src/Umbraco.Core/Events/SortedNotification.cs b/src/Umbraco.Core/Notifications/SortedNotification.cs similarity index 84% rename from src/Umbraco.Core/Events/SortedNotification.cs rename to src/Umbraco.Core/Notifications/SortedNotification.cs index 11c81f001b..ffc50d6bc9 100644 --- a/src/Umbraco.Core/Events/SortedNotification.cs +++ b/src/Umbraco.Core/Notifications/SortedNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SortedNotification : EnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/SortingNotification.cs b/src/Umbraco.Core/Notifications/SortingNotification.cs similarity index 84% rename from src/Umbraco.Core/Events/SortingNotification.cs rename to src/Umbraco.Core/Notifications/SortingNotification.cs index 78a301dfb5..1801bfa656 100644 --- a/src/Umbraco.Core/Events/SortingNotification.cs +++ b/src/Umbraco.Core/Notifications/SortingNotification.cs @@ -2,8 +2,9 @@ // See LICENSE for more details. using System.Collections.Generic; +using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class SortingNotification : CancelableEnumerableObjectNotification { diff --git a/src/Umbraco.Core/Events/StatefulNotification.cs b/src/Umbraco.Core/Notifications/StatefulNotification.cs similarity index 93% rename from src/Umbraco.Core/Events/StatefulNotification.cs rename to src/Umbraco.Core/Notifications/StatefulNotification.cs index 4d86262322..cf01b86e58 100644 --- a/src/Umbraco.Core/Events/StatefulNotification.cs +++ b/src/Umbraco.Core/Notifications/StatefulNotification.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class StatefulNotification : IStatefulNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetDeletedNotification.cs b/src/Umbraco.Core/Notifications/StylesheetDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/StylesheetDeletedNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetDeletedNotification.cs index 12b84283bc..743cadab63 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetDeletingNotification.cs b/src/Umbraco.Core/Notifications/StylesheetDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/StylesheetDeletingNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetDeletingNotification.cs index eccf10d4c6..8a0c411b13 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetSavedNotification.cs b/src/Umbraco.Core/Notifications/StylesheetSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/StylesheetSavedNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetSavedNotification.cs index 1e0690ba88..0ceeb209e0 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/StylesheetSavingNotification.cs b/src/Umbraco.Core/Notifications/StylesheetSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/StylesheetSavingNotification.cs rename to src/Umbraco.Core/Notifications/StylesheetSavingNotification.cs index 6bd2f2e4a7..d08bdebac4 100644 --- a/src/Umbraco.Core/Services/Notifications/StylesheetSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/StylesheetSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class StylesheetSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/TemplateCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/TemplateCacheRefresherNotification.cs index 88ff2284cb..689d2a52ff 100644 --- a/src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class TemplateCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateDeletedNotification.cs b/src/Umbraco.Core/Notifications/TemplateDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/TemplateDeletedNotification.cs rename to src/Umbraco.Core/Notifications/TemplateDeletedNotification.cs index 2b51f6adc1..01d6dc7e6d 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateDeletingNotification.cs b/src/Umbraco.Core/Notifications/TemplateDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/TemplateDeletingNotification.cs rename to src/Umbraco.Core/Notifications/TemplateDeletingNotification.cs index 9384aa9af5..6434c47c46 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateSavedNotification.cs b/src/Umbraco.Core/Notifications/TemplateSavedNotification.cs similarity index 96% rename from src/Umbraco.Core/Services/Notifications/TemplateSavedNotification.cs rename to src/Umbraco.Core/Notifications/TemplateSavedNotification.cs index 81940620c5..7cfbd8a7bb 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/TemplateSavingNotification.cs b/src/Umbraco.Core/Notifications/TemplateSavingNotification.cs similarity index 97% rename from src/Umbraco.Core/Services/Notifications/TemplateSavingNotification.cs rename to src/Umbraco.Core/Notifications/TemplateSavingNotification.cs index 36c70e72dd..95023441d7 100644 --- a/src/Umbraco.Core/Services/Notifications/TemplateSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/TemplateSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public class TemplateSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Events/TreeChangeNotification.cs b/src/Umbraco.Core/Notifications/TreeChangeNotification.cs similarity index 88% rename from src/Umbraco.Core/Events/TreeChangeNotification.cs rename to src/Umbraco.Core/Notifications/TreeChangeNotification.cs index 83b28383a5..bdbd0fc044 100644 --- a/src/Umbraco.Core/Events/TreeChangeNotification.cs +++ b/src/Umbraco.Core/Notifications/TreeChangeNotification.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Services.Changes; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public abstract class TreeChangeNotification : EnumerableObjectNotification> { diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs new file mode 100644 index 0000000000..c29df4e85f --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs @@ -0,0 +1,22 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Notifications +{ + /// + /// Notification that occurs during the Umbraco boot process, before instances of initialize. + /// + public class UmbracoApplicationComponentsInstallingNotification : INotification + { + /// + /// Initializes a new instance of the class. + /// + /// The runtime level + public UmbracoApplicationComponentsInstallingNotification(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; + + /// + /// Gets the runtime level of execution. + /// + public RuntimeLevel RuntimeLevel { get; } + } +} diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs new file mode 100644 index 0000000000..6a66e2413f --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs @@ -0,0 +1,19 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Notifications +{ + /// + /// Notification that occurs during Umbraco boot after the MainDom has been acquired. + /// + public class UmbracoApplicationMainDomAcquiredNotification : INotification + { + /// + /// Initializes a new instance of the class. + /// + /// The runtime level + public UmbracoApplicationMainDomAcquiredNotification() + { + } + } +} diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs new file mode 100644 index 0000000000..4cbf0a55c6 --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationStartingNotification.cs @@ -0,0 +1,23 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Notifications +{ + /// + /// Notification that occurs at the very end of the Umbraco boot + /// process and after all initialize. + /// + public class UmbracoApplicationStartingNotification : INotification + { + /// + /// Initializes a new instance of the class. + /// + /// The runtime level + public UmbracoApplicationStartingNotification(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel; + + /// + /// Gets the runtime level of execution. + /// + public RuntimeLevel RuntimeLevel { get; } + } +} diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs new file mode 100644 index 0000000000..db86a1e614 --- /dev/null +++ b/src/Umbraco.Core/Notifications/UmbracoApplicationStoppingNotification.cs @@ -0,0 +1,4 @@ +namespace Umbraco.Cms.Core.Notifications +{ + public class UmbracoApplicationStoppingNotification : INotification { } +} diff --git a/src/Umbraco.Core/Events/UmbracoRequestBegin.cs b/src/Umbraco.Core/Notifications/UmbracoRequestBeginNotification.cs similarity index 63% rename from src/Umbraco.Core/Events/UmbracoRequestBegin.cs rename to src/Umbraco.Core/Notifications/UmbracoRequestBeginNotification.cs index 00eb41df96..76683f8d65 100644 --- a/src/Umbraco.Core/Events/UmbracoRequestBegin.cs +++ b/src/Umbraco.Core/Notifications/UmbracoRequestBeginNotification.cs @@ -3,17 +3,17 @@ using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// Notification raised on each request begin. /// - public class UmbracoRequestBegin : INotification + public class UmbracoRequestBeginNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public UmbracoRequestBegin(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; + public UmbracoRequestBeginNotification(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; /// /// Gets the diff --git a/src/Umbraco.Core/Events/UmbracoRequestEnd.cs b/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs similarity index 60% rename from src/Umbraco.Core/Events/UmbracoRequestEnd.cs rename to src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs index b4e0f26b81..27fb6ff09d 100644 --- a/src/Umbraco.Core/Events/UmbracoRequestEnd.cs +++ b/src/Umbraco.Core/Notifications/UmbracoRequestEndNotification.cs @@ -1,19 +1,19 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using Umbraco.Cms.Core.Web; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// Notification raised on each request end. /// - public class UmbracoRequestEnd : INotification + public class UmbracoRequestEndNotification : INotification { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public UmbracoRequestEnd(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; + public UmbracoRequestEndNotification(IUmbracoContext umbracoContext) => UmbracoContext = umbracoContext; /// /// Gets the diff --git a/src/Umbraco.Core/Events/UnattendedInstallNotification.cs b/src/Umbraco.Core/Notifications/UnattendedInstallNotification.cs similarity index 81% rename from src/Umbraco.Core/Events/UnattendedInstallNotification.cs rename to src/Umbraco.Core/Notifications/UnattendedInstallNotification.cs index 20373ad15b..7f9b239ce2 100644 --- a/src/Umbraco.Core/Events/UnattendedInstallNotification.cs +++ b/src/Umbraco.Core/Notifications/UnattendedInstallNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { /// /// Used to notify that an Unattended install has completed diff --git a/src/Umbraco.Core/Events/UninstallPackageNotification.cs b/src/Umbraco.Core/Notifications/UninstallPackageNotification.cs similarity index 90% rename from src/Umbraco.Core/Events/UninstallPackageNotification.cs rename to src/Umbraco.Core/Notifications/UninstallPackageNotification.cs index 2ce76fd5fc..de6afc4eb6 100644 --- a/src/Umbraco.Core/Events/UninstallPackageNotification.cs +++ b/src/Umbraco.Core/Notifications/UninstallPackageNotification.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Packaging; -namespace Umbraco.Cms.Core.Events +namespace Umbraco.Cms.Core.Notifications { public class UninstallPackageNotification : INotification { diff --git a/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/UserCacheRefresherNotification.cs similarity index 86% rename from src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/UserCacheRefresherNotification.cs index b91a7b93c8..4181d74dd7 100644 --- a/src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/UserCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class UserCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserDeletedNotification.cs b/src/Umbraco.Core/Notifications/UserDeletedNotification.cs similarity index 87% rename from src/Umbraco.Core/Services/Notifications/UserDeletedNotification.cs rename to src/Umbraco.Core/Notifications/UserDeletedNotification.cs index 4347211847..c272e51b22 100644 --- a/src/Umbraco.Core/Services/Notifications/UserDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserDeletingNotification.cs b/src/Umbraco.Core/Notifications/UserDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserDeletingNotification.cs rename to src/Umbraco.Core/Notifications/UserDeletingNotification.cs index 5e5c094b65..febfa27d94 100644 --- a/src/Umbraco.Core/Services/Notifications/UserDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs b/src/Umbraco.Core/Notifications/UserForgotPasswordChangedNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs rename to src/Umbraco.Core/Notifications/UserForgotPasswordChangedNotification.cs index 04fb74a1ca..b4e93f8b67 100644 --- a/src/Umbraco.Web.Common/Security/UserForgotPasswordChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserForgotPasswordChangedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserForgotPasswordChangedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs b/src/Umbraco.Core/Notifications/UserForgotPasswordRequestedNotification.cs similarity index 87% rename from src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs rename to src/Umbraco.Core/Notifications/UserForgotPasswordRequestedNotification.cs index 766e1c61ca..608e5c0f63 100644 --- a/src/Umbraco.Web.Common/Security/UserForgotPasswordRequestedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserForgotPasswordRequestedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserForgotPasswordRequestedNotification : UserNotification { diff --git a/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs b/src/Umbraco.Core/Notifications/UserGroupCacheRefresherNotification.cs similarity index 87% rename from src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupCacheRefresherNotification.cs index a0adf915d8..7aca0d5edb 100644 --- a/src/Umbraco.Core/Cache/UserGroupCacheRefresherNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupCacheRefresherNotification.cs @@ -1,6 +1,6 @@ using Umbraco.Cms.Core.Sync; -namespace Umbraco.Cms.Core.Cache +namespace Umbraco.Cms.Core.Notifications { public class UserGroupCacheRefresherNotification : CacheRefresherNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupDeletedNotification.cs b/src/Umbraco.Core/Notifications/UserGroupDeletedNotification.cs similarity index 88% rename from src/Umbraco.Core/Services/Notifications/UserGroupDeletedNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupDeletedNotification.cs index 934f607226..9877d95441 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupDeletedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupDeletedNotification.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupDeletedNotification : DeletedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupDeletingNotification.cs b/src/Umbraco.Core/Notifications/UserGroupDeletingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupDeletingNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupDeletingNotification.cs index 17ee3a5f83..af0e8d76d6 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupDeletingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupDeletingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupDeletingNotification : DeletingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupSavedNotification.cs b/src/Umbraco.Core/Notifications/UserGroupSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupSavedNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupSavedNotification.cs index 459c265eaa..fee23c06ea 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupSavingNotification.cs b/src/Umbraco.Core/Notifications/UserGroupSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupSavingNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupSavingNotification.cs index d479e2d15d..0dc074bfdc 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupSavingNotification : SavingNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavedNotification.cs b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavedNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupWithUsersSavedNotification.cs index d1a3ad2651..5e239660aa 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavedNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupWithUsersSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavingNotification.cs b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavingNotification.cs rename to src/Umbraco.Core/Notifications/UserGroupWithUsersSavingNotification.cs index a2705e7971..f3dd362c20 100644 --- a/src/Umbraco.Core/Services/Notifications/UserGroupWithUsersSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserGroupWithUsersSavingNotification.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserGroupWithUsersSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLockedNotification.cs b/src/Umbraco.Core/Notifications/UserLockedNotification.cs similarity index 85% rename from src/Umbraco.Web.Common/Security/UserLockedNotification.cs rename to src/Umbraco.Core/Notifications/UserLockedNotification.cs index 6c4737fb49..492296b7a2 100644 --- a/src/Umbraco.Web.Common/Security/UserLockedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLockedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLockedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs b/src/Umbraco.Core/Notifications/UserLoginFailedNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs rename to src/Umbraco.Core/Notifications/UserLoginFailedNotification.cs index de8578015e..ff07b57832 100644 --- a/src/Umbraco.Web.Common/Security/UserLoginFailedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginFailedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLoginFailedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs b/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs similarity index 87% rename from src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs rename to src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs index cff2d9f45e..b99bf58b7e 100644 --- a/src/Umbraco.Web.Common/Security/UserLoginRequiresVerificationNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginRequiresVerificationNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLoginRequiresVerificationNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs b/src/Umbraco.Core/Notifications/UserLoginSuccessNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs rename to src/Umbraco.Core/Notifications/UserLoginSuccessNotification.cs index bb9f3ccecb..e9b79c68fe 100644 --- a/src/Umbraco.Web.Common/Security/UserLoginSuccessNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLoginSuccessNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLoginSuccessNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs b/src/Umbraco.Core/Notifications/UserLogoutSuccessNotification.cs similarity index 88% rename from src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs rename to src/Umbraco.Core/Notifications/UserLogoutSuccessNotification.cs index 9ef9d5d0e7..cc6a2f819b 100644 --- a/src/Umbraco.Web.Common/Security/UserLogoutSuccessNotification.cs +++ b/src/Umbraco.Core/Notifications/UserLogoutSuccessNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserLogoutSuccessNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserNotification.cs b/src/Umbraco.Core/Notifications/UserNotification.cs similarity index 93% rename from src/Umbraco.Web.Common/Security/UserNotification.cs rename to src/Umbraco.Core/Notifications/UserNotification.cs index 40d37ec82a..aa3223e980 100644 --- a/src/Umbraco.Web.Common/Security/UserNotification.cs +++ b/src/Umbraco.Core/Notifications/UserNotification.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Cms.Core.Events; -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public abstract class UserNotification : INotification { diff --git a/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs b/src/Umbraco.Core/Notifications/UserPasswordChangedNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs rename to src/Umbraco.Core/Notifications/UserPasswordChangedNotification.cs index 68115d411c..098be36867 100644 --- a/src/Umbraco.Web.Common/Security/UserPasswordChangedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserPasswordChangedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserPasswordChangedNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs b/src/Umbraco.Core/Notifications/UserPasswordResetNotification.cs similarity index 86% rename from src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs rename to src/Umbraco.Core/Notifications/UserPasswordResetNotification.cs index 618fa04f4c..fc60eef61e 100644 --- a/src/Umbraco.Web.Common/Security/UserPasswordResetNotification.cs +++ b/src/Umbraco.Core/Notifications/UserPasswordResetNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserPasswordResetNotification : UserNotification { diff --git a/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs b/src/Umbraco.Core/Notifications/UserResetAccessFailedCountNotification.cs similarity index 87% rename from src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs rename to src/Umbraco.Core/Notifications/UserResetAccessFailedCountNotification.cs index 9335bd76a0..5cd03cc140 100644 --- a/src/Umbraco.Web.Common/Security/UserResetAccessFailedCountNotification.cs +++ b/src/Umbraco.Core/Notifications/UserResetAccessFailedCountNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserResetAccessFailedCountNotification : UserNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserSavedNotification.cs b/src/Umbraco.Core/Notifications/UserSavedNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserSavedNotification.cs rename to src/Umbraco.Core/Notifications/UserSavedNotification.cs index b173709a2b..892218af82 100644 --- a/src/Umbraco.Core/Services/Notifications/UserSavedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserSavedNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserSavedNotification : SavedNotification { diff --git a/src/Umbraco.Core/Services/Notifications/UserSavingNotification.cs b/src/Umbraco.Core/Notifications/UserSavingNotification.cs similarity index 91% rename from src/Umbraco.Core/Services/Notifications/UserSavingNotification.cs rename to src/Umbraco.Core/Notifications/UserSavingNotification.cs index ddb10dd181..57c0d867fa 100644 --- a/src/Umbraco.Core/Services/Notifications/UserSavingNotification.cs +++ b/src/Umbraco.Core/Notifications/UserSavingNotification.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; -namespace Umbraco.Cms.Core.Services.Notifications +namespace Umbraco.Cms.Core.Notifications { public sealed class UserSavingNotification : SavingNotification { diff --git a/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs b/src/Umbraco.Core/Notifications/UserUnlockedNotification.cs similarity index 85% rename from src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs rename to src/Umbraco.Core/Notifications/UserUnlockedNotification.cs index 0ecba4d597..0c6cc7b9fd 100644 --- a/src/Umbraco.Web.Common/Security/UserUnlockedNotification.cs +++ b/src/Umbraco.Core/Notifications/UserUnlockedNotification.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Web.Common.Security +namespace Umbraco.Cms.Core.Notifications { public class UserUnlockedNotification : UserNotification { diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 2838a2678d..5dc25c31f1 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -133,7 +133,7 @@ namespace Umbraco.Cms.Core.Packaging private static string PrepareAsFilePathElement(string pathElement) { - return pathElement.TrimStart(new[] { '\\', '/', '~' }).Replace("/", "\\"); + return pathElement.TrimStart(new[] { '\\', '/', '~' }).Replace('/', Path.DirectorySeparatorChar); } private string UpdatePathPlaceholders(string path) diff --git a/src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs new file mode 100644 index 0000000000..ce76086ed2 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IFileRepository.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Umbraco.Cms.Core.Persistence.Repositories +{ + public interface IFileRepository + { + Stream GetFileContentStream(string filepath); + + void SetFileContent(string filepath, Stream content); + + long GetFileSize(string filepath); + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs new file mode 100644 index 0000000000..77c2f9d40b --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/IFileWithFoldersRepository.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.Persistence.Repositories +{ + public interface IFileWithFoldersRepository + { + void AddFolder(string folderPath); + + void DeleteFolder(string folderPath); + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs index b0eb7055ec..a8a84079fa 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs @@ -1,15 +1,8 @@ -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface IPartialViewRepository : IReadRepository, IWriteRepository + public interface IPartialViewRepository : IReadRepository, IWriteRepository, IFileRepository, IFileWithFoldersRepository { - void AddFolder(string folderPath); - void DeleteFolder(string folderPath); - bool ValidatePartialView(IPartialView partialView); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs index 6d2dd4bc57..604e1da8d2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs @@ -1,16 +1,9 @@ -using System.IO; +using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface IScriptRepository : IReadRepository, IWriteRepository + public interface IScriptRepository : IReadRepository, IWriteRepository, IFileRepository, IFileWithFoldersRepository { - bool ValidateScript(IScript script); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); - - void AddFolder(string folderPath); - void DeleteFolder(string folderPath); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs index 445630a535..dcdb5debe7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IStylesheetRepository.cs @@ -1,16 +1,8 @@ -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface IStylesheetRepository : IReadRepository, IWriteRepository + public interface IStylesheetRepository : IReadRepository, IWriteRepository, IFileRepository, IFileWithFoldersRepository { - bool ValidateStylesheet(IStylesheet stylesheet); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); - - void AddFolder(string folderPath); - void DeleteFolder(string folderPath); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs index 24c83ff29d..3c9174e818 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITemplateRepository.cs @@ -1,42 +1,16 @@ -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Persistence.Repositories { - public interface ITemplateRepository : IReadWriteQueryRepository + public interface ITemplateRepository : IReadWriteQueryRepository, IFileRepository { ITemplate Get(string alias); IEnumerable GetAll(params string[] aliases); IEnumerable GetChildren(int masterTemplateId); - IEnumerable GetChildren(string alias); IEnumerable GetDescendants(int masterTemplateId); - IEnumerable GetDescendants(string alias); - - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - bool ValidateTemplate(ITemplate template); - - /// - /// Gets the content of a template as a stream. - /// - /// The filesystem path to the template. - /// The content of the template. - Stream GetFileContentStream(string filepath); - - /// - /// Sets the content of a template. - /// - /// The filesystem path to the template. - /// The content of the template. - void SetFileContent(string filepath, Stream content); - - long GetFileSize(string filepath); } } diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index de011c6555..e2fc0071be 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -2,12 +2,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; -using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Serialization; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors @@ -29,24 +25,11 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// Initializes a new instance of the class. /// - public DataEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer, - EditorType type = EditorType.PropertyValue) + public DataEditor(IDataValueEditorFactory dataValueEditorFactory, EditorType type = EditorType.PropertyValue) { - LoggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - DataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); - LocalizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); - LocalizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService)); - ShortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper)); - JsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer)); - // defaults + DataValueEditorFactory = dataValueEditorFactory; Type = type; Icon = Constants.Icons.PropertyEditor; Group = Constants.PropertyEditors.Groups.Common; @@ -68,17 +51,12 @@ namespace Umbraco.Cms.Core.PropertyEditors /// protected DataEditorAttribute Attribute { get; } - protected IShortStringHelper ShortStringHelper { get; } - public IJsonSerializer JsonSerializer { get; } - protected ILocalizedTextService LocalizedTextService { get; } - protected ILocalizationService LocalizationService { get; } - protected ILoggerFactory LoggerFactory { get; } - protected IDataTypeService DataTypeService { get; } - /// [DataMember(Name = "alias", IsRequired = true)] public string Alias { get; set; } + protected IDataValueEditorFactory DataValueEditorFactory { get; } + /// [IgnoreDataMember] public EditorType Type { get; } @@ -186,7 +164,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (Attribute == null) throw new InvalidOperationException($"The editor is not attributed with {nameof(DataEditorAttribute)}"); - return new DataValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, JsonSerializer, Attribute); + return DataValueEditorFactory.Create(Attribute); } /// diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs index 2415f48907..07607be2b0 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Runtime.Serialization; using System.Xml.Linq; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; using Umbraco.Cms.Core.PropertyEditors.Validators; @@ -25,15 +27,11 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly ILocalizedTextService _localizedTextService; private readonly IShortStringHelper _shortStringHelper; private readonly IJsonSerializer _jsonSerializer; - protected IDataTypeService DataTypeService { get; } - protected ILocalizationService LocalizationService { get; } /// /// Initializes a new instance of the class. /// public DataValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer) // for tests, and manifest @@ -43,19 +41,16 @@ namespace Umbraco.Cms.Core.PropertyEditors _jsonSerializer = jsonSerializer; ValueType = ValueTypes.String; Validators = new List(); - DataTypeService = dataTypeService; - LocalizationService = localizationService; } /// /// Initializes a new instance of the class. /// public DataValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, + IIOHelper ioHelper, DataEditorAttribute attribute) { if (attribute == null) throw new ArgumentNullException(nameof(attribute)); @@ -67,12 +62,14 @@ namespace Umbraco.Cms.Core.PropertyEditors if (string.IsNullOrWhiteSpace(view)) throw new ArgumentException("The attribute does not specify a view.", nameof(attribute)); + if (view.StartsWith("~/")) + { + view = ioHelper.ResolveRelativeOrVirtualUrl(view); + } + View = view; ValueType = attribute.ValueType; HideLabel = attribute.HideLabel; - - DataTypeService = dataTypeService; - LocalizationService = localizationService; } /// @@ -302,8 +299,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// Converts a property to Xml fragments. /// - public IEnumerable ConvertDbToXml(IProperty property, IDataTypeService dataTypeService, - ILocalizationService localizationService, bool published) + public IEnumerable ConvertDbToXml(IProperty property, bool published) { published &= property.PropertyType.SupportsPublishing; @@ -321,7 +317,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (pvalue.Segment != null) xElement.Add(new XAttribute("segment", pvalue.Segment)); - var xValue = ConvertDbToXml(property.PropertyType, value, dataTypeService); + var xValue = ConvertDbToXml(property.PropertyType, value); xElement.Add(xValue); yield return xElement; @@ -337,12 +333,12 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Returns an XText or XCData instance which must be wrapped in a element. /// If the value is empty we will not return as CDATA since that will just take up more space in the file. /// - public XNode ConvertDbToXml(IPropertyType propertyType, object value, IDataTypeService dataTypeService) + public XNode ConvertDbToXml(IPropertyType propertyType, object value) { //check for null or empty value, we don't want to return CDATA if that is the case if (value == null || value.ToString().IsNullOrWhiteSpace()) { - return new XText(ConvertDbToString(propertyType, value, dataTypeService)); + return new XText(ConvertDbToString(propertyType, value)); } switch (ValueTypes.ToStorageType(ValueType)) @@ -350,11 +346,11 @@ namespace Umbraco.Cms.Core.PropertyEditors case ValueStorageType.Date: case ValueStorageType.Integer: case ValueStorageType.Decimal: - return new XText(ConvertDbToString(propertyType, value, dataTypeService)); + return new XText(ConvertDbToString(propertyType, value)); case ValueStorageType.Nvarchar: case ValueStorageType.Ntext: //put text in cdata - return new XCData(ConvertDbToString(propertyType, value, dataTypeService)); + return new XCData(ConvertDbToString(propertyType, value)); default: throw new ArgumentOutOfRangeException(); } @@ -363,7 +359,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// Converts a property value to a string. /// - public virtual string ConvertDbToString(IPropertyType propertyType, object value, IDataTypeService dataTypeService) + public virtual string ConvertDbToString(IPropertyType propertyType, object value) { if (value == null) return string.Empty; diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditorFactory.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditorFactory.cs new file mode 100644 index 0000000000..300bdde672 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditorFactory.cs @@ -0,0 +1,18 @@ +using System; +using Umbraco.Cms.Core.Models; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + public class DataValueEditorFactory : IDataValueEditorFactory + { + private readonly IServiceProvider _serviceProvider; + + public DataValueEditorFactory(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + public TDataValueEditor Create(params object[] args) + where TDataValueEditor: class, IDataValueEditor + => _serviceProvider.CreateInstance(args); + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/DateValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DateValueEditor.cs index 2cb9c0f887..244fa1354d 100644 --- a/src/Umbraco.Core/PropertyEditors/DateValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DateValueEditor.cs @@ -1,4 +1,6 @@ using System; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors.Validators; using Umbraco.Cms.Core.Serialization; @@ -15,13 +17,12 @@ namespace Umbraco.Cms.Core.PropertyEditors internal class DateValueEditor : DataValueEditor { public DateValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, + IIOHelper ioHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { Validators.Add(new DateTimeValidator()); } diff --git a/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs index 01723ecea4..c940560c90 100644 --- a/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DecimalPropertyEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors.Validators; using Umbraco.Cms.Core.Serialization; @@ -22,13 +23,8 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public DecimalPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } /// diff --git a/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs b/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs new file mode 100644 index 0000000000..d653a831cb --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + [DataContract] + public class FileExtensionConfigItem : IFileExtensionConfigItem + { + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "value")] + public string Value { get; set; } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/FileUploadConfiguration.cs b/src/Umbraco.Core/PropertyEditors/FileUploadConfiguration.cs new file mode 100644 index 0000000000..2953e2a1ed --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/FileUploadConfiguration.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Represents the configuration for the file upload address value editor. + /// + public class FileUploadConfiguration : IFileExtensionsConfig + { + [ConfigurationField("fileExtensions", "Accepted file extensions", "multivalues")] + public List FileExtensions { get; set; } = new List(); + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditorFactory.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditorFactory.cs new file mode 100644 index 0000000000..663c7db6d6 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditorFactory.cs @@ -0,0 +1,11 @@ +using System; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + public interface IDataValueEditorFactory + { + TDataValueEditor Create(params object[] args) + where TDataValueEditor : class, IDataValueEditor; + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IFileExtensionConfig.cs b/src/Umbraco.Core/PropertyEditors/IFileExtensionConfig.cs new file mode 100644 index 0000000000..6e9e9221f6 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IFileExtensionConfig.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Marker interface for any editor configuration that supports defining file extensions + /// + public interface IFileExtensionsConfig + { + List FileExtensions { get; set; } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IFileExtensionConfigItem.cs b/src/Umbraco.Core/PropertyEditors/IFileExtensionConfigItem.cs new file mode 100644 index 0000000000..6394007fad --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IFileExtensionConfigItem.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.PropertyEditors +{ + public interface IFileExtensionConfigItem + { + int Id { get; set; } + + string Value { get; set; } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs index 639cd4293a..f243158db3 100644 --- a/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IntegerPropertyEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors.Validators; using Umbraco.Cms.Core.Serialization; @@ -19,13 +20,8 @@ namespace Umbraco.Cms.Core.PropertyEditors public class IntegerPropertyEditor : DataEditor { public IntegerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IShortStringHelper shortStringHelper, - ILocalizedTextService localizedTextService, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } /// diff --git a/src/Umbraco.Core/PropertyEditors/MediaPicker3Configuration.cs b/src/Umbraco.Core/PropertyEditors/MediaPicker3Configuration.cs new file mode 100644 index 0000000000..7fa2753be9 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/MediaPicker3Configuration.cs @@ -0,0 +1,61 @@ +using System.Runtime.Serialization; + + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Represents the configuration for the media picker value editor. + /// + public class MediaPicker3Configuration : IIgnoreUserStartNodesConfig + { + [ConfigurationField("filter", "Accepted types", "treesourcetypepicker", + Description = "Limit to specific types")] + public string Filter { get; set; } + + [ConfigurationField("multiple", "Pick multiple items", "boolean", Description = "Outputs a IEnumerable")] + public bool Multiple { get; set; } + + [ConfigurationField("validationLimit", "Amount", "numberrange", Description = "Set a required range of medias")] + public NumberRange ValidationLimit { get; set; } = new NumberRange(); + + [DataContract] + public class NumberRange + { + [DataMember(Name = "min")] + public int? Min { get; set; } + + [DataMember(Name = "max")] + public int? Max { get; set; } + } + + [ConfigurationField("startNodeId", "Start node", "mediapicker")] + public Udi StartNodeId { get; set; } + + [ConfigurationField(Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] + public bool IgnoreUserStartNodes { get; set; } + + [ConfigurationField("enableLocalFocalPoint", "Enable Focal Point", "boolean")] + public bool EnableLocalFocalPoint { get; set; } + + [ConfigurationField("crops", "Image Crops", "views/propertyeditors/MediaPicker3/prevalue/mediapicker3.crops.html", Description = "Local crops, stored on document")] + public CropConfiguration[] Crops { get; set; } + + [DataContract] + public class CropConfiguration + { + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "label")] + public string Label { get; set; } + + [DataMember(Name = "width")] + public int Width { get; set; } + + [DataMember(Name = "height")] + public int Height { get; set; } + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs index 06faedea0d..967347e83d 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs @@ -5,6 +5,9 @@ /// public class MediaPickerConfiguration : IIgnoreUserStartNodesConfig { + [ConfigurationField("notice", "You can NOT change the property editor", "obsoletemediapickernotice")] + public bool Notice { get; set; } + [ConfigurationField("multiPicker", "Pick multiple items", "boolean")] public bool Multiple { get; set; } diff --git a/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs index 6fb2e5467d..cccf0fe2b7 100644 --- a/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/MemberGroupPickerPropertyEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -15,13 +16,8 @@ namespace Umbraco.Cms.Core.PropertyEditors public class MemberGroupPickerPropertyEditor : DataEditor { public MemberGroupPickerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } } } diff --git a/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs index a2cf440b58..d348d6f22e 100644 --- a/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/MemberPickerPropertyEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -15,13 +16,8 @@ namespace Umbraco.Cms.Core.PropertyEditors public class MemberPickerPropertyEditor : DataEditor { public MemberPickerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } protected override IConfigurationEditor CreateConfigurationEditor() => new MemberPickerConfiguration(); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs index dd5e0f7480..c7d8067fff 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/ContentTypeParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -19,13 +20,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors /// Initializes a new instance of the class. /// public ContentTypeParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiple", false); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs index 10019cf086..048ad40ac0 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentPickerParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -19,13 +20,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors /// Initializes a new instance of the class. /// public MultipleContentPickerParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiPicker", "1"); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs index d251a66f05..01bae2ada2 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleContentTypeParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -13,13 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors public class MultipleContentTypeParameterEditor : DataEditor { public MultipleContentTypeParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiple", true); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs index b593994ac5..d8f74b1b28 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultipleMediaPickerParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -20,13 +21,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors /// Initializes a new instance of the class. /// public MultipleMediaPickerParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { DefaultConfiguration.Add("multiPicker", "1"); } diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs index 2a4369cdb8..d39f792971 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyGroupParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -13,13 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors public class MultiplePropertyGroupParameterEditor : DataEditor { public MultiplePropertyGroupParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiple", true); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs index 5222463964..64e310551b 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/MultiplePropertyTypeParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -13,13 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors public class MultiplePropertyTypeParameterEditor : DataEditor { public MultiplePropertyTypeParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiple", "1"); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs index 7e53fa79a5..6441e8cb24 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyGroupParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -13,13 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors public class PropertyGroupParameterEditor : DataEditor { public PropertyGroupParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiple", "0"); diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs index 8da3f6c610..9e253d4e41 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditors/PropertyTypeParameterEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -13,13 +14,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors public class PropertyTypeParameterEditor : DataEditor { public PropertyTypeParameterEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { // configure DefaultConfiguration.Add("multiple", "0"); diff --git a/src/Umbraco.Core/PropertyEditors/TextOnlyValueEditor.cs b/src/Umbraco.Core/PropertyEditors/TextOnlyValueEditor.cs index 11c1fe93a5..d067c33c35 100644 --- a/src/Umbraco.Core/PropertyEditors/TextOnlyValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/TextOnlyValueEditor.cs @@ -1,4 +1,6 @@ using System; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; @@ -13,13 +15,12 @@ namespace Umbraco.Cms.Core.PropertyEditors public class TextOnlyValueEditor : DataValueEditor { public TextOnlyValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, DataEditorAttribute attribute, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + IJsonSerializer jsonSerializer, + IIOHelper ioHelper) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { } /// diff --git a/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs index 963e44dab6..17dd8060f5 100644 --- a/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/UserPickerPropertyEditor.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -15,13 +16,8 @@ namespace Umbraco.Cms.Core.PropertyEditors public class UserPickerPropertyEditor : DataEditor { public UserPickerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } protected override IConfigurationEditor CreateConfigurationEditor() => new UserPickerConfiguration(); diff --git a/src/Umbraco.Core/PropertyEditors/VoidEditor.cs b/src/Umbraco.Core/PropertyEditors/VoidEditor.cs index 8f0f4293cd..716e722be1 100644 --- a/src/Umbraco.Core/PropertyEditors/VoidEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/VoidEditor.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -26,13 +27,8 @@ namespace Umbraco.Cms.Core.PropertyEditors /// it is appended to the alias. Eg if the suffix is "Foo" the alias is "Umbraco.Void.Foo". public VoidEditor( string aliasSuffix, - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { Alias = "Umbraco.Void"; if (string.IsNullOrWhiteSpace(aliasSuffix)) return; @@ -44,8 +40,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// A logger factory. /// The alias of the editor is "Umbraco.Void". - public VoidEditor(ILoggerFactory loggerFactory, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer) - : this(null, loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + public VoidEditor( + IDataValueEditorFactory dataValueEditorFactory) + : this(null, dataValueEditorFactory) { } } } diff --git a/src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs b/src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs index 50ec3c4c16..145206daf1 100644 --- a/src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs +++ b/src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs @@ -10,8 +10,9 @@ namespace Umbraco.Cms.Core.PublishedCache public class DefaultCultureAccessor : IDefaultCultureAccessor { private readonly ILocalizationService _localizationService; + private readonly IRuntimeState _runtimeState; private readonly IOptions _options; - private readonly RuntimeLevel _runtimeLevel; + /// /// Initializes a new instance of the class. @@ -19,12 +20,13 @@ namespace Umbraco.Cms.Core.PublishedCache public DefaultCultureAccessor(ILocalizationService localizationService, IRuntimeState runtimeState, IOptions options) { _localizationService = localizationService; + _runtimeState = runtimeState; _options = options; - _runtimeLevel = runtimeState.Level; + } /// - public string DefaultCulture => _runtimeLevel == RuntimeLevel.Run + public string DefaultCulture => _runtimeState.Level == RuntimeLevel.Run ? _localizationService.GetDefaultLanguageIsoCode() ?? "" // fast : _options.Value.DefaultUILanguage; // default for install and upgrade, when the service is n/a } diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index f8697e640a..502868fb69 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; diff --git a/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs b/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs index 0b46dc0afd..a416452966 100644 --- a/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs +++ b/src/Umbraco.Core/Runtime/AppPluginsManifestWatcherNotificationHandler.cs @@ -5,13 +5,14 @@ using System.Threading.Tasks; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Runtime { /// /// Starts monitoring AppPlugins directory during debug runs, to restart site when a plugin manifest changes. /// - public sealed class AppPluginsManifestWatcherNotificationHandler : INotificationAsyncHandler + public sealed class AppPluginsManifestWatcherNotificationHandler : INotificationAsyncHandler { private readonly ManifestWatcher _manifestWatcher; private readonly IHostingEnvironment _hostingEnvironment; @@ -22,7 +23,7 @@ namespace Umbraco.Cms.Core.Runtime _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); } - public Task HandleAsync(UmbracoApplicationStarting notification, CancellationToken cancellationToken) + public Task HandleAsync(UmbracoApplicationStartingNotification notification, CancellationToken cancellationToken) { if (!_hostingEnvironment.IsDebugMode) { diff --git a/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs b/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs index c1ef8f98b4..a9564712c3 100644 --- a/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs +++ b/src/Umbraco.Core/Runtime/EssentialDirectoryCreator.cs @@ -3,10 +3,11 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Core.Runtime { - public class EssentialDirectoryCreator : INotificationHandler + public class EssentialDirectoryCreator : INotificationHandler { private readonly IIOHelper _ioHelper; private readonly IHostingEnvironment _hostingEnvironment; @@ -19,7 +20,7 @@ namespace Umbraco.Cms.Core.Runtime _globalSettings = globalSettings.Value; } - public void Handle(UmbracoApplicationStarting notification) + public void Handle(UmbracoApplicationStartingNotification notification) { // ensure we have some essential directories // every other component can then initialize safely diff --git a/src/Umbraco.Core/Runtime/MainDom.cs b/src/Umbraco.Core/Runtime/MainDom.cs index adab97ed6d..ec4e56df1b 100644 --- a/src/Umbraco.Core/Runtime/MainDom.cs +++ b/src/Umbraco.Core/Runtime/MainDom.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Core.Runtime private bool _isInitialized; // indicates whether... - private bool _isMainDom; // we are the main domain + private bool? _isMainDom; // we are the main domain private volatile bool _signaled; // we have been signaled // actions to run before releasing the main domain @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Core.Runtime { hostingEnvironment.RegisterObject(this); return Acquire(); - }); + }).Value; } /// @@ -85,7 +85,11 @@ namespace Umbraco.Cms.Core.Runtime return false; } - if (_isMainDom == false) + if (_isMainDom.HasValue == false) + { + throw new InvalidOperationException("Register called when MainDom has not been acquired"); + } + else if (_isMainDom == false) { _logger.LogWarning("Register called when MainDom has not been acquired"); return false; @@ -215,7 +219,17 @@ namespace Umbraco.Cms.Core.Runtime /// /// Acquire must be called first else this will always return false /// - public bool IsMainDom => _isMainDom; + public bool IsMainDom + { + get + { + if (!_isMainDom.HasValue) + { + throw new InvalidOperationException("MainDom has not been acquired yet"); + } + return _isMainDom.Value; + } + } // IRegisteredObject void IRegisteredObject.Stop(bool immediate) diff --git a/src/Umbraco.Core/Scoping/CallContext.cs b/src/Umbraco.Core/Scoping/CallContext.cs deleted file mode 100644 index b77414ddd6..0000000000 --- a/src/Umbraco.Core/Scoping/CallContext.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading; - -namespace Umbraco.Cms.Core.Scoping -{ - /// - /// Represents ambient data that is local to a given asynchronous control flow, such as an asynchronous method. - /// - /// - /// This is just a simple wrapper around - /// - public static class CallContext - { - // TODO: Kill this. Wherever we need AsyncLocal, we should just use it there. - - private static readonly ConcurrentDictionary> s_state = new ConcurrentDictionary>(); - - /// - /// Stores a given object and associates it with the specified name. - /// - /// The name with which to associate the new item in the call context. - /// The object to store in the call context. - public static void SetData(string name, T data) => s_state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; - - //Replace the SetData with the following when you need to debug AsyncLocal. The args.ThreadContextChanged can be usefull - //public static void SetData(string name, T data) => _state.GetOrAdd(name, _ => new AsyncLocal(OnValueChanged)).Value = data; - // public static void OnValueChanged(AsyncLocalValueChangedArgs args) - // { - // var typeName = typeof(T).ToString(); - // Console.WriteLine($"OnValueChanged!, Type: {typeName} Prev: #{args.PreviousValue} Current: #{args.CurrentValue}"); - // } - - /// - /// Retrieves an object with the specified name from the . - /// - /// The type of the data being retrieved. Must match the type used when the was set via . - /// The name of the item in the call context. - /// The object in the call context associated with the specified name, or a default value for if none is found. - public static T GetData(string name) => s_state.TryGetValue(name, out AsyncLocal data) ? data.Value : default; - - // NOTE: If you have used the old CallContext in the past you might be thinking you need to clean this up but that is not the case. - // With CallContext you had to call FreeNamedDataSlot to prevent leaks but with AsyncLocal this is not the case, there is no way to clean this up. - // The above dictionary is sort of a trick because sure, there is always going to be a string key that will exist in the collection but the values - // themselves are managed per ExecutionContext so they don't build up. - // There's an SO article relating to this here https://stackoverflow.com/questions/36511243/safety-of-asynclocal-in-asp-net-core - - } -} diff --git a/src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs b/src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs deleted file mode 100644 index 84116584a2..0000000000 --- a/src/Umbraco.Core/Services/CacheInstructionServiceProcessInstructionsResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Umbraco.Cms.Core.Services -{ - /// - /// Defines a result object for the operation. - /// - public class CacheInstructionServiceProcessInstructionsResult - { - private CacheInstructionServiceProcessInstructionsResult() - { - } - - public int NumberOfInstructionsProcessed { get; private set; } - - public int LastId { get; private set; } - - public bool InstructionsWerePruned { get; private set; } - - public static CacheInstructionServiceProcessInstructionsResult AsCompleted(int numberOfInstructionsProcessed, int lastId) => - new CacheInstructionServiceProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId }; - - public static CacheInstructionServiceProcessInstructionsResult AsCompletedAndPruned(int numberOfInstructionsProcessed, int lastId) => - new CacheInstructionServiceProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId, InstructionsWerePruned = true }; - }; -} diff --git a/src/Umbraco.Core/Services/ICacheInstructionService.cs b/src/Umbraco.Core/Services/ICacheInstructionService.cs index faf05f2237..c884b8bed8 100644 --- a/src/Umbraco.Core/Services/ICacheInstructionService.cs +++ b/src/Umbraco.Core/Services/ICacheInstructionService.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Threading; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Services @@ -40,6 +42,12 @@ namespace Umbraco.Cms.Core.Services /// Local identity of the executing AppDomain. /// Date of last prune operation. /// Id of the latest processed instruction - CacheInstructionServiceProcessInstructionsResult ProcessInstructions(bool released, string localIdentity, DateTime lastPruned, int lastId); + ProcessInstructionsResult ProcessInstructions( + CacheRefresherCollection cacheRefreshers, + ServerRole serverRole, + CancellationToken cancellationToken, + string localIdentity, + DateTime lastPruned, + int lastId); } } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index bcf2a0caee..5df5602bc6 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using Umbraco.Cms.Core.Models; @@ -17,15 +17,54 @@ namespace Umbraco.Cms.Core.Services void DeletePartialViewMacroFolder(string folderPath); IPartialView GetPartialView(string path); IPartialView GetPartialViewMacro(string path); - IEnumerable GetPartialViewMacros(params string[] names); Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId); Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId); bool DeletePartialView(string path, int userId = Constants.Security.SuperUserId); bool DeletePartialViewMacro(string path, int userId = Constants.Security.SuperUserId); Attempt SavePartialView(IPartialView partialView, int userId = Constants.Security.SuperUserId); Attempt SavePartialViewMacro(IPartialView partialView, int userId = Constants.Security.SuperUserId); - bool ValidatePartialView(IPartialView partialView); - bool ValidatePartialViewMacro(IPartialView partialView); + + /// + /// Gets the content of a partial view as a stream. + /// + /// The filesystem path to the partial view. + /// The content of the partial view. + Stream GetPartialViewFileContentStream(string filepath); + + /// + /// Sets the content of a partial view. + /// + /// The filesystem path to the partial view. + /// The content of the partial view. + void SetPartialViewFileContent(string filepath, Stream content); + + /// + /// Gets the size of a partial view. + /// + /// The filesystem path to the partial view. + /// The size of the partial view. + long GetPartialViewFileSize(string filepath); + + /// + /// Gets the content of a macro partial view as a stream. + /// + /// The filesystem path to the macro partial view. + /// The content of the macro partial view. + Stream GetPartialViewMacroFileContentStream(string filepath); + + /// + /// Sets the content of a macro partial view. + /// + /// The filesystem path to the macro partial view. + /// The content of the macro partial view. + void SetPartialViewMacroFileContent(string filepath, Stream content); + + /// + /// Gets the size of a macro partial view. + /// + /// The filesystem path to the macro partial view. + /// The size of the macro partial view. + long GetPartialViewMacroFileSize(string filepath); /// /// Gets a list of all objects @@ -55,17 +94,38 @@ namespace Umbraco.Cms.Core.Services void DeleteStylesheet(string path, int userId = Constants.Security.SuperUserId); /// - /// Validates a + /// Creates a folder for style sheets /// - /// to validate - /// True if Stylesheet is valid, otherwise false - bool ValidateStylesheet(IStylesheet stylesheet); + /// + /// + void CreateStyleSheetFolder(string folderPath); /// - /// Gets a list of all objects + /// Deletes a folder for style sheets /// - /// An enumerable list of objects - IEnumerable GetScripts(params string[] names); + /// + void DeleteStyleSheetFolder(string folderPath); + + /// + /// Gets the content of a stylesheet as a stream. + /// + /// The filesystem path to the stylesheet. + /// The content of the stylesheet. + Stream GetStylesheetFileContentStream(string filepath); + + /// + /// Sets the content of a stylesheet. + /// + /// The filesystem path to the stylesheet. + /// The content of the stylesheet. + void SetStylesheetFileContent(string filepath, Stream content); + + /// + /// Gets the size of a stylesheet. + /// + /// The filesystem path to the stylesheet. + /// The size of the stylesheet. + long GetStylesheetFileSize(string filepath); /// /// Gets a object by its name @@ -88,13 +148,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user deleting the script void DeleteScript(string path, int userId = Constants.Security.SuperUserId); - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - bool ValidateScript(IScript script); - /// /// Creates a folder for scripts /// @@ -109,17 +162,25 @@ namespace Umbraco.Cms.Core.Services void DeleteScriptFolder(string folderPath); /// - /// Creates a folder for style sheets + /// Gets the content of a script file as a stream. /// - /// - /// - void CreateStyleSheetFolder(string folderPath); + /// The filesystem path to the script. + /// The content of the script file. + Stream GetScriptFileContentStream(string filepath); /// - /// Deletes a folder for style sheets + /// Sets the content of a script file. /// - /// - void DeleteStyleSheetFolder(string folderPath); + /// The filesystem path to the script. + /// The content of the script file. + void SetScriptFileContent(string filepath, Stream content); + + /// + /// Gets the size of a script file. + /// + /// The filesystem path to the script file. + /// The size of the script file. + long GetScriptFileSize(string filepath); /// /// Gets a list of all objects @@ -154,13 +215,6 @@ namespace Umbraco.Cms.Core.Services /// The object matching the identifier, or null. ITemplate GetTemplate(Guid id); - /// - /// Gets the template descendants - /// - /// - /// - IEnumerable GetTemplateDescendants(string alias); - /// /// Gets the template descendants /// @@ -168,20 +222,6 @@ namespace Umbraco.Cms.Core.Services /// IEnumerable GetTemplateDescendants(int masterTemplateId); - /// - /// Gets the template children - /// - /// - /// - IEnumerable GetTemplateChildren(string alias); - - /// - /// Gets the template children - /// - /// - /// - IEnumerable GetTemplateChildren(int masterTemplateId); - /// /// Saves a /// @@ -209,13 +249,6 @@ namespace Umbraco.Cms.Core.Services /// Optional id of the user deleting the template void DeleteTemplate(string alias, int userId = Constants.Security.SuperUserId); - /// - /// Validates a - /// - /// to validate - /// True if template is valid, otherwise false - bool ValidateTemplate(ITemplate template); - /// /// Saves a collection of objects /// @@ -244,90 +277,6 @@ namespace Umbraco.Cms.Core.Services /// The size of the template. long GetTemplateFileSize(string filepath); - /// - /// Gets the content of a stylesheet as a stream. - /// - /// The filesystem path to the stylesheet. - /// The content of the stylesheet. - Stream GetStylesheetFileContentStream(string filepath); - - /// - /// Sets the content of a stylesheet. - /// - /// The filesystem path to the stylesheet. - /// The content of the stylesheet. - void SetStylesheetFileContent(string filepath, Stream content); - - /// - /// Gets the size of a stylesheet. - /// - /// The filesystem path to the stylesheet. - /// The size of the stylesheet. - long GetStylesheetFileSize(string filepath); - - /// - /// Gets the content of a script file as a stream. - /// - /// The filesystem path to the script. - /// The content of the script file. - Stream GetScriptFileContentStream(string filepath); - - /// - /// Sets the content of a script file. - /// - /// The filesystem path to the script. - /// The content of the script file. - void SetScriptFileContent(string filepath, Stream content); - - /// - /// Gets the size of a script file. - /// - /// The filesystem path to the script file. - /// The size of the script file. - long GetScriptFileSize(string filepath); - - /// - /// Gets the content of a macro partial view as a stream. - /// - /// The filesystem path to the macro partial view. - /// The content of the macro partial view. - Stream GetPartialViewMacroFileContentStream(string filepath); - - /// - /// Sets the content of a macro partial view. - /// - /// The filesystem path to the macro partial view. - /// The content of the macro partial view. - void SetPartialViewMacroFileContent(string filepath, Stream content); - - /// - /// Gets the size of a macro partial view. - /// - /// The filesystem path to the macro partial view. - /// The size of the macro partial view. - long GetPartialViewMacroFileSize(string filepath); - - /// - /// Gets the content of a partial view as a stream. - /// - /// The filesystem path to the partial view. - /// The content of the partial view. - Stream GetPartialViewFileContentStream(string filepath); - - /// - /// Sets the content of a partial view. - /// - /// The filesystem path to the partial view. - /// The content of the partial view. - void SetPartialViewFileContent(string filepath, Stream content); - - /// - /// Gets the size of a partial view. - /// - /// The filesystem path to the partial view. - /// The size of the partial view. - long GetPartialViewFileSize(string filepath); - /// /// Gets the content of a macro partial view snippet as a string /// diff --git a/src/Umbraco.Core/Services/IPropertyValidationService.cs b/src/Umbraco.Core/Services/IPropertyValidationService.cs index 0fc2a0b02d..0e3cb2bae0 100644 --- a/src/Umbraco.Core/Services/IPropertyValidationService.cs +++ b/src/Umbraco.Core/Services/IPropertyValidationService.cs @@ -17,6 +17,9 @@ namespace Umbraco.Cms.Core.Services /// bool IsPropertyValid(IProperty property, string culture = "*", string segment = "*"); + /// + /// Validates a property value. + /// IEnumerable ValidatePropertyValue( IDataEditor editor, IDataType dataType, @@ -25,5 +28,12 @@ namespace Umbraco.Cms.Core.Services string validationRegExp, string isRequiredMessage, string validationRegExpMessage); + + /// + /// Validates a property value. + /// + IEnumerable ValidatePropertyValue( + IPropertyType propertyType, + object postedValue); } } diff --git a/src/Umbraco.Core/Services/IRuntime.cs b/src/Umbraco.Core/Services/IRuntime.cs index b10c9fe90f..caa430ce1f 100644 --- a/src/Umbraco.Core/Services/IRuntime.cs +++ b/src/Umbraco.Core/Services/IRuntime.cs @@ -1,3 +1,5 @@ +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Hosting; namespace Umbraco.Cms.Core.Services @@ -11,5 +13,10 @@ namespace Umbraco.Cms.Core.Services /// Gets the runtime state. /// IRuntimeState State { get; } + + /// + /// Stops and Starts the runtime using the original cancellation token. + /// + Task RestartAsync(); } } diff --git a/src/Umbraco.Core/Services/ProcessInstructionsResult.cs b/src/Umbraco.Core/Services/ProcessInstructionsResult.cs new file mode 100644 index 0000000000..9a368dab7e --- /dev/null +++ b/src/Umbraco.Core/Services/ProcessInstructionsResult.cs @@ -0,0 +1,26 @@ +using System; + +namespace Umbraco.Cms.Core.Services +{ + /// + /// Defines a result object for the operation. + /// + public class ProcessInstructionsResult + { + private ProcessInstructionsResult() + { + } + + public int NumberOfInstructionsProcessed { get; private set; } + + public int LastId { get; private set; } + + public bool InstructionsWerePruned { get; private set; } + + public static ProcessInstructionsResult AsCompleted(int numberOfInstructionsProcessed, int lastId) => + new ProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId }; + + public static ProcessInstructionsResult AsCompletedAndPruned(int numberOfInstructionsProcessed, int lastId) => + new ProcessInstructionsResult { NumberOfInstructionsProcessed = numberOfInstructionsProcessed, LastId = lastId, InstructionsWerePruned = true }; + }; +} diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs b/src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs deleted file mode 100644 index 2107bbde20..0000000000 --- a/src/Umbraco.Core/Sync/DatabaseServerMessengerCallbacks.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Cms.Core.Sync -{ - /// - /// Holds a list of callbacks associated with implementations of . - /// - public class DatabaseServerMessengerCallbacks - { - /// - /// A list of callbacks that will be invoked if the lastsynced.txt file does not exist. - /// - /// - /// These callbacks will typically be for e.g. rebuilding the xml cache file, or examine indexes, based on - /// the data in the database to get this particular server node up to date. - /// - public IEnumerable InitializingCallbacks { get; set; } - } -} diff --git a/src/Umbraco.Core/Sync/IServerAddress.cs b/src/Umbraco.Core/Sync/IServerAddress.cs index c9333f33b8..a177454886 100644 --- a/src/Umbraco.Core/Sync/IServerAddress.cs +++ b/src/Umbraco.Core/Sync/IServerAddress.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Sync +namespace Umbraco.Cms.Core.Sync { /// /// Provides the address of a server. diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs new file mode 100644 index 0000000000..0c616a4e68 --- /dev/null +++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Cms.Core.Sync +{ + /// + /// Retrieve the for the application during startup + /// + public interface ISyncBootStateAccessor + { + /// + /// Get the + /// + /// + SyncBootState GetSyncBootState(); + } +} diff --git a/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs new file mode 100644 index 0000000000..0dcfa471db --- /dev/null +++ b/src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Sync +{ + /// + /// Boot state implementation for when umbraco is not in the run state + /// + public sealed class NonRuntimeLevelBootStateAccessor : ISyncBootStateAccessor + { + public SyncBootState GetSyncBootState() => SyncBootState.Unknown; + } +} diff --git a/src/Umbraco.Core/Sync/SyncBootState.cs b/src/Umbraco.Core/Sync/SyncBootState.cs new file mode 100644 index 0000000000..670930de31 --- /dev/null +++ b/src/Umbraco.Core/Sync/SyncBootState.cs @@ -0,0 +1,20 @@ +namespace Umbraco.Cms.Core.Sync +{ + public enum SyncBootState + { + /// + /// Unknown state. Treat as WarmBoot + /// + Unknown = 0, + + /// + /// Cold boot. No Sync state + /// + ColdBoot = 1, + + /// + /// Warm boot. Sync state present + /// + WarmBoot = 2 + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 59bf33d110..89b878ac6f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -16,15 +16,22 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + @@ -51,4 +58,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs b/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs new file mode 100644 index 0000000000..8648a59d32 --- /dev/null +++ b/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Composing; + +namespace Umbraco.Cms.Core.WebAssets +{ + public class CustomBackOfficeAssetsCollection : BuilderCollectionBase + { + public CustomBackOfficeAssetsCollection(IEnumerable items) + : base(items) + { } + } +} diff --git a/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollectionBuilder.cs b/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollectionBuilder.cs new file mode 100644 index 0000000000..df84bf013d --- /dev/null +++ b/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollectionBuilder.cs @@ -0,0 +1,9 @@ +using Umbraco.Cms.Core.Composing; + +namespace Umbraco.Cms.Core.WebAssets +{ + public class CustomBackOfficeAssetsCollectionBuilder : OrderedCollectionBuilderBase + { + protected override CustomBackOfficeAssetsCollectionBuilder This => this; + } +} diff --git a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index d05bb8f07f..0a99c4d6ef 100644 --- a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -7,6 +7,8 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using Examine; +using Examine.Search; +using Lucene.Net.QueryParsers.Classic; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Mapping; @@ -103,17 +105,16 @@ namespace Umbraco.Cms.Infrastructure.Examine if (!_examineManager.TryGetIndex(indexName, out var index)) throw new InvalidOperationException("No index found by name " + indexName); - var internalSearcher = index.GetSearcher(); - if (!BuildQuery(sb, query, searchFrom, fields, type)) { totalFound = 0; return Enumerable.Empty(); } - var result = internalSearcher.CreateQuery().NativeQuery(sb.ToString()) - //only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested - .Execute(Convert.ToInt32(pageSize * (pageIndex + 1))); + var result = index.Searcher + .CreateQuery() + .NativeQuery(sb.ToString()) + .Execute(QueryOptions.SkipTake(Convert.ToInt32(pageSize * pageIndex), pageSize)); totalFound = result.TotalItemCount; @@ -143,7 +144,7 @@ namespace Umbraco.Cms.Infrastructure.Examine //strip quotes, escape string, the replace again query = query.Trim(Constants.CharArrays.DoubleQuoteSingleQuote); - query = Lucene.Net.QueryParsers.QueryParser.Escape(query); + query = QueryParser.Escape(query); //nothing to search if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace()) @@ -186,7 +187,7 @@ namespace Umbraco.Cms.Infrastructure.Examine //update the query with the query term if (trimmed.IsNullOrWhiteSpace() == false) { - query = Lucene.Net.QueryParsers.QueryParser.Escape(query); + query = QueryParser.Escape(query); var querywords = query.Split(Constants.CharArrays.Space, StringSplitOptions.RemoveEmptyEntries); @@ -355,6 +356,8 @@ namespace Umbraco.Cms.Infrastructure.Examine sb.Append("\\,*"); } + // TODO: When/Where is this used? + /// /// Returns a collection of entities for media based on search results /// @@ -389,6 +392,8 @@ namespace Umbraco.Cms.Infrastructure.Examine } } + // TODO: When/Where is this used? + /// /// Returns a collection of entities for media based on search results /// @@ -397,6 +402,8 @@ namespace Umbraco.Cms.Infrastructure.Examine private IEnumerable MediaFromSearchResults(IEnumerable results) => _umbracoMapper.Map>(results); + // TODO: When/Where is this used? + /// /// Returns a collection of entities for content based on search results /// diff --git a/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs new file mode 100644 index 0000000000..dfed005470 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/ConfigurationEnabledDirectoryFactory.cs @@ -0,0 +1,65 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.IO; +using Examine; +using Examine.Lucene.Directories; +using Examine.Lucene.Providers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// An Examine directory factory implementation based on configured values + /// + public class ConfigurationEnabledDirectoryFactory : DirectoryFactoryBase + { + private readonly IServiceProvider _services; + private readonly IApplicationRoot _applicationRoot; + private readonly IndexCreatorSettings _settings; + private IDirectoryFactory _directoryFactory; + + public ConfigurationEnabledDirectoryFactory( + IServiceProvider services, + IOptions settings, + IApplicationRoot applicationRoot) + { + _services = services; + _applicationRoot = applicationRoot; + _settings = settings.Value; + } + + protected override Lucene.Net.Store.Directory CreateDirectory(LuceneIndex luceneIndex, bool forceUnlock) + { + _directoryFactory = CreateFactory(); + return _directoryFactory.CreateDirectory(luceneIndex, forceUnlock); + } + + /// + /// Creates a directory factory based on the configured value and ensures that + /// + private IDirectoryFactory CreateFactory() + { + DirectoryInfo dirInfo = _applicationRoot.ApplicationRoot; + + if (!dirInfo.Exists) + { + Directory.CreateDirectory(dirInfo.FullName); + } + + switch (_settings.LuceneDirectoryFactory) + { + case LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory: + return _services.GetRequiredService(); + case LuceneDirectoryFactory.TempFileSystemDirectoryFactory: + return _services.GetRequiredService(); + case LuceneDirectoryFactory.Default: + default: + return _services.GetRequiredService(); + } + } + } +} diff --git a/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs b/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs new file mode 100644 index 0000000000..7d434cbf23 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs @@ -0,0 +1,65 @@ +using System; +using Examine; +using Examine.Lucene; +using Examine.Lucene.Analyzers; +using Lucene.Net.Analysis.Standard; +using Lucene.Net.Index; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection +{ + /// + /// Configures the index options to construct the Examine indexes + /// + public sealed class ConfigureIndexOptions : IConfigureNamedOptions + { + private readonly IUmbracoIndexConfig _umbracoIndexConfig; + private readonly IOptions _settings; + + public ConfigureIndexOptions( + IUmbracoIndexConfig umbracoIndexConfig, + IOptions settings) + { + _umbracoIndexConfig = umbracoIndexConfig; + _settings = settings; + } + + public void Configure(string name, LuceneDirectoryIndexOptions options) + { + switch (name) + { + case Constants.UmbracoIndexes.InternalIndexName: + options.Analyzer = new CultureInvariantWhitespaceAnalyzer(); + options.Validator = _umbracoIndexConfig.GetContentValueSetValidator(); + options.FieldDefinitions = new UmbracoFieldDefinitionCollection(); + break; + case Constants.UmbracoIndexes.ExternalIndexName: + options.Analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); + options.Validator = _umbracoIndexConfig.GetPublishedContentValueSetValidator(); + options.FieldDefinitions = new UmbracoFieldDefinitionCollection(); + break; + case Constants.UmbracoIndexes.MembersIndexName: + options.Analyzer = new CultureInvariantWhitespaceAnalyzer(); + options.Validator = _umbracoIndexConfig.GetMemberValueSetValidator(); + options.FieldDefinitions = new UmbracoFieldDefinitionCollection(); + break; + } + + // ensure indexes are unlocked on startup + options.UnlockIndex = true; + + if (_settings.Value.LuceneDirectoryFactory == LuceneDirectoryFactory.SyncedTempFileSystemDirectoryFactory) + { + // if this directory factory is enabled then a snapshot deletion policy is required + options.IndexDeletionPolicy = new SnapshotDeletionPolicy(new KeepOnlyLastCommitDeletionPolicy()); + } + + + } + + public void Configure(LuceneDirectoryIndexOptions options) + => throw new NotImplementedException("This is never called and is just part of the interface"); + } +} diff --git a/src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs new file mode 100644 index 0000000000..8eafde1a38 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/DependencyInjection/UmbracoBuilderExtensions.cs @@ -0,0 +1,40 @@ +using Examine; +using Examine.Lucene.Directories; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Infrastructure.DependencyInjection; + +namespace Umbraco.Cms.Infrastructure.Examine.DependencyInjection +{ + public static class UmbracoBuilderExtensions + { + /// + /// Adds the Examine indexes for Umbraco + /// + /// + /// + public static IUmbracoBuilder AddExamineIndexes(this IUmbracoBuilder umbracoBuilder) + { + IServiceCollection services = umbracoBuilder.Services; + + services.AddSingleton(); + services.AddSingleton(); + + services.AddExamine(); + + // Create the indexes + services + .AddExamineLuceneIndex(Constants.UmbracoIndexes.InternalIndexName) + .AddExamineLuceneIndex(Constants.UmbracoIndexes.ExternalIndexName) + .AddExamineLuceneIndex(Constants.UmbracoIndexes.MembersIndexName) + .ConfigureOptions(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + return umbracoBuilder; + } + } +} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs deleted file mode 100644 index 1243e01578..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneComposer.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - // We want to run after core composers since we are replacing some items - public sealed class ExamineLuceneComposer :IComposer - { - public void Compose(IUmbracoBuilder builder) - { - builder.AddExamineLucene(); - } - } -} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneConfigureIndexes.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneConfigureIndexes.cs deleted file mode 100644 index d64daab514..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneConfigureIndexes.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class ExamineLuceneConfigureIndexes : INotificationHandler - { - private readonly ILoggerFactory _loggerFactory; - private readonly IExamineManager _examineManager; - private readonly IMainDom _mainDom; - - public ExamineLuceneConfigureIndexes(ILoggerFactory loggerFactory, IExamineManager examineManager, IMainDom mainDom) - { - _loggerFactory = loggerFactory; - _examineManager = examineManager; - _mainDom = mainDom; - } - - public void Handle(UmbracoApplicationStarting notification) - { - if (!_mainDom.IsMainDom) return; - - // Ensures all lucene based indexes are unlocked and ready to go - _examineManager.ConfigureIndexes(_mainDom, _loggerFactory.CreateLogger()); - } - - - } -} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs deleted file mode 100644 index ab3476cb70..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneFinalComposer.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - // examine's Lucene final composer composes after all user composers - // and *also* after ICoreComposer (in case IUserComposer is disabled) - [ComposeAfter(typeof(IUserComposer))] - public class ExamineLuceneFinalComposer : IComposer - { - public void Compose(IUmbracoBuilder builder) => builder.AddExamineIndexConfiguration(); - } -} diff --git a/src/Umbraco.Examine.Lucene/ExamineLuceneStarting.cs b/src/Umbraco.Examine.Lucene/ExamineLuceneStarting.cs deleted file mode 100644 index f640ca4968..0000000000 --- a/src/Umbraco.Examine.Lucene/ExamineLuceneStarting.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Examine; -using Examine.LuceneEngine.Directories; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public sealed class ExamineLuceneStarting : INotificationHandler - { - private readonly IndexRebuilder _indexRebuilder; - private readonly IExamineManager _examineManager; - private readonly IMainDom _mainDom; - private readonly ILoggerFactory _loggerFactory; - - public ExamineLuceneStarting(IndexRebuilder indexRebuilder, IExamineManager examineManager, IMainDom mainDom, ILoggerFactory loggerFactory) - { - _indexRebuilder = indexRebuilder; - _examineManager = examineManager; - _mainDom = mainDom; - _loggerFactory = loggerFactory; - } - - public void Handle(UmbracoApplicationStarting notification) - { - //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the AppDomain - //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock - //which simply checks the existence of the lock file - DirectoryFactory.DefaultLockFactory = d => - { - var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); - return simpleFsLockFactory; - }; - - _indexRebuilder.RebuildingIndexes += IndexRebuilder_RebuildingIndexes; - } - - /// - /// Handles event to ensure that all lucene based indexes are properly configured before rebuilding - /// - /// - /// - private void IndexRebuilder_RebuildingIndexes(object sender, IndexRebuildingEventArgs e) => _examineManager.ConfigureIndexes(_mainDom, _loggerFactory.CreateLogger()); - - } -} diff --git a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs index 9307c4cbf4..82b43eaa20 100644 --- a/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs +++ b/src/Umbraco.Examine.Lucene/Extensions/ExamineExtensions.cs @@ -1,19 +1,19 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; using System.Linq; using System.Threading; using Examine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Analysis; +using Examine.Lucene.Providers; +using Lucene.Net.Analysis.Core; using Lucene.Net.Index; -using Lucene.Net.QueryParsers; -using Lucene.Net.Search; +using Lucene.Net.QueryParsers.Classic; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.Examine; -using Version = Lucene.Net.Util.Version; namespace Umbraco.Extensions { @@ -22,40 +22,19 @@ namespace Umbraco.Extensions /// public static class ExamineExtensions { - private static bool _isConfigured = false; - private static object _configuredInit = null; - private static object _isConfiguredLocker = new object(); - - /// - /// Called on startup to configure each index. - /// - /// - /// Configures and unlocks all Lucene based indexes registered with the . - /// - internal static void ConfigureIndexes(this IExamineManager examineManager, IMainDom mainDom, ILogger logger) - { - LazyInitializer.EnsureInitialized( - ref _configuredInit, - ref _isConfigured, - ref _isConfiguredLocker, - () => - { - examineManager.ConfigureLuceneIndexes(logger, !mainDom.IsMainDom); - return null; - }); - } - internal static bool TryParseLuceneQuery(string query) { // TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll // also do this rudimentary check if (!query.Contains(":")) + { return false; + } try { //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse - var parsed = new QueryParser(Version.LUCENE_30, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query); + var parsed = new QueryParser(LuceneInfo.CurrentVersion, UmbracoExamineFieldNames.NodeNameFieldName, new KeywordAnalyzer()).Parse(query); return true; } catch (ParseException) @@ -68,34 +47,6 @@ namespace Umbraco.Extensions } } - /// - /// Forcibly unlocks all lucene based indexes - /// - /// - /// This is not thread safe, use with care - /// - private static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing) - { - foreach (var luceneIndexer in examineManager.Indexes.OfType()) - { - //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending - //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because - //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems. - luceneIndexer.WaitForIndexQueueOnShutdown = false; - - if (disableExamineIndexing) continue; //exit if not enabled, we don't need to unlock them if we're not maindom - - //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that - //the indexes are not operational unless MainDom is true - var dir = luceneIndexer.GetLuceneDirectory(); - if (IndexWriter.IsLocked(dir)) - { - logger.LogDebug("Forcing index {IndexerName} to be unlocked since it was left in a locked state", luceneIndexer.Name); - IndexWriter.Unlock(dir); - } - } - } - /// /// Checks if the index can be read/opened /// @@ -106,7 +57,7 @@ namespace Umbraco.Extensions { try { - using (indexer.GetIndexWriter().GetReader()) + using (indexer.IndexWriter.IndexWriter.GetReader(false)) { ex = null; return true; @@ -119,38 +70,5 @@ namespace Umbraco.Extensions } } - /// - /// Return the number of indexed documents in Lucene - /// - /// - /// - public static int GetIndexDocumentCount(this LuceneIndex indexer) - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.NumDocs(); - } - } - - /// - /// Return the total number of fields in the index - /// - /// - /// - public static int GetIndexFieldCount(this LuceneIndex indexer) - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.GetFieldNames(IndexReader.FieldOption.ALL).Count; - } - } } } diff --git a/src/Umbraco.Examine.Lucene/Extensions/UmbracoBuilderExtensions.cs b/src/Umbraco.Examine.Lucene/Extensions/UmbracoBuilderExtensions.cs deleted file mode 100644 index 1e5c2a6edd..0000000000 --- a/src/Umbraco.Examine.Lucene/Extensions/UmbracoBuilderExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Runtime.InteropServices; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Infrastructure.Examine; - -namespace Umbraco.Extensions -{ - /// - /// Extension methods for for the Examine.Lucene - /// - public static class UmbracoBuilderExtensions - { - /// - /// Adds Umbraco preview support - /// - public static IUmbracoBuilder AddExamineLucene(this IUmbracoBuilder builder) - { - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (!isWindows) - { - return builder; - } - - builder.AddNotificationHandler(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - builder.Services.AddUnique(); - - return builder; - } - - public static IUmbracoBuilder AddExamineIndexConfiguration(this IUmbracoBuilder builder) - { - builder.AddNotificationHandler(); - - - return builder; - } - - } -} diff --git a/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs deleted file mode 100644 index 70f3825667..0000000000 --- a/src/Umbraco.Examine.Lucene/ILuceneDirectoryFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public interface ILuceneDirectoryFactory - { - Lucene.Net.Store.Directory CreateDirectory(string indexName); - } -} diff --git a/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs deleted file mode 100644 index 9e09c7e96e..0000000000 --- a/src/Umbraco.Examine.Lucene/LuceneFileSystemDirectoryFactory.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.IO; -using Examine.LuceneEngine.Directories; -using Lucene.Net.Store; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class LuceneFileSystemDirectoryFactory : ILuceneDirectoryFactory - { - private readonly ITypeFinder _typeFinder; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IndexCreatorSettings _settings; - - public LuceneFileSystemDirectoryFactory(ITypeFinder typeFinder, IHostingEnvironment hostingEnvironment, IOptions settings) - { - _typeFinder = typeFinder; - _hostingEnvironment = hostingEnvironment; - _settings = settings.Value; - } - - public Lucene.Net.Store.Directory CreateDirectory(string indexName) => CreateFileSystemLuceneDirectory(indexName); - - /// - /// Creates a file system based Lucene with the correct locking guidelines for Umbraco - /// - /// - /// The folder name to store the index (single word, not a fully qualified folder) (i.e. Internal) - /// - /// - public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string folderName) - { - - var dirInfo = new DirectoryInfo(Path.Combine(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData), "ExamineIndexes", folderName)); - if (!dirInfo.Exists) - System.IO.Directory.CreateDirectory(dirInfo.FullName); - - //check if there's a configured directory factory, if so create it and use that to create the lucene dir - var configuredDirectoryFactory = _settings.LuceneDirectoryFactory; - - if (!configuredDirectoryFactory.IsNullOrWhiteSpace()) - { - //this should be a fully qualified type - var factoryType = _typeFinder.GetTypeByName(configuredDirectoryFactory); - if (factoryType == null) throw new NullReferenceException("No directory type found for value: " + configuredDirectoryFactory); - var directoryFactory = (IDirectoryFactory)Activator.CreateInstance(factoryType); - return directoryFactory.CreateDirectory(dirInfo); - } - - //no dir factory, just create a normal fs directory - - var luceneDir = new SimpleFSDirectory(dirInfo); - - //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain - //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock - //which simply checks the existence of the lock file - // The full syntax of this is: new NoPrefixSimpleFsLockFactory(dirInfo) - // however, we are setting the DefaultLockFactory in startup so we'll use that instead since it can be managed globally. - luceneDir.SetLockFactory(DirectoryFactory.DefaultLockFactory(dirInfo)); - return luceneDir; - - - } - } -} diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs b/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs deleted file mode 100644 index dc2acfa66d..0000000000 --- a/src/Umbraco.Examine.Lucene/LuceneIndexCreator.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Collections.Generic; -using Examine; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// - /// Abstract class for creating Lucene based Indexes - /// - public abstract class LuceneIndexCreator : IIndexCreator - { - private readonly ITypeFinder _typeFinder; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IndexCreatorSettings _settings; - - protected LuceneIndexCreator(ITypeFinder typeFinder, IHostingEnvironment hostingEnvironment, IOptions settings) - { - _typeFinder = typeFinder; - _hostingEnvironment = hostingEnvironment; - _settings = settings.Value; - } - - public abstract IEnumerable Create(); - } -} diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs index 1cba0767eb..ebc7d13b58 100644 --- a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs +++ b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnostics.cs @@ -1,10 +1,14 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Collections.Generic; -using Examine.LuceneEngine.Providers; +using System.Threading.Tasks; +using Examine.Lucene; +using Examine.Lucene.Providers; using Lucene.Net.Store; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Hosting; using Umbraco.Extensions; @@ -14,10 +18,16 @@ namespace Umbraco.Cms.Infrastructure.Examine public class LuceneIndexDiagnostics : IIndexDiagnostics { private readonly IHostingEnvironment _hostingEnvironment; + private readonly LuceneDirectoryIndexOptions _indexOptions; - public LuceneIndexDiagnostics(LuceneIndex index, ILogger logger, IHostingEnvironment hostingEnvironment) + public LuceneIndexDiagnostics( + LuceneIndex index, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IOptionsSnapshot indexOptions) { _hostingEnvironment = hostingEnvironment; + _indexOptions = indexOptions.Get(index.Name); Index = index; Logger = logger; } @@ -25,37 +35,7 @@ namespace Umbraco.Cms.Infrastructure.Examine public LuceneIndex Index { get; } public ILogger Logger { get; } - public int DocumentCount - { - get - { - try - { - return Index.GetIndexDocumentCount(); - } - catch (AlreadyClosedException) - { - Logger.LogWarning("Cannot get GetIndexDocumentCount, the writer is already closed"); - return 0; - } - } - } - - public int FieldCount - { - get - { - try - { - return Index.GetIndexFieldCount(); - } - catch (AlreadyClosedException) - { - Logger.LogWarning("Cannot get GetIndexFieldCount, the writer is already closed"); - return 0; - } - } - } + public Attempt IsHealthy() { @@ -63,11 +43,15 @@ namespace Umbraco.Cms.Infrastructure.Examine return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); } + public long GetDocumentCount() => Index.GetDocumentCount(); + + public IEnumerable GetFieldNames() => Index.GetFieldNames(); + public virtual IReadOnlyDictionary Metadata { get { - var luceneDir = Index.GetLuceneDirectory(); + Directory luceneDir = Index.GetLuceneDirectory(); var d = new Dictionary { [nameof(UmbracoExamineIndex.CommitCount)] = Index.CommitCount, @@ -82,6 +66,20 @@ namespace Umbraco.Cms.Infrastructure.Examine d[nameof(UmbracoExamineIndex.LuceneIndexFolder)] = fsDir.Directory.ToString().ToLowerInvariant().TrimStart(rootDir.ToLowerInvariant()).Replace("\\", "/").EnsureStartsWith('/'); } + if (_indexOptions != null) + { + if (_indexOptions.DirectoryFactory != null) + { + d[nameof(LuceneDirectoryIndexOptions.DirectoryFactory)] = _indexOptions.DirectoryFactory.GetType(); + } + + if (_indexOptions.IndexDeletionPolicy != null) + { + d[nameof(LuceneDirectoryIndexOptions.IndexDeletionPolicy)] = _indexOptions.IndexDeletionPolicy.GetType(); + } + + } + return d; } } diff --git a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs index 322da710dc..fb8b082d15 100644 --- a/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs +++ b/src/Umbraco.Examine.Lucene/LuceneIndexDiagnosticsFactory.cs @@ -1,9 +1,13 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. +using System; using Examine; -using Examine.LuceneEngine.Providers; +using Examine.Lucene; +using Examine.Lucene.Providers; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Hosting; namespace Umbraco.Cms.Infrastructure.Examine @@ -17,7 +21,9 @@ namespace Umbraco.Cms.Infrastructure.Examine private readonly ILoggerFactory _loggerFactory; private readonly IHostingEnvironment _hostingEnvironment; - public LuceneIndexDiagnosticsFactory(ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment) + public LuceneIndexDiagnosticsFactory( + ILoggerFactory loggerFactory, + IHostingEnvironment hostingEnvironment) { _loggerFactory = loggerFactory; _hostingEnvironment = hostingEnvironment; @@ -28,9 +34,17 @@ namespace Umbraco.Cms.Infrastructure.Examine if (!(index is IIndexDiagnostics indexDiag)) { if (index is LuceneIndex luceneIndex) - indexDiag = new LuceneIndexDiagnostics(luceneIndex, _loggerFactory.CreateLogger(), _hostingEnvironment); + { + indexDiag = new LuceneIndexDiagnostics( + luceneIndex, + _loggerFactory.CreateLogger(), + _hostingEnvironment, + null); + } else + { indexDiag = base.Create(index); + } } return indexDiag; } diff --git a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs index 1f9802f072..1c7127b9d5 100644 --- a/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs +++ b/src/Umbraco.Examine.Lucene/LuceneRAMDirectoryFactory.cs @@ -1,27 +1,30 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; +using System.IO; +using System.Threading; +using Examine.Lucene.Directories; +using Examine.Lucene.Providers; using Lucene.Net.Store; +using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Cms.Infrastructure.Examine { - public class LuceneRAMDirectoryFactory : ILuceneDirectoryFactory + public class LuceneRAMDirectoryFactory : DirectoryFactoryBase { + public LuceneRAMDirectoryFactory() { - } - public Lucene.Net.Store.Directory CreateDirectory(string indexName) => new RandomIdRAMDirectory(); + protected override Directory CreateDirectory(LuceneIndex luceneIndex, bool forceUnlock) + => new RandomIdRAMDirectory(); private class RandomIdRAMDirectory : RAMDirectory { private readonly string _lockId = Guid.NewGuid().ToString(); - public override string GetLockId() - { - return _lockId; - } + public override string GetLockID() => _lockId; } } } diff --git a/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs b/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs index 38d704e681..ed6f47c882 100644 --- a/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs +++ b/src/Umbraco.Examine.Lucene/NoPrefixSimpleFsLockFactory.cs @@ -6,6 +6,7 @@ using Lucene.Net.Store; namespace Umbraco.Cms.Infrastructure.Examine { + /// /// A custom that ensures a prefixless lock prefix /// diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index 329c11f879..fdd8ba9d64 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -1,53 +1,46 @@  - - - net472 - Umbraco.Cms.Infrastructure.Examine - Umbraco CMS - Umbraco.Examine.Lucene - - - false - - Umbraco.Cms.Examine.Lucene - - - - true - bin\Release\Umbraco.Examine.Lucene.xml - - - - - - - - - - - - - - - - - - - - - - - 1.0.0 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - 3.5.4 - runtime; build; native; contentfiles; analyzers - all - - - - + + netstandard2.0 + Umbraco.Cms.Infrastructure.Examine + Umbraco CMS + Umbraco.Examine.Lucene + + Umbraco.Cms.Examine.Lucene + + + true + bin\Release\Umbraco.Examine.Lucene.xml + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + 3.5.4 + runtime; build; native; contentfiles; analyzers + all + + + all + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs b/src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs new file mode 100644 index 0000000000..e99f986176 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/UmbracoApplicationRoot.cs @@ -0,0 +1,23 @@ +using System.IO; +using Examine; +using Umbraco.Cms.Core.Hosting; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// Sets the Examine to be ExamineIndexes sub directory of the Umbraco TEMP folder + /// + public class UmbracoApplicationRoot : IApplicationRoot + { + private readonly IHostingEnvironment _hostingEnvironment; + + public UmbracoApplicationRoot(IHostingEnvironment hostingEnvironment) + => _hostingEnvironment = hostingEnvironment; + + public DirectoryInfo ApplicationRoot + => new DirectoryInfo( + Path.Combine( + _hostingEnvironment.MapPathContentRoot(Core.Constants.SystemDirectories.TempData), + "ExamineIndexes")); + } +} diff --git a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs index 18b9945a6e..b3852254af 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs @@ -5,12 +5,10 @@ using System; using System.Collections.Generic; using System.Linq; using Examine; -using Examine.LuceneEngine; -using Lucene.Net.Analysis; -using Lucene.Net.Store; +using Examine.Lucene; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Infrastructure.Examine @@ -21,49 +19,38 @@ namespace Umbraco.Cms.Infrastructure.Examine public class UmbracoContentIndex : UmbracoExamineIndex, IUmbracoContentIndex, IDisposable { private readonly ILogger _logger; - protected ILocalizationService LanguageService { get; } - #region Constructors - - /// - /// Create an index at runtime - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public UmbracoContentIndex( - string name, - Directory luceneDirectory, - FieldDefinitionCollection fieldDefinitions, - Analyzer defaultAnalyzer, - IProfilingLogger profilingLogger, - ILogger logger, ILoggerFactory loggerFactory, + string name, + IOptionsSnapshot indexOptions, IHostingEnvironment hostingEnvironment, IRuntimeState runtimeState, - ILocalizationService languageService, - IContentValueSetValidator validator, - IReadOnlyDictionary indexValueTypes = null) - : base(name, luceneDirectory, fieldDefinitions, defaultAnalyzer, profilingLogger, logger, loggerFactory ,hostingEnvironment, runtimeState, validator, indexValueTypes) + ILocalizationService languageService = null) + : base(loggerFactory, name, indexOptions, hostingEnvironment, runtimeState) { - if (validator == null) throw new ArgumentNullException(nameof(validator)); - _logger = logger; - LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); + LanguageService = languageService; + _logger = loggerFactory.CreateLogger(); - if (validator is IContentValueSetValidator contentValueSetValidator) + LuceneDirectoryIndexOptions namedOptions = indexOptions.Get(name); + if (namedOptions == null) + { + throw new InvalidOperationException($"No named {typeof(LuceneDirectoryIndexOptions)} options with name {name}"); + } + + if (namedOptions.Validator is IContentValueSetValidator contentValueSetValidator) + { PublishedValuesOnly = contentValueSetValidator.PublishedValuesOnly; + } } - #endregion + protected ILocalizationService LanguageService { get; } + + /// + /// Explicitly override because we need to do validation differently than the underlying logic + /// + /// + void IIndex.IndexItems(IEnumerable values) => PerformIndexItems(values, OnIndexOperationComplete); /// /// Special check for invalid paths @@ -75,45 +62,48 @@ namespace Umbraco.Cms.Infrastructure.Examine // We don't want to re-enumerate this list, but we need to split it into 2x enumerables: invalid and valid items. // The Invalid items will be deleted, these are items that have invalid paths (i.e. moved to the recycle bin, etc...) // Then we'll index the Value group all together. - // We return 0 or 1 here so we can order the results and do the invalid first and then the valid. var invalidOrValid = values.GroupBy(v => { - if (!v.Values.TryGetValue("path", out var paths) || paths.Count <= 0 || paths[0] == null) - return 0; + if (!v.Values.TryGetValue("path", out List paths) || paths.Count <= 0 || paths[0] == null) + { + return ValueSetValidationResult.Failed; + } - //we know this is an IContentValueSetValidator - var validator = (IContentValueSetValidator)ValueSetValidator; - var path = paths[0].ToString(); + ValueSetValidationResult validationResult = ValueSetValidator.Validate(v); - return (!validator.ValidatePath(path, v.Category) - || !validator.ValidateRecycleBin(path, v.Category) - || !validator.ValidateProtectedContent(path, v.Category)) - ? 0 - : 1; + return validationResult; }).ToList(); var hasDeletes = false; var hasUpdates = false; - foreach (var group in invalidOrValid.OrderBy(x => x.Key)) - { - if (group.Key == 0) - { - hasDeletes = true; - //these are the invalid items so we'll delete them - //since the path is not valid we need to delete this item in case it exists in the index already and has now - //been moved to an invalid parent. - base.PerformDeleteFromIndex(group.Select(x => x.Id), args => { /*noop*/ }); - } - else + // ordering by descending so that Filtered/Failed processes first + foreach (IGrouping group in invalidOrValid.OrderByDescending(x => x.Key)) + { + switch (group.Key) { - hasUpdates = true; - //these are the valid ones, so just index them all at once - base.PerformIndexItems(group.ToList(), onComplete); + case ValueSetValidationResult.Valid: + hasUpdates = true; + + //these are the valid ones, so just index them all at once + base.PerformIndexItems(group.ToList(), onComplete); + break; + case ValueSetValidationResult.Failed: + // don't index anything that is invalid + break; + case ValueSetValidationResult.Filtered: + hasDeletes = true; + + // these are the invalid/filtered items so we'll delete them + // since the path is not valid we need to delete this item in + // case it exists in the index already and has now + // been moved to an invalid parent. + base.PerformDeleteFromIndex(group.Select(x => x.Id), null); + break; } } - if (hasDeletes && !hasUpdates || !hasDeletes && !hasUpdates) + if ((hasDeletes && !hasUpdates) || (!hasDeletes && !hasUpdates)) { //we need to manually call the completed method onComplete(new IndexOperationEventArgs(this, 0)); @@ -133,21 +123,27 @@ namespace Umbraco.Cms.Infrastructure.Examine protected override void PerformDeleteFromIndex(IEnumerable itemIds, Action onComplete) { var idsAsList = itemIds.ToList(); - foreach (var nodeId in idsAsList) + + for (int i = 0; i < idsAsList.Count; i++) { + string nodeId = idsAsList[i]; + //find all descendants based on path var descendantPath = $@"\-1\,*{nodeId}\,*"; var rawQuery = $"{UmbracoExamineFieldNames.IndexPathFieldName}:{descendantPath}"; - var searcher = GetSearcher(); - var c = searcher.CreateQuery(); + var c = Searcher.CreateQuery(); var filtered = c.NativeQuery(rawQuery); var results = filtered.Execute(); _logger. LogDebug("DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); - //need to queue a delete item for each one found - QueueIndexOperation(results.Select(r => new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete))); + var toRemove = results.Select(x => x.Id).ToList(); + // delete those descendants (ensure base. is used here so we aren't calling ourselves!) + base.PerformDeleteFromIndex(toRemove, null); + + // remove any ids from our list that were part of the descendants + idsAsList.RemoveAll(x => toRemove.Contains(x)); } base.PerformDeleteFromIndex(idsAsList, onComplete); diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs index 851dfbd152..c8dfdd756a 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs @@ -4,20 +4,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Examine; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Analysis; +using Examine.Lucene; +using Examine.Lucene.Providers; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Store; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Services; -using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Cms.Infrastructure.Examine { @@ -27,61 +24,24 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public abstract class UmbracoExamineIndex : LuceneIndex, IUmbracoIndex, IIndexDiagnostics { - private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; - + private readonly UmbracoExamineIndexDiagnostics _diagnostics; private readonly IRuntimeState _runtimeState; + private bool _hasLoggedInitLog = false; + private readonly ILogger _logger; - // note - // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call - // context because they will fork a thread/task/whatever which should *not* capture our - // call context (and the database it can contain)! - // TODO: FIX Examine to not flow the ExecutionContext so callers don't need to worry about this! - - /// - /// Create a new - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// protected UmbracoExamineIndex( - string name, - Directory luceneDirectory, - FieldDefinitionCollection fieldDefinitions, - Analyzer defaultAnalyzer, - IProfilingLogger profilingLogger, - ILogger logger, ILoggerFactory loggerFactory, + string name, + IOptionsSnapshot indexOptions, IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - IValueSetValidator validator = null, - IReadOnlyDictionary indexValueTypes = null) - : base(name, luceneDirectory, fieldDefinitions, defaultAnalyzer, validator, indexValueTypes) + IRuntimeState runtimeState) + : base(loggerFactory, name, indexOptions) { - _logger = logger; - _loggerFactory = loggerFactory; _runtimeState = runtimeState; - ProfilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); - - //try to set the value of `LuceneIndexFolder` for diagnostic reasons - if (luceneDirectory is FSDirectory fsDir) - LuceneIndexFolder = fsDir.Directory; - - _diagnostics = new UmbracoExamineIndexDiagnostics(this, _loggerFactory.CreateLogger(), hostingEnvironment); + _diagnostics = new UmbracoExamineIndexDiagnostics(this, loggerFactory.CreateLogger(), hostingEnvironment, indexOptions); + _logger = loggerFactory.CreateLogger(); } - private readonly bool _configBased = false; - - protected IProfilingLogger ProfilingLogger { get; } - /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically /// @@ -89,14 +49,6 @@ namespace Umbraco.Cms.Infrastructure.Examine public bool PublishedValuesOnly { get; protected set; } = false; - /// - public IEnumerable GetFields() - { - //we know this is a LuceneSearcher - var searcher = (LuceneSearcher)GetSearcher(); - return searcher.GetAllIndexedFields(); - } - /// /// override to check if we can actually initialize. /// @@ -107,13 +59,7 @@ namespace Umbraco.Cms.Infrastructure.Examine { if (CanInitialize()) { - // Use ExecutionContext.SuppressFlow to prevent the current Execution Context (AsyncLocal) flow to child - // tasks executed in the base class so we don't leak Scopes. - // TODO: See notes at the top of this class - using (ExecutionContext.SuppressFlow()) - { - base.PerformDeleteFromIndex(itemIds, onComplete); - } + base.PerformDeleteFromIndex(itemIds, onComplete); } } @@ -121,13 +67,7 @@ namespace Umbraco.Cms.Infrastructure.Examine { if (CanInitialize()) { - // Use ExecutionContext.SuppressFlow to prevent the current Execution Context (AsyncLocal) flow to child - // tasks executed in the base class so we don't leak Scopes. - // TODO: See notes at the top of this class - using (ExecutionContext.SuppressFlow()) - { - base.PerformIndexItems(values, onComplete); - } + base.PerformIndexItems(values, onComplete); } } @@ -137,19 +77,15 @@ namespace Umbraco.Cms.Infrastructure.Examine /// protected bool CanInitialize() { - // only affects indexers that are config file based, if an index was created via code then - // this has no effect, it is assumed the index would not be created if it could not be initialized - return _configBased == false || _runtimeState.Level == RuntimeLevel.Run; - } + var canInit = _runtimeState.Level == RuntimeLevel.Run; - /// - /// overridden for logging - /// - /// - protected override void OnIndexingError(IndexingErrorEventArgs ex) - { - _logger.LogError(ex.InnerException, ex.Message); - base.OnIndexingError(ex); + if (!canInit && !_hasLoggedInitLog) + { + _hasLoggedInitLog = true; + _logger.LogWarning("Runtime state is not " + RuntimeLevel.Run + ", no indexing will occur"); + } + + return canInit; } /// @@ -167,31 +103,16 @@ namespace Umbraco.Cms.Infrastructure.Examine //remove the original value so we can store it the correct way d.RemoveField(f.Key); - d.Add(new Field( + d.Add(new StringField( f.Key, f.Value[0].ToString(), - Field.Store.YES, - Field.Index.NO, //don't index this field, we never want to search by it - Field.TermVector.NO)); + Field.Store.YES)); } } base.OnDocumentWriting(docArgs); } - /// - /// Overridden for logging. - /// - protected override void AddDocument(Document doc, ValueSet valueSet, IndexWriter writer) - { - _logger.LogDebug("Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", - valueSet.Id, - valueSet.Category, - valueSet.ItemType); - - base.AddDocument(doc, valueSet, writer); - } - protected override void OnTransformingIndexValues(IndexingItemEventArgs e) { base.OnTransformingIndexValues(e); @@ -210,15 +131,7 @@ namespace Umbraco.Cms.Infrastructure.Examine } } - #region IIndexDiagnostics - - private readonly UmbracoExamineIndexDiagnostics _diagnostics; - - public int DocumentCount => _diagnostics.DocumentCount; - public int FieldCount => _diagnostics.FieldCount; public Attempt IsHealthy() => _diagnostics.IsHealthy(); public virtual IReadOnlyDictionary Metadata => _diagnostics.Metadata; - - #endregion } } diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs index b8e6a20e68..08b9cd27d3 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndexDiagnostics.cs @@ -1,9 +1,11 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System.Collections.Generic; using System.Linq; +using Examine.Lucene; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Hosting; namespace Umbraco.Cms.Infrastructure.Examine @@ -12,8 +14,12 @@ namespace Umbraco.Cms.Infrastructure.Examine { private readonly UmbracoExamineIndex _index; - public UmbracoExamineIndexDiagnostics(UmbracoExamineIndex index, ILogger logger, IHostingEnvironment hostingEnvironment) - : base(index, logger, hostingEnvironment) + public UmbracoExamineIndexDiagnostics( + UmbracoExamineIndex index, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IOptionsSnapshot indexOptions) + : base(index, logger, hostingEnvironment, indexOptions) { _index = index; } diff --git a/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs b/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs deleted file mode 100644 index aa7b30677f..0000000000 --- a/src/Umbraco.Examine.Lucene/UmbracoIndexesCreator.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Collections.Generic; -using Examine; -using Examine.LuceneEngine; -using Lucene.Net.Analysis.Standard; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Services; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// Creates the indexes used by Umbraco - /// - public class UmbracoIndexesCreator : LuceneIndexCreator, IUmbracoIndexesCreator - { - // TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? - - public UmbracoIndexesCreator( - ITypeFinder typeFinder, - IProfilingLogger profilingLogger, - ILoggerFactory loggerFactory, - ILocalizationService languageService, - IPublicAccessService publicAccessService, - IMemberService memberService, - IUmbracoIndexConfig umbracoIndexConfig, - IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - IOptions settings, - ILuceneDirectoryFactory directoryFactory) : base(typeFinder, hostingEnvironment, settings) - { - ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); - LoggerFactory = loggerFactory; - LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); - PublicAccessService = publicAccessService ?? throw new System.ArgumentNullException(nameof(publicAccessService)); - MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); - UmbracoIndexConfig = umbracoIndexConfig; - HostingEnvironment = hostingEnvironment ?? throw new System.ArgumentNullException(nameof(hostingEnvironment)); - RuntimeState = runtimeState ?? throw new System.ArgumentNullException(nameof(runtimeState)); - DirectoryFactory = directoryFactory; - } - - protected IProfilingLogger ProfilingLogger { get; } - protected ILoggerFactory LoggerFactory { get; } - protected IHostingEnvironment HostingEnvironment { get; } - protected IRuntimeState RuntimeState { get; } - protected ILuceneDirectoryFactory DirectoryFactory { get; } - protected ILocalizationService LanguageService { get; } - protected IPublicAccessService PublicAccessService { get; } - protected IMemberService MemberService { get; } - protected IUmbracoIndexConfig UmbracoIndexConfig { get; } - - /// - /// Creates the Umbraco indexes - /// - /// - public override IEnumerable Create() - { - return new[] - { - CreateInternalIndex(), - CreateExternalIndex(), - CreateMemberIndex() - }; - } - - private IIndex CreateInternalIndex() - { - var index = new UmbracoContentIndex( - Constants.UmbracoIndexes.InternalIndexName, - DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.InternalIndexPath), - new UmbracoFieldDefinitionCollection(), - new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, - LoggerFactory.CreateLogger(), - LoggerFactory, - HostingEnvironment, - RuntimeState, - LanguageService, - UmbracoIndexConfig.GetContentValueSetValidator() - ); - return index; - } - - private IIndex CreateExternalIndex() - { - var index = new UmbracoContentIndex( - Constants.UmbracoIndexes.ExternalIndexName, - DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.ExternalIndexPath), - new UmbracoFieldDefinitionCollection(), - new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), - ProfilingLogger, - LoggerFactory.CreateLogger(), - LoggerFactory, - HostingEnvironment, - RuntimeState, - LanguageService, - UmbracoIndexConfig.GetPublishedContentValueSetValidator()); - return index; - } - - private IIndex CreateMemberIndex() - { - var index = new UmbracoMemberIndex( - Constants.UmbracoIndexes.MembersIndexName, - new UmbracoFieldDefinitionCollection(), - DirectoryFactory.CreateDirectory(Constants.UmbracoIndexes.MembersIndexPath), - new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, - LoggerFactory.CreateLogger(), - LoggerFactory, - HostingEnvironment, - RuntimeState, - UmbracoIndexConfig.GetMemberValueSetValidator() - ); - return index; - } - } -} diff --git a/src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs b/src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs new file mode 100644 index 0000000000..89f61c1e53 --- /dev/null +++ b/src/Umbraco.Examine.Lucene/UmbracoLockFactory.cs @@ -0,0 +1,15 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.IO; +using Examine.Lucene.Directories; +using Lucene.Net.Store; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public class UmbracoLockFactory : ILockFactory + { + public LockFactory GetLockFactory(DirectoryInfo directory) + => new NoPrefixSimpleFsLockFactory(directory); + } +} diff --git a/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs index 3889209fdb..0792dd8a6f 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoMemberIndex.cs @@ -1,13 +1,11 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. -using Examine; -using Lucene.Net.Analysis; +using Examine.Lucene; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Services; -using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Cms.Infrastructure.Examine { @@ -16,31 +14,14 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public class UmbracoMemberIndex : UmbracoExamineIndex, IUmbracoMemberIndex { - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - /// - /// - /// - /// - /// - /// public UmbracoMemberIndex( - string name, - FieldDefinitionCollection fieldDefinitions, - Directory luceneDirectory, - Analyzer analyzer, - IProfilingLogger profilingLogger, - ILogger logger, ILoggerFactory loggerFactory, + string name, + IOptionsSnapshot indexOptions, IHostingEnvironment hostingEnvironment, - IRuntimeState runtimeState, - IValueSetValidator validator = null) : - base(name, luceneDirectory, fieldDefinitions, analyzer, profilingLogger, logger, loggerFactory, hostingEnvironment, runtimeState, validator) + IRuntimeState runtimeState) + : base(loggerFactory, name, indexOptions, hostingEnvironment, runtimeState) { } - } } diff --git a/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs b/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs index 72fb312d08..092b4c6b99 100644 --- a/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Cache/DatabaseServerMessengerNotificationHandler.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Infrastructure.Persistence; @@ -12,7 +13,7 @@ namespace Umbraco.Cms.Core.Cache /// /// Ensures that distributed cache events are setup and the is initialized /// - public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler, INotificationHandler + public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler, INotificationHandler { private readonly IServerMessenger _messenger; private readonly IUmbracoDatabaseFactory _databaseFactory; @@ -35,26 +36,26 @@ namespace Umbraco.Cms.Core.Cache } /// - public void Handle(UmbracoApplicationStarting notification) + public void Handle(UmbracoApplicationStartingNotification notification) { + if (_runtimeState.Level < RuntimeLevel.Run) + { + return; + } + if (_databaseFactory.CanConnect == false) { _logger.LogWarning("Cannot connect to the database, distributed calls will not be enabled for this server."); - } - else if (_runtimeState.Level != RuntimeLevel.Run) - { - _logger.LogWarning("Distributed calls are not available outside the Run runtime level"); - } - else - { - // Sync on startup, this will run through the messenger's initialization sequence - _messenger?.Sync(); + return; } + + // Sync on startup, this will run through the messenger's initialization sequence + _messenger?.Sync(); } /// /// Clear the batch on end request /// - public void Handle(UmbracoRequestEnd notification) => _messenger?.SendMessages(); + public void Handle(UmbracoRequestEndNotification notification) => _messenger?.SendMessages(); } } diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs index b37ccabb61..35845f3cd0 100644 --- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs @@ -5,8 +5,8 @@ using System.Linq; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache diff --git a/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs b/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs index 78bb73f301..f9cc387929 100644 --- a/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs +++ b/src/Umbraco.Infrastructure/Configuration/JsonConfigManipulator.cs @@ -74,7 +74,7 @@ namespace Umbraco.Cms.Core.Configuration var json = GetJson(provider); - var item = GetDisableRedirectUrlItem(disable.ToString().ToLowerInvariant()); + var item = GetDisableRedirectUrlItem(disable); json.Merge(item, new JsonMergeSettings()); @@ -115,7 +115,7 @@ namespace Umbraco.Cms.Core.Configuration return writer.Token; } - private JToken GetDisableRedirectUrlItem(string value) + private JToken GetDisableRedirectUrlItem(bool value) { JTokenWriter writer = new JTokenWriter(); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 4d8a6948a9..f49931c105 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -21,6 +21,7 @@ using Umbraco.Cms.Core.Mail; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; @@ -30,7 +31,6 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Trees; @@ -164,8 +164,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); - // Register noop versions for examine to be overridden by examine - builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); @@ -180,7 +178,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection // Services required to run background jobs (with out the handler) builder.Services.AddUnique(); - builder.Services.AddUnique(); return builder; } @@ -252,7 +249,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection public static IUmbracoBuilder AddCoreNotifications(this IUmbracoBuilder builder) { -// add handlers for sending user notifications (i.e. emails) + // add handlers for sending user notifications (i.e. emails) builder.Services.AddUnique(); builder .AddNotificationHandler() diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs index ed44e170fb..05dba2cc0f 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Sync; @@ -26,10 +27,11 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// public static IUmbracoBuilder AddDistributedCache(this IUmbracoBuilder builder) { - builder.SetDatabaseServerMessengerCallbacks(GetCallbacks); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.SetServerMessenger(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); return builder; } @@ -58,24 +60,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection public static void SetServerRegistrar(this IUmbracoBuilder builder, IServerRoleAccessor registrar) => builder.Services.AddUnique(registrar); - /// - /// Sets the database server messenger options. - /// - /// The builder. - /// A function creating the options. - /// Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default. - public static void SetDatabaseServerMessengerCallbacks(this IUmbracoBuilder builder, Func factory) - => builder.Services.AddUnique(factory); - - /// - /// Sets the database server messenger options. - /// - /// The builder. - /// Options. - /// Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default. - public static void SetDatabaseServerMessengerOptions(this IUmbracoBuilder builder, DatabaseServerMessengerCallbacks options) - => builder.Services.AddUnique(options); - /// /// Sets the server messenger. /// @@ -100,36 +84,5 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection /// A server messenger. public static void SetServerMessenger(this IUmbracoBuilder builder, IServerMessenger registrar) => builder.Services.AddUnique(registrar); - - private static DatabaseServerMessengerCallbacks GetCallbacks(IServiceProvider factory) => new DatabaseServerMessengerCallbacks - { - // These callbacks will be executed if the server has not been synced - // (i.e. it is a new server or the lastsynced.txt file has been removed) - InitializingCallbacks = new Action[] - { - // rebuild the xml cache file if the server is not synced - () => - { - IPublishedSnapshotService publishedSnapshotService = factory.GetRequiredService(); - - // rebuild the published snapshot caches entirely, if the server is not synced - // this is equivalent to DistributedCache RefreshAll... but local only - // (we really should have a way to reuse RefreshAll... locally) - // note: refresh all content & media caches does refresh content types too - publishedSnapshotService.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) }); - publishedSnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _); - publishedSnapshotService.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _); - }, - - // rebuild indexes if the server is not synced - // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific - // indexes then they can adjust this logic themselves. - () => - { - var indexRebuilder = factory.GetRequiredService(); - indexRebuilder.RebuildIndexes(false, TimeSpan.FromSeconds(5)); - } - } - }; } } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs index 033ab76298..d061a4372c 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; @@ -27,7 +28,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(factory => @@ -48,14 +50,15 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection false)); builder.Services.AddUnique, MediaValueSetBuilder>(); builder.Services.AddUnique, MemberValueSetBuilder>(); - builder.Services.AddUnique(); + builder.Services.AddUnique(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); - builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + + builder.AddNotificationHandler(); return builder; } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs index b00b9a4985..edb8033f3d 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs @@ -19,10 +19,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection * Create an implementation of IFileSystem and register it as the underlying filesystem for * MediaFileSystem with the following extension on composition. * - * composition.SetMediaFileSystem(factory => FactoryMethodToReturnYourImplementation()) - * - * Alternatively you can just register an Implementation of IMediaFileSystem, however the - * extension above ensures that your IFileSystem implementation is wrapped by the "ShadowWrapper". + * builder.SetMediaFileSystem(factory => FactoryMethodToReturnYourImplementation()) * * WHAT IS SHADOWING * ----------------- @@ -37,23 +34,17 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection internal static IUmbracoBuilder AddFileSystems(this IUmbracoBuilder builder) { // register FileSystems, which manages all filesystems - // it needs to be registered (not only the interface) because it provides additional - // functionality eg for scoping, and is injected in the scope provider - whereas the - // interface is really for end-users to get access to filesystems. - builder.Services.AddUnique(factory => factory.CreateInstance(factory)); - - // register IFileSystems, which gives access too all filesystems - builder.Services.AddUnique(factory => factory.GetRequiredService()); + builder.Services.AddUnique(); // register the scheme for media paths builder.Services.AddUnique(); builder.SetMediaFileSystem(factory => { - var ioHelper = factory.GetRequiredService(); - var hostingEnvironment = factory.GetRequiredService(); - var logger = factory.GetRequiredService>(); - var globalSettings = factory.GetRequiredService>().Value; + IIOHelper ioHelper = factory.GetRequiredService(); + IHostingEnvironment hostingEnvironment = factory.GetRequiredService(); + ILogger logger = factory.GetRequiredService>(); + GlobalSettings globalSettings = factory.GetRequiredService>().Value; var rootPath = hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPath); var rootUrl = hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs index 5a6c8fe8f2..cbbaa6a3e0 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.IO; @@ -109,20 +110,48 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection } /// - /// Sets the underlying media filesystem. + /// Sets the filesystem used by the MediaFileManager /// /// A builder. - /// A filesystem factory. - /// - /// Using this helper will ensure that your IFileSystem implementation is wrapped by the ShadowWrapper - /// - public static void SetMediaFileSystem(this IUmbracoBuilder builder, Func filesystemFactory) - => builder.Services.AddUnique(factory => + /// Factory method to create an IFileSystem implementation used in the MediaFileManager + public static void SetMediaFileSystem(this IUmbracoBuilder builder, + Func filesystemFactory) => builder.Services.AddUnique( + provider => { - var fileSystems = factory.GetRequiredService(); - return fileSystems.GetFileSystem(filesystemFactory(factory)); + IFileSystem filesystem = filesystemFactory(provider); + // We need to use the Filesystems to create a shadow wrapper, + // because shadow wrapper requires the IsScoped delegate from the FileSystems. + // This is used by the scope provider when taking control of the filesystems. + FileSystems fileSystems = provider.GetRequiredService(); + IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media"); + + return provider.CreateInstance(shadow); }); + /// + /// Register FileSystems with a method to configure the . + /// + /// A builder. + /// Method that configures the . + /// Throws exception if is null. + /// Throws exception if full path can't be resolved successfully. + public static void ConfigureFileSystems(this IUmbracoBuilder builder, + Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + builder.Services.AddUnique( + provider => + { + FileSystems fileSystems = provider.CreateInstance(); + configure(provider, fileSystems); + return fileSystems; + }); + } + /// /// Sets the log viewer. /// diff --git a/src/Umbraco.Infrastructure/EmailSender.cs b/src/Umbraco.Infrastructure/EmailSender.cs index 14b5d7e0e6..02e83405d6 100644 --- a/src/Umbraco.Infrastructure/EmailSender.cs +++ b/src/Umbraco.Infrastructure/EmailSender.cs @@ -5,8 +5,10 @@ using System.Net.Mail; using System.Threading.Tasks; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Mail; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Infrastructure.Extensions; using SmtpClient = MailKit.Net.Smtp.SmtpClient; @@ -18,20 +20,41 @@ namespace Umbraco.Cms.Infrastructure public class EmailSender : IEmailSender { // TODO: This should encapsulate a BackgroundTaskRunner with a queue to send these emails! - + private readonly IEventAggregator _eventAggregator; private readonly GlobalSettings _globalSettings; + private readonly bool _notificationHandlerRegistered; - public EmailSender(IOptions globalSettings) => _globalSettings = globalSettings.Value; + public EmailSender(IOptions globalSettings, IEventAggregator eventAggregator) + : this(globalSettings, eventAggregator, null) + { + + } + + public EmailSender(IOptions globalSettings, IEventAggregator eventAggregator, INotificationHandler handler) + { + _eventAggregator = eventAggregator; + _globalSettings = globalSettings.Value; + _notificationHandlerRegistered = handler is not null; + } /// /// Sends the message async /// /// /// - public async Task SendAsync(EmailMessage message) + public async Task SendAsync(EmailMessage message) => await SendAsyncInternal(message, false); + + public async Task SendAsync(EmailMessage message, bool enableNotification) => + await SendAsyncInternal(message, enableNotification); + + private async Task SendAsyncInternal(EmailMessage message, bool enableNotification) { if (_globalSettings.IsSmtpServerConfigured == false) { + if (enableNotification) + { + await _eventAggregator.PublishAsync(new SendEmailNotification(message)); + } return; } @@ -66,6 +89,6 @@ namespace Umbraco.Cms.Infrastructure /// /// We assume this is possible if either an event handler is registered or an smtp server is configured /// - public bool CanSendRequiredEmail() => _globalSettings.IsSmtpServerConfigured; + public bool CanSendRequiredEmail() => _globalSettings.IsSmtpServerConfigured || _notificationHandlerRegistered; } } diff --git a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs index fc338fd3fe..67c1dbda06 100644 --- a/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/Events/RelateOnTrashNotificationHandler.cs @@ -3,9 +3,9 @@ using System.Linq; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Events diff --git a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs index d9fd10f1d7..bd205e2009 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; @@ -27,6 +28,7 @@ namespace Umbraco.Cms.Infrastructure.Examine private readonly bool _publishedValuesOnly; private readonly int? _parentId; + private readonly ILogger _logger; /// /// Default constructor to lookup all content data @@ -34,20 +36,30 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// /// - public ContentIndexPopulator(IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IContentValueSetBuilder contentValueSetBuilder) - : this(false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) + public ContentIndexPopulator( + ILogger logger, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IContentValueSetBuilder contentValueSetBuilder) + : this(logger, false, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) { } /// /// Optional constructor allowing specifying custom query parameters /// - public ContentIndexPopulator(bool publishedValuesOnly, int? parentId, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IValueSetBuilder contentValueSetBuilder) + public ContentIndexPopulator( + ILogger logger, + bool publishedValuesOnly, + int? parentId, + IContentService contentService, + IUmbracoDatabaseFactory umbracoDatabaseFactory, + IValueSetBuilder contentValueSetBuilder) { - if (umbracoDatabaseFactory == null) throw new ArgumentNullException(nameof(umbracoDatabaseFactory)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - _umbracoDatabaseFactory = umbracoDatabaseFactory; + _umbracoDatabaseFactory = umbracoDatabaseFactory ?? throw new ArgumentNullException(nameof(umbracoDatabaseFactory)); _contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _publishedValuesOnly = publishedValuesOnly; _parentId = parentId; } @@ -60,7 +72,11 @@ namespace Umbraco.Cms.Infrastructure.Examine protected override void PopulateIndexes(IReadOnlyList indexes) { - if (indexes.Count == 0) return; + if (indexes.Count == 0) + { + _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + return; + } const int pageSize = 10000; var pageIndex = 0; @@ -144,9 +160,10 @@ namespace Umbraco.Cms.Infrastructure.Examine var valueSets = _contentValueSetBuilder.GetValueSets(indexableContent.ToArray()).ToList(); - // ReSharper disable once PossibleMultipleEnumeration - foreach (var index in indexes) + foreach (IIndex index in indexes) + { index.IndexItems(valueSets); + } } pageIndex++; diff --git a/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs index 463e8dee26..39d260a24d 100644 --- a/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; using Umbraco.Cms.Core.Scoping; @@ -106,10 +106,14 @@ namespace Umbraco.Cms.Infrastructure.Examine if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly) { if (!valueSet.Values.TryGetValue(UmbracoExamineFieldNames.PublishedFieldName, out var published)) + { return ValueSetValidationResult.Failed; + } if (!published[0].Equals("y")) + { return ValueSetValidationResult.Failed; + } //deal with variants, if there are unpublished variants than we need to remove them from the value set if (valueSet.Values.TryGetValue(UmbracoExamineFieldNames.VariesByCultureFieldName, out var variesByCulture) diff --git a/src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs similarity index 88% rename from src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs rename to src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs index d14cef8ccf..ff9f499217 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineIndexModel.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.Serialization; -namespace Umbraco.Cms.Infrastructure.Search +namespace Umbraco.Cms.Infrastructure.Examine { [DataContract(Name = "indexer", Namespace = "")] public class ExamineIndexModel diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs new file mode 100644 index 0000000000..7d0a933027 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -0,0 +1,216 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Examine; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Runtime; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.HostedServices; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public class ExamineIndexRebuilder : IIndexRebuilder + { + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly IMainDom _mainDom; + private readonly IRuntimeState _runtimeState; + private readonly ILogger _logger; + private readonly IExamineManager _examineManager; + private readonly IEnumerable _populators; + private readonly object _rebuildLocker = new(); + + /// + /// Initializes a new instance of the class. + /// + public ExamineIndexRebuilder( + IMainDom mainDom, + IRuntimeState runtimeState, + ILogger logger, + IExamineManager examineManager, + IEnumerable populators, + IBackgroundTaskQueue backgroundTaskQueue) + { + _mainDom = mainDom; + _runtimeState = runtimeState; + _logger = logger; + _examineManager = examineManager; + _populators = populators; + _backgroundTaskQueue = backgroundTaskQueue; + } + + public bool CanRebuild(string indexName) + { + if (!_examineManager.TryGetIndex(indexName, out IIndex index)) + { + throw new InvalidOperationException("No index found by name " + indexName); + } + + return _populators.Any(x => x.IsRegistered(index)); + } + + public virtual void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true) + { + if (delay == null) + { + delay = TimeSpan.Zero; + } + + if (!CanRun()) + { + return; + } + + if (useBackgroundThread) + { + _logger.LogInformation($"Starting async background thread for rebuilding index {indexName}."); + + _backgroundTaskQueue.QueueBackgroundWorkItem( + cancellationToken => Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken))); + } + else + { + RebuildIndex(indexName, delay.Value, CancellationToken.None); + } + } + + public virtual void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true) + { + if (delay == null) + { + delay = TimeSpan.Zero; + } + + if (!CanRun()) + { + return; + } + + if (useBackgroundThread) + { + _logger.LogDebug($"Queuing background job for {nameof(RebuildIndexes)}."); + + _backgroundTaskQueue.QueueBackgroundWorkItem( + cancellationToken => + { + // This is a fire/forget task spawned by the background thread queue (which means we + // don't need to worry about ExecutionContext flowing). + Task.Run(() => RebuildIndexes(onlyEmptyIndexes, delay.Value, cancellationToken)); + + // immediately return so the queue isn't waiting. + return Task.CompletedTask; + }); + } + else + { + RebuildIndexes(onlyEmptyIndexes, delay.Value, CancellationToken.None); + } + } + + private bool CanRun() => _mainDom.IsMainDom && _runtimeState.Level >= RuntimeLevel.Run; + + private void RebuildIndex(string indexName, TimeSpan delay, CancellationToken cancellationToken) + { + if (delay > TimeSpan.Zero) + { + Thread.Sleep(delay); + } + + try + { + if (!Monitor.TryEnter(_rebuildLocker)) + { + _logger.LogWarning("Call was made to RebuildIndexes but the task runner for rebuilding is already running"); + } + else + { + if (!_examineManager.TryGetIndex(indexName, out IIndex index)) + { + throw new InvalidOperationException($"No index found with name {indexName}"); + } + + index.CreateIndex(); // clear the index + foreach (IIndexPopulator populator in _populators) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + populator.Populate(index); + } + } + } + finally + { + if (Monitor.IsEntered(_rebuildLocker)) + { + Monitor.Exit(_rebuildLocker); + } + } + } + + private void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan delay, CancellationToken cancellationToken) + { + if (delay > TimeSpan.Zero) + { + Thread.Sleep(delay); + } + + try + { + if (!Monitor.TryEnter(_rebuildLocker)) + { + _logger.LogWarning($"Call was made to {nameof(RebuildIndexes)} but the task runner for rebuilding is already running"); + } + else + { + // If an index exists but it has zero docs we'll consider it empty and rebuild + IIndex[] indexes = (onlyEmptyIndexes + ? _examineManager.Indexes.Where(x => !x.IndexExists() || (x is IIndexStats stats && stats.GetDocumentCount() == 0)) + : _examineManager.Indexes).ToArray(); + + if (indexes.Length == 0) + { + return; + } + + foreach (IIndex index in indexes) + { + index.CreateIndex(); // clear the index + } + + // run each populator over the indexes + foreach (IIndexPopulator populator in _populators) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + try + { + populator.Populate(indexes); + } + catch (Exception e) + { + _logger.LogError(e, "Index populating failed for populator {Populator}", populator.GetType()); + } + } + } + } + finally + { + if (Monitor.IsEntered(_rebuildLocker)) + { + Monitor.Exit(_rebuildLocker); + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs b/src/Umbraco.Infrastructure/Examine/ExamineSearcherModel.cs similarity index 74% rename from src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs rename to src/Umbraco.Infrastructure/Examine/ExamineSearcherModel.cs index 8e6ea30c0c..c4b602e430 100644 --- a/src/Umbraco.Infrastructure/Search/ExamineSearcherModel.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineSearcherModel.cs @@ -1,6 +1,6 @@ -using System.Runtime.Serialization; +using System.Runtime.Serialization; -namespace Umbraco.Cms.Infrastructure.Search +namespace Umbraco.Cms.Infrastructure.Examine { [DataContract(Name = "searcher", Namespace = "")] public class ExamineSearcherModel diff --git a/src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs b/src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs new file mode 100644 index 0000000000..d19db3b285 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Examine; +using Examine.Search; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Runtime; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Infrastructure.HostedServices; +using Umbraco.Cms.Infrastructure.Search; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// Indexing handler for Examine indexes + /// + internal class ExamineUmbracoIndexingHandler : IUmbracoIndexingHandler + { + // the default enlist priority is 100 + // enlist with a lower priority to ensure that anything "default" runs after us + // but greater that SafeXmlReaderWriter priority which is 60 + private const int EnlistPriority = 80; + private readonly IMainDom _mainDom; + private readonly ILogger _logger; + private readonly IProfilingLogger _profilingLogger; + private readonly IScopeProvider _scopeProvider; + private readonly IExamineManager _examineManager; + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly IContentValueSetBuilder _contentValueSetBuilder; + private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; + private readonly IValueSetBuilder _mediaValueSetBuilder; + private readonly IValueSetBuilder _memberValueSetBuilder; + private readonly Lazy _enabled; + + public ExamineUmbracoIndexingHandler( + IMainDom mainDom, + ILogger logger, + IProfilingLogger profilingLogger, + IScopeProvider scopeProvider, + IExamineManager examineManager, + IBackgroundTaskQueue backgroundTaskQueue, + IContentValueSetBuilder contentValueSetBuilder, + IPublishedContentValueSetBuilder publishedContentValueSetBuilder, + IValueSetBuilder mediaValueSetBuilder, + IValueSetBuilder memberValueSetBuilder) + { + _mainDom = mainDom; + _logger = logger; + _profilingLogger = profilingLogger; + _scopeProvider = scopeProvider; + _examineManager = examineManager; + _backgroundTaskQueue = backgroundTaskQueue; + _contentValueSetBuilder = contentValueSetBuilder; + _publishedContentValueSetBuilder = publishedContentValueSetBuilder; + _mediaValueSetBuilder = mediaValueSetBuilder; + _memberValueSetBuilder = memberValueSetBuilder; + _enabled = new Lazy(IsEnabled); + } + + /// + /// Used to lazily check if Examine Index handling is enabled + /// + /// + private bool IsEnabled() + { + //let's deal with shutting down Examine with MainDom + var examineShutdownRegistered = _mainDom.Register(release: () => + { + using (_profilingLogger.TraceDuration("Examine shutting down")) + { + _examineManager.Dispose(); + } + }); + + if (!examineShutdownRegistered) + { + _logger.LogInformation("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled"); + + //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! + Suspendable.ExamineEvents.SuspendIndexers(_logger); + return false; //exit, do not continue + } + + _logger.LogDebug("Examine shutdown registered with MainDom"); + + var registeredIndexers = _examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); + + _logger.LogInformation("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); + + // don't bind event handlers if we're not suppose to listen + if (registeredIndexers == 0) + { + return false; + } + + return true; + } + + /// + public bool Enabled => _enabled.Value; + + /// + public void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedDeleteIndex(this, entityId, keepIfUnpublished)); + } + else + { + DeferedDeleteIndex.Execute(this, entityId, keepIfUnpublished); + } + } + + /// + public void ReIndexForContent(IContent sender, bool isPublished) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedReIndexForContent(_backgroundTaskQueue, this, sender, isPublished)); + } + else + { + DeferedReIndexForContent.Execute(_backgroundTaskQueue, this, sender, isPublished); + } + } + + /// + public void ReIndexForMedia(IMedia sender, bool isPublished) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedReIndexForMedia(_backgroundTaskQueue, this, sender, isPublished)); + } + else + { + DeferedReIndexForMedia.Execute(_backgroundTaskQueue, this, sender, isPublished); + } + } + + /// + public void ReIndexForMember(IMember member) + { + var actions = DeferedActions.Get(_scopeProvider); + if (actions != null) + { + actions.Add(new DeferedReIndexForMember(_backgroundTaskQueue, this, member)); + } + else + { + DeferedReIndexForMember.Execute(_backgroundTaskQueue, this, member); + } + } + + /// + public void DeleteDocumentsForContentTypes(IReadOnlyCollection removedContentTypes) + { + const int pageSize = 500; + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + foreach (var id in removedContentTypes) + { + foreach (var index in _examineManager.Indexes.OfType()) + { + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = index.Searcher + .CreateQuery() + .Field("nodeType", id.ToInvariantString()) + .Execute(QueryOptions.SkipTake(page * pageSize, pageSize)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach (ISearchResult item in paged) + { + if (int.TryParse(item.Id, out int contentId)) + { + DeleteIndexForEntity(contentId, false); + } + } + + page++; + } + } + } + } + + #region Deferred Actions + private class DeferedActions + { + private readonly List _actions = new List(); + + public static DeferedActions Get(IScopeProvider scopeProvider) + { + IScopeContext scopeContext = scopeProvider.Context; + + return scopeContext?.Enlist("examineEvents", + () => new DeferedActions(), // creator + (completed, actions) => // action + { + if (completed) + { + actions.Execute(); + } + }, EnlistPriority); + } + + public void Add(DeferedAction action) => _actions.Add(action); + + private void Execute() + { + foreach (DeferedAction action in _actions) + { + action.Execute(); + } + } + } + + /// + /// An action that will execute at the end of the Scope being completed + /// + private abstract class DeferedAction + { + public virtual void Execute() + { } + } + + /// + /// Re-indexes an item on a background thread + /// + private class DeferedReIndexForContent : DeferedAction + { + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler; + private readonly IContent _content; + private readonly bool _isPublished; + + public DeferedReIndexForContent(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IContent content, bool isPublished) + { + _backgroundTaskQueue = backgroundTaskQueue; + _examineUmbracoIndexingHandler = examineUmbracoIndexingHandler; + _content = content; + _isPublished = isPublished; + } + + public override void Execute() => Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _content, _isPublished); + + public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IContent content, bool isPublished) + => backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => + { + using IScope scope = examineUmbracoIndexingHandler._scopeProvider.CreateScope(autoComplete: true); + + // for content we have a different builder for published vs unpublished + // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published + var builders = new Dictionary>> + { + [true] = new Lazy>(() => examineUmbracoIndexingHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()), + [false] = new Lazy>(() => examineUmbracoIndexingHandler._contentValueSetBuilder.GetValueSets(content).ToList()) + }; + + // This is only for content - so only index items for IUmbracoContentIndex (to exlude members) + foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) + .Where(x => x.EnableDefaultEventHandler)) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.CompletedTask; + } + + List valueSet = builders[index.PublishedValuesOnly].Value; + index.IndexItems(valueSet); + } + + return Task.CompletedTask; + }); + } + + /// + /// Re-indexes an item on a background thread + /// + private class DeferedReIndexForMedia : DeferedAction + { + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler; + private readonly IMedia _media; + private readonly bool _isPublished; + + public DeferedReIndexForMedia(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMedia media, bool isPublished) + { + _backgroundTaskQueue = backgroundTaskQueue; + _examineUmbracoIndexingHandler = examineUmbracoIndexingHandler; + _media = media; + _isPublished = isPublished; + } + + public override void Execute() => Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _media, _isPublished); + + public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMedia media, bool isPublished) => + // perform the ValueSet lookup on a background thread + backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => + { + using IScope scope = examineUmbracoIndexingHandler._scopeProvider.CreateScope(autoComplete: true); + + var valueSet = examineUmbracoIndexingHandler._mediaValueSetBuilder.GetValueSets(media).ToList(); + + // This is only for content - so only index items for IUmbracoContentIndex (to exlude members) + foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } + + return Task.CompletedTask; + }); + } + + /// + /// Re-indexes an item on a background thread + /// + private class DeferedReIndexForMember : DeferedAction + { + private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler; + private readonly IMember _member; + private readonly IBackgroundTaskQueue _backgroundTaskQueue; + + public DeferedReIndexForMember(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMember member) + { + _examineUmbracoIndexingHandler = examineUmbracoIndexingHandler; + _member = member; + _backgroundTaskQueue = backgroundTaskQueue; + } + + public override void Execute() => Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _member); + + public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMember member) => + // perform the ValueSet lookup on a background thread + backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => + { + using IScope scope = examineUmbracoIndexingHandler._scopeProvider.CreateScope(autoComplete: true); + + var valueSet = examineUmbracoIndexingHandler._memberValueSetBuilder.GetValueSets(member).ToList(); + + // only process for IUmbracoMemberIndex (not content indexes) + foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType() + //filter the indexers + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } + + return Task.CompletedTask; + }); + } + + private class DeferedDeleteIndex : DeferedAction + { + private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler; + private readonly int _id; + private readonly bool _keepIfUnpublished; + + public DeferedDeleteIndex(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, int id, bool keepIfUnpublished) + { + _examineUmbracoIndexingHandler = examineUmbracoIndexingHandler; + _id = id; + _keepIfUnpublished = keepIfUnpublished; + } + + public override void Execute() => Execute(_examineUmbracoIndexingHandler, _id, _keepIfUnpublished); + + public static void Execute(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, int id, bool keepIfUnpublished) + { + var strId = id.ToString(CultureInfo.InvariantCulture); + foreach (var index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType() + .Where(x => x.PublishedValuesOnly || !keepIfUnpublished) + .Where(x => x.EnableDefaultEventHandler)) + { + index.DeleteFromIndex(strId); + } + } + } + #endregion + } +} diff --git a/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs index 8c05926483..2ff01c51dc 100644 --- a/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/GenericIndexDiagnostics.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Examine; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Composing; @@ -15,12 +16,9 @@ namespace Umbraco.Cms.Infrastructure.Examine public class GenericIndexDiagnostics : IIndexDiagnostics { private readonly IIndex _index; - private static readonly string[] IgnoreProperties = { "Description" }; + private static readonly string[] s_ignoreProperties = { "Description" }; - public GenericIndexDiagnostics(IIndex index) - { - _index = index; - } + public GenericIndexDiagnostics(IIndex index) => _index = index; public int DocumentCount => -1; //unknown @@ -33,8 +31,7 @@ namespace Umbraco.Cms.Infrastructure.Examine try { - var searcher = _index.GetSearcher(); - var result = searcher.Search("test"); + var result = _index.Searcher.Search("test"); return Attempt.Succeed(); //if we can search we'll assume it's healthy } catch (Exception e) @@ -43,6 +40,10 @@ namespace Umbraco.Cms.Infrastructure.Examine } } + public long GetDocumentCount() => -1L; + + public IEnumerable GetFieldNames() => Enumerable.Empty(); + public IReadOnlyDictionary Metadata { get @@ -50,7 +51,7 @@ namespace Umbraco.Cms.Infrastructure.Examine var result = new Dictionary(); var props = TypeHelper.CachedDiscoverableProperties(_index.GetType(), mustWrite: false) - .Where(x => IgnoreProperties.InvariantContains(x.Name) == false) + .Where(x => s_ignoreProperties.InvariantContains(x.Name) == false) .OrderBy(x => x.Name); foreach (var p in props) diff --git a/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs b/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs deleted file mode 100644 index aadaa00f46..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IIndexCreator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using Examine; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// Creates 's - /// - public interface IIndexCreator - { - IEnumerable Create(); - } -} diff --git a/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs index a4e1c0ca4f..716b7731eb 100644 --- a/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs +++ b/src/Umbraco.Infrastructure/Examine/IIndexDiagnostics.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using System.Threading.Tasks; +using Examine; using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Examine @@ -7,18 +9,8 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// Exposes diagnostic information about an index /// - public interface IIndexDiagnostics + public interface IIndexDiagnostics : IIndexStats { - /// - /// The number of documents in the index - /// - int DocumentCount { get; } - - /// - /// The number of fields in the index - /// - int FieldCount { get; } - /// /// If the index can be open/read /// diff --git a/src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs new file mode 100644 index 0000000000..127a20d685 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/IIndexRebuilder.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Examine; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + public interface IIndexRebuilder + { + bool CanRebuild(string indexName); + void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true); + void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true); + } +} diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoContentIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoContentIndex.cs index 63bbfb047a..0735cc255d 100644 --- a/src/Umbraco.Infrastructure/Examine/IUmbracoContentIndex.cs +++ b/src/Umbraco.Infrastructure/Examine/IUmbracoContentIndex.cs @@ -5,8 +5,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// Marker interface for indexes of Umbraco content /// - public interface IUmbracoContentIndex : IIndex + public interface IUmbracoContentIndex : IUmbracoIndex { - bool PublishedValuesOnly { get; } } } diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs index 8dfdf6d812..f2221e5c91 100644 --- a/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs +++ b/src/Umbraco.Infrastructure/Examine/IUmbracoIndex.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Examine; namespace Umbraco.Cms.Infrastructure.Examine @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// A Marker interface for defining an Umbraco indexer /// - public interface IUmbracoIndex : IIndex + public interface IUmbracoIndex : IIndex, IIndexStats { /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically @@ -22,11 +22,5 @@ namespace Umbraco.Cms.Infrastructure.Examine /// * non-published Variants /// bool PublishedValuesOnly { get; } - - /// - /// Returns a list of all indexed fields - /// - /// - IEnumerable GetFields(); } } diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs deleted file mode 100644 index df61901dba..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IUmbracoIndexesCreator.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Umbraco.Cms.Infrastructure.Examine -{ - /// - /// - /// Used to create the Umbraco indexes - /// - public interface IUmbracoIndexesCreator : IIndexCreator - { - } -} diff --git a/src/Umbraco.Infrastructure/Examine/IUmbracoMemberIndex.cs b/src/Umbraco.Infrastructure/Examine/IUmbracoMemberIndex.cs index d09c8518fa..7dc07688a9 100644 --- a/src/Umbraco.Infrastructure/Examine/IUmbracoMemberIndex.cs +++ b/src/Umbraco.Infrastructure/Examine/IUmbracoMemberIndex.cs @@ -2,7 +2,7 @@ using Examine; namespace Umbraco.Cms.Infrastructure.Examine { - public interface IUmbracoMemberIndex : IIndex + public interface IUmbracoMemberIndex : IUmbracoIndex { } diff --git a/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs b/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs index ca2e732071..a60a373e65 100644 --- a/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs +++ b/src/Umbraco.Infrastructure/Examine/IndexDiagnosticsFactory.cs @@ -1,4 +1,4 @@ -using Examine; +using Examine; namespace Umbraco.Cms.Infrastructure.Examine { @@ -9,8 +9,11 @@ namespace Umbraco.Cms.Infrastructure.Examine { public virtual IIndexDiagnostics Create(IIndex index) { - if (!(index is IIndexDiagnostics indexDiag)) + if (index is not IIndexDiagnostics indexDiag) + { indexDiag = new GenericIndexDiagnostics(index); + } + return indexDiag; } } diff --git a/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs index 2feac0710a..d32470d875 100644 --- a/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/IndexPopulator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; using Umbraco.Cms.Core.Collections; diff --git a/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs deleted file mode 100644 index 9e4fe6fed0..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IndexRebuilder.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Logging; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - - /// - /// Utility to rebuild all indexes ensuring minimal data queries - /// - public class IndexRebuilder - { - private readonly IProfilingLogger _profilingLogger; - private readonly ILogger _logger; - private readonly IEnumerable _populators; - public IExamineManager ExamineManager { get; } - - public IndexRebuilder(IProfilingLogger profilingLogger , ILogger logger, IExamineManager examineManager, IEnumerable populators) - { - _profilingLogger = profilingLogger ; - _populators = populators; - _logger = logger; - ExamineManager = examineManager; - } - - public bool CanRebuild(IIndex index) - { - return _populators.Any(x => x.IsRegistered(index)); - } - - public void RebuildIndex(string indexName) - { - if (!ExamineManager.TryGetIndex(indexName, out var index)) - throw new InvalidOperationException($"No index found with name {indexName}"); - index.CreateIndex(); // clear the index - foreach (var populator in _populators) - { - populator.Populate(index); - } - } - - public void RebuildIndexes(bool onlyEmptyIndexes) - { - var indexes = (onlyEmptyIndexes - ? ExamineManager.Indexes.Where(x => !x.IndexExists()) - : ExamineManager.Indexes).ToArray(); - - if (indexes.Length == 0) return; - - OnRebuildingIndexes(new IndexRebuildingEventArgs(indexes)); - - foreach (var index in indexes) - { - index.CreateIndex(); // clear the index - } - - // run each populator over the indexes - foreach(var populator in _populators) - { - try - { - populator.Populate(indexes); - } - catch (Exception e) - { - _logger.LogError(e, "Index populating failed for populator {Populator}", populator.GetType()); - } - } - } - - /// - /// Event raised when indexes are being rebuilt - /// - public event EventHandler RebuildingIndexes; - - private void OnRebuildingIndexes(IndexRebuildingEventArgs args) => RebuildingIndexes?.Invoke(this, args); - } -} diff --git a/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs b/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs deleted file mode 100644 index fbe3dbcbe3..0000000000 --- a/src/Umbraco.Infrastructure/Examine/IndexRebuildingEventArgs.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using Examine; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class IndexRebuildingEventArgs : EventArgs - { - public IndexRebuildingEventArgs(IEnumerable indexes) - { - Indexes = indexes; - } - - /// - /// The indexes being rebuilt - /// - public IEnumerable Indexes { get; } - } -} diff --git a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs index 429285fa85..9f6e33f8dd 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaIndexPopulator.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -11,6 +12,7 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public class MediaIndexPopulator : IndexPopulator { + private readonly ILogger _logger; private readonly int? _parentId; private readonly IMediaService _mediaService; private readonly IValueSetBuilder _mediaValueSetBuilder; @@ -20,8 +22,8 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// /// - public MediaIndexPopulator(IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) - : this(null, mediaService, mediaValueSetBuilder) + public MediaIndexPopulator(ILogger logger, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + : this(logger, null, mediaService, mediaValueSetBuilder) { } @@ -31,8 +33,9 @@ namespace Umbraco.Cms.Infrastructure.Examine /// /// /// - public MediaIndexPopulator(int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + public MediaIndexPopulator(ILogger logger, int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) { + _logger = logger; _parentId = parentId; _mediaService = mediaService; _mediaValueSetBuilder = mediaValueSetBuilder; @@ -40,7 +43,11 @@ namespace Umbraco.Cms.Infrastructure.Examine protected override void PopulateIndexes(IReadOnlyList indexes) { - if (indexes.Count == 0) return; + if (indexes.Count == 0) + { + _logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator."); + return; + } const int pageSize = 10000; var pageIndex = 0; diff --git a/src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs b/src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs deleted file mode 100644 index e84fb96a74..0000000000 --- a/src/Umbraco.Infrastructure/Examine/NoopUmbracoIndexesCreator.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Examine; - -namespace Umbraco.Cms.Infrastructure.Examine -{ - public class NoopUmbracoIndexesCreator : IUmbracoIndexesCreator - { - public IEnumerable Create() - { - return Enumerable.Empty(); - } - } -} diff --git a/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs index 4b55337670..f9ccaffdbc 100644 --- a/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs +++ b/src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs @@ -1,4 +1,5 @@ -using Umbraco.Cms.Core.Services; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Infrastructure.Examine @@ -13,8 +14,8 @@ namespace Umbraco.Cms.Infrastructure.Examine /// public class PublishedContentIndexPopulator : ContentIndexPopulator { - public PublishedContentIndexPopulator(IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IPublishedContentValueSetBuilder contentValueSetBuilder) : - base(true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) + public PublishedContentIndexPopulator(ILogger logger, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IPublishedContentValueSetBuilder contentValueSetBuilder) : + base(logger, true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder) { } } diff --git a/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs b/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs new file mode 100644 index 0000000000..05b016dbb6 --- /dev/null +++ b/src/Umbraco.Infrastructure/Examine/RebuildOnStartupHandler.cs @@ -0,0 +1,72 @@ +using System; +using System.Threading; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Infrastructure.Examine +{ + /// + /// Handles how the indexes are rebuilt on startup + /// + /// + /// On the first HTTP request this will rebuild the Examine indexes if they are empty. + /// If it is a cold boot, they are all rebuilt. + /// + public sealed class RebuildOnStartupHandler : INotificationHandler + { + private readonly ISyncBootStateAccessor _syncBootStateAccessor; + private readonly ExamineIndexRebuilder _backgroundIndexRebuilder; + private readonly IRuntimeState _runtimeState; + + // These must be static because notification handlers are transient. + // this does unfortunatley mean that one RebuildOnStartupHandler instance + // will be created for each front-end request even though we only use the first one. + // TODO: Is there a better way to acheive this without allocating? We cannot remove + // a handler from the notification system. It's not a huge deal but would be better + // with less objects. + private static bool _isReady; + private static bool _isReadSet; + private static object _isReadyLock; + + public RebuildOnStartupHandler( + ISyncBootStateAccessor syncBootStateAccessor, + ExamineIndexRebuilder backgroundIndexRebuilder, + IRuntimeState runtimeState) + { + _syncBootStateAccessor = syncBootStateAccessor; + _backgroundIndexRebuilder = backgroundIndexRebuilder; + _runtimeState = runtimeState; + } + + /// + /// On first http request schedule an index rebuild for any empty indexes (or all if it's a cold boot) + /// + /// + public void Handle(UmbracoRequestBeginNotification notification) + { + if (_runtimeState.Level < RuntimeLevel.Run) + { + return; + } + + LazyInitializer.EnsureInitialized( + ref _isReady, + ref _isReadSet, + ref _isReadyLock, + () => + { + SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState(); + + _backgroundIndexRebuilder.RebuildIndexes( + // if it's not a cold boot, only rebuild empty ones + bootState != SyncBootState.ColdBoot, + TimeSpan.FromMinutes(1)); + + return true; + }); + } + } +} diff --git a/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs b/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs index 90f012f08a..0d341d1d9b 100644 --- a/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs +++ b/src/Umbraco.Infrastructure/Examine/UmbracoExamineExtensions.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Examine; using Examine.Search; using Umbraco.Cms.Infrastructure.Examine; @@ -16,8 +17,6 @@ namespace Umbraco.Extensions /// internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled); - - //TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression /// @@ -28,14 +27,19 @@ namespace Umbraco.Extensions /// public static IEnumerable GetCultureFields(this IUmbracoIndex index, string culture) { - var allFields = index.GetFields(); - // ReSharper disable once LoopCanBeConvertedToQuery + IEnumerable allFields = index.GetFieldNames(); + + var results = new List(); foreach (var field in allFields) { var match = CultureIsoCodeFieldNameMatchExpression.Match(field); if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value)) - yield return field; + { + results.Add(field); + } } + + return results; } /// @@ -46,8 +50,8 @@ namespace Umbraco.Extensions /// public static IEnumerable GetCultureAndInvariantFields(this IUmbracoIndex index, string culture) { - var allFields = index.GetFields(); - // ReSharper disable once LoopCanBeConvertedToQuery + IEnumerable allFields = index.GetFieldNames(); + foreach (var field in allFields) { var match = CultureIsoCodeFieldNameMatchExpression.Match(field); @@ -59,7 +63,6 @@ namespace Umbraco.Extensions { yield return field; //matches no culture field (invariant) } - } } diff --git a/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs b/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs index db933fec31..c15a37855e 100644 --- a/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs +++ b/src/Umbraco.Infrastructure/HostedServices/QueuedHostedService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -35,8 +35,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices { while (!stoppingToken.IsCancellationRequested) { - var workItem = - await TaskQueue.DequeueAsync(stoppingToken); + Func workItem = await TaskQueue.DequeueAsync(stoppingToken); try { diff --git a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs index 1ec13f334e..70dcb3a04e 100644 --- a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs +++ b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs @@ -47,12 +47,26 @@ namespace Umbraco.Cms.Infrastructure.HostedServices /// Executes the task. /// /// The task state. - public async void ExecuteAsync(object state) => - // Delegate work to method returning a task, that can be called and asserted in a unit test. - // Without this there can be behaviour where tests pass, but an error within them causes the test - // running process to crash. - // Hat-tip: https://stackoverflow.com/a/14207615/489433 - await PerformExecuteAsync(state); + public async void ExecuteAsync(object state) + { + try + { + // First, stop the timer, we do not want tasks to execute in parallel + _timer?.Change(Timeout.Infinite, 0); + + // Delegate work to method returning a task, that can be called and asserted in a unit test. + // Without this there can be behaviour where tests pass, but an error within them causes the test + // running process to crash. + // Hat-tip: https://stackoverflow.com/a/14207615/489433 + await PerformExecuteAsync(state); + } + finally + { + // Resume now that the task is complete - Note we use period in both because we don't want to execute again after the delay. + // So first execution is after _delay, and the we wait _period between each + _timer?.Change((int)_period.TotalMilliseconds, (int)_period.TotalMilliseconds); + } + } public abstract Task PerformExecuteAsync(object state); diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs index fe540b73dd..6eab3a60bc 100644 --- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs +++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs @@ -52,24 +52,32 @@ namespace Umbraco.Cms.Infrastructure.HostedServices try { - // Send data to LIVE telemetry - s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/"); + + if (s_httpClient.BaseAddress is null) + { + // Send data to LIVE telemetry + s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/"); + + // Set a low timeout - no need to use a larger default timeout for this POST request + s_httpClient.Timeout = new TimeSpan(0, 0, 1); #if DEBUG - // Send data to DEBUG telemetry service - s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/"); + // Send data to DEBUG telemetry service + s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/"); + + + #endif + } + s_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); using (var request = new HttpRequestMessage(HttpMethod.Post, "installs/")) { - var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = _umbracoVersion.SemanticVersion.ToSemanticString() }; + var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild() }; request.Content = new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json"); //CONTENT-TYPE header - // Set a low timeout - no need to use a larger default timeout for this POST request - s_httpClient.Timeout = new TimeSpan(0, 0, 1); - // Make a HTTP Post to telemetry service // https://telemetry.umbraco.com/installs/ // Fire & Forget, do not need to know if its a 200, 500 etc diff --git a/src/Umbraco.Infrastructure/Manifest/DataEditorConverter.cs b/src/Umbraco.Infrastructure/Manifest/DataEditorConverter.cs index 515709cfe5..3ef5d443e4 100644 --- a/src/Umbraco.Infrastructure/Manifest/DataEditorConverter.cs +++ b/src/Umbraco.Infrastructure/Manifest/DataEditorConverter.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; @@ -17,10 +18,8 @@ namespace Umbraco.Cms.Core.Manifest /// internal class DataEditorConverter : JsonReadConverter { - private readonly ILoggerFactory _loggerFactory; + private readonly IDataValueEditorFactory _dataValueEditorFactory; private readonly IIOHelper _ioHelper; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly ILocalizedTextService _textService; private readonly IShortStringHelper _shortStringHelper; private readonly IJsonSerializer _jsonSerializer; @@ -29,18 +28,14 @@ namespace Umbraco.Cms.Core.Manifest /// Initializes a new instance of the class. /// public DataEditorConverter( - ILoggerFactory loggerFactory, + IDataValueEditorFactory dataValueEditorFactory, IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService textService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer) { - _loggerFactory = loggerFactory; + _dataValueEditorFactory = dataValueEditorFactory; _ioHelper = ioHelper; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _textService = textService; _shortStringHelper = shortStringHelper; _jsonSerializer = jsonSerializer; @@ -71,7 +66,7 @@ namespace Umbraco.Cms.Core.Manifest type = EditorType.MacroParameter; } - return new DataEditor(_loggerFactory, _dataTypeService, _localizationService, _textService, _shortStringHelper, _jsonSerializer, type); + return new DataEditor(_dataValueEditorFactory, type); } /// @@ -98,7 +93,7 @@ namespace Umbraco.Cms.Core.Manifest // explicitly assign a value editor of type ValueEditor // (else the deserializer will try to read it before setting it) // (and besides it's an interface) - target.ExplicitValueEditor = new DataValueEditor(_dataTypeService, _localizationService, _textService, _shortStringHelper, _jsonSerializer); + target.ExplicitValueEditor = new DataValueEditor(_textService, _shortStringHelper, _jsonSerializer); // in the manifest, validators are a simple dictionary eg // { @@ -177,7 +172,7 @@ namespace Umbraco.Cms.Core.Manifest if (jobject.Property("view") != null) { // explicitly assign a value editor of type ParameterValueEditor - target.ExplicitValueEditor = new DataValueEditor(_dataTypeService, _localizationService, _textService, _shortStringHelper, _jsonSerializer); + target.ExplicitValueEditor = new DataValueEditor(_textService, _shortStringHelper, _jsonSerializer); // move the 'view' property jobject["editor"] = new JObject { ["view"] = jobject["view"] }; diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 5c37e0b2d9..9a44121f0b 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -21,18 +21,17 @@ namespace Umbraco.Cms.Core.Manifest /// public class ManifestParser : IManifestParser { - private readonly ILoggerFactory _loggerFactory; + private readonly IIOHelper _ioHelper; private readonly IHostingEnvironment _hostingEnvironment; private readonly IJsonSerializer _jsonSerializer; private readonly ILocalizedTextService _localizedTextService; private readonly IShortStringHelper _shortStringHelper; + private readonly IDataValueEditorFactory _dataValueEditorFactory; private static readonly string Utf8Preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); private readonly IAppPolicyCache _cache; private readonly ILogger _logger; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly ManifestValueValidatorCollection _validators; private readonly ManifestFilterCollection _filters; @@ -46,43 +45,36 @@ namespace Umbraco.Cms.Core.Manifest ManifestValueValidatorCollection validators, ManifestFilterCollection filters, ILogger logger, - ILoggerFactory loggerFactory, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, - IDataTypeService dataTypeService, - ILocalizationService localizationService, IJsonSerializer jsonSerializer, ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper) - : this(appCaches, validators, filters, "~/App_Plugins", logger, loggerFactory, ioHelper, hostingEnvironment, dataTypeService, localizationService) + IShortStringHelper shortStringHelper, + IDataValueEditorFactory dataValueEditorFactory) + : this(appCaches, validators, filters, "~/App_Plugins", logger, ioHelper, hostingEnvironment) { - _loggerFactory = loggerFactory; _jsonSerializer = jsonSerializer; _localizedTextService = localizedTextService; _shortStringHelper = shortStringHelper; + _dataValueEditorFactory = dataValueEditorFactory; } /// /// Initializes a new instance of the class. /// - private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, IDataTypeService dataTypeService, ILocalizationService localizationService) + private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment) { if (appCaches == null) throw new ArgumentNullException(nameof(appCaches)); _cache = appCaches.RuntimeCache; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _validators = validators ?? throw new ArgumentNullException(nameof(validators)); _filters = filters ?? throw new ArgumentNullException(nameof(filters)); if (path == null) throw new ArgumentNullException(nameof(path)); if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(path)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _ioHelper = ioHelper; _hostingEnvironment = hostingEnvironment; Path = path; - - } public string Path @@ -197,7 +189,7 @@ namespace Umbraco.Cms.Core.Manifest if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text)); var manifest = JsonConvert.DeserializeObject(text, - new DataEditorConverter(_loggerFactory, _ioHelper, _dataTypeService, _localizationService, _localizedTextService, _shortStringHelper, _jsonSerializer), + new DataEditorConverter(_dataValueEditorFactory, _ioHelper, _localizedTextService, _shortStringHelper, _jsonSerializer), new ValueValidatorConverter(_validators), new DashboardAccessRuleConverter()); diff --git a/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs b/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs index 091d3c395b..f8dc089a0d 100644 --- a/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs +++ b/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs @@ -1,8 +1,8 @@ using System; -using System.Drawing; using System.IO; +using SixLabors.ImageSharp; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Media; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Infrastructure.Media { @@ -34,8 +34,12 @@ namespace Umbraco.Cms.Infrastructure.Media // we have no choice but to try to read in via GDI try { - // TODO: We should be using ImageSharp for this - using (var image = Image.FromStream(stream)) + if (stream.CanRead && stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + using (var image = Image.Load(stream)) { var fileWidth = image.Width; var fileHeight = image.Height; diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index 30759ae789..248325a30e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -115,7 +115,11 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelDateTime, 37, Cms.Core.Constants.DataTypes.Guids.LabelDateTime, "Label (datetime)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelTime, 38, Cms.Core.Constants.DataTypes.Guids.LabelTime, "Label (time)"); InsertDataTypeNodeDto(Cms.Core.Constants.DataTypes.LabelDecimal, 39, Cms.Core.Constants.DataTypes.Guids.LabelDecimal, "Label (decimal)"); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Upload, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Upload}", SortOrder = 34, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadGuid, Text = "Upload", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Upload, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Upload}", SortOrder = 34, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadGuid, Text = "Upload File", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadVideo, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadVideo}", SortOrder = 35, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadVideoGuid, Text = "Upload Video", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadAudio, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadAudio}", SortOrder = 36, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadAudioGuid, Text = "Upload Audio", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadArticle, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadArticle}", SortOrder = 37, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadArticleGuid, Text = "Upload Article", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.UploadVectorGraphics}", SortOrder = 38, UniqueId = Cms.Core.Constants.DataTypes.Guids.UploadVectorGraphicsGuid, Text = "Upload Vector Graphics", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Textarea, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Textarea}", SortOrder = 33, UniqueId = Cms.Core.Constants.DataTypes.Guids.TextareaGuid, Text = "Textarea", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Textbox, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Textbox}", SortOrder = 32, UniqueId = Cms.Core.Constants.DataTypes.Guids.TextstringGuid, Text = "Textstring", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.RichtextEditor, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.RichtextEditor}", SortOrder = 4, UniqueId = Cms.Core.Constants.DataTypes.Guids.RichtextEditorGuid, Text = "Richtext editor", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); @@ -134,6 +138,12 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1031, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1031", SortOrder = 2, UniqueId = new Guid("f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d"), Text = Cms.Core.Constants.Conventions.MediaTypes.Folder, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1032, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1032", SortOrder = 2, UniqueId = new Guid("cc07b313-0843-4aa8-bbda-871c8da728c8"), Text = Cms.Core.Constants.Conventions.MediaTypes.Image, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Cms.Core.Constants.Conventions.MediaTypes.File, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1034, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1034", SortOrder = 2, UniqueId = new Guid("f6c515bb-653c-4bdc-821c-987729ebe327"), Text = Cms.Core.Constants.Conventions.MediaTypes.Video, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1035, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1035", SortOrder = 2, UniqueId = new Guid("a5ddeee0-8fd8-4cee-a658-6f1fcdb00de3"), Text = Cms.Core.Constants.Conventions.MediaTypes.Audio, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("a43e3414-9599-4230-a7d3-943a21b20122"), Text = Cms.Core.Constants.Conventions.MediaTypes.Article, NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1037, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1037", SortOrder = 2, UniqueId = new Guid("c4b1efcf-a9d5-41c4-9621-e9d273b52a9c"), Text = "Vector Graphics (SVG)", NodeObjectType = Cms.Core.Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.Tags, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.Tags}", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = Cms.Core.Constants.DataTypes.ImageCropper, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = $"-1,{Cms.Core.Constants.DataTypes.ImageCropper}", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Cms.Core.Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = Cms.Core.Constants.ObjectTypes.MemberType, CreateDate = DateTime.Now }); @@ -141,9 +151,15 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install //New UDI pickers with newer Ids _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "(Obsolete) Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "(Obsolete) Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + } private void CreateLockData() @@ -168,6 +184,11 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Cms.Core.Constants.Conventions.MediaTypes.Folder, Icon = Cms.Core.Constants.Icons.MediaFolder, Thumbnail = Cms.Core.Constants.Icons.MediaFolder, IsContainer = false, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 533, NodeId = 1032, Alias = Cms.Core.Constants.Conventions.MediaTypes.Image, Icon = Cms.Core.Constants.Icons.MediaImage, Thumbnail = Cms.Core.Constants.Icons.MediaImage, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 534, NodeId = 1033, Alias = Cms.Core.Constants.Conventions.MediaTypes.File, Icon = Cms.Core.Constants.Icons.MediaFile, Thumbnail = Cms.Core.Constants.Icons.MediaFile, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 540, NodeId = 1034, Alias = Cms.Core.Constants.Conventions.MediaTypes.Video, Icon = Cms.Core.Constants.Icons.MediaVideo, Thumbnail = Cms.Core.Constants.Icons.MediaVideo, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 541, NodeId = 1035, Alias = Cms.Core.Constants.Conventions.MediaTypes.Audio, Icon = Cms.Core.Constants.Icons.MediaAudio, Thumbnail = Cms.Core.Constants.Icons.MediaAudio, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 542, NodeId = 1036, Alias = Cms.Core.Constants.Conventions.MediaTypes.Article, Icon = Cms.Core.Constants.Icons.MediaArticle, Thumbnail = Cms.Core.Constants.Icons.MediaArticle, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 543, NodeId = 1037, Alias = Cms.Core.Constants.Conventions.MediaTypes.VectorGraphics, Icon = Cms.Core.Constants.Icons.MediaVectorGraphics, Thumbnail = Cms.Core.Constants.Icons.MediaVectorGraphics, AllowAtRoot = true, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentType, "pk", false, new ContentTypeDto { PrimaryKey = 531, NodeId = 1044, Alias = Cms.Core.Constants.Conventions.MemberTypes.DefaultAlias, Icon = Cms.Core.Constants.Icons.Member, Thumbnail = Cms.Core.Constants.Icons.Member, Variations = (byte) ContentVariation.Nothing }); } @@ -215,20 +236,41 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install { _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Image) }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.File) }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 52, ContentTypeNodeId = 1034, Text = "Video", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Video) }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 53, ContentTypeNodeId = 1035, Text = "Audio", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Audio) }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 54, ContentTypeNodeId = 1036, Text = "Article", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Article) }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 55, ContentTypeNodeId = 1037, Text = "Vector Graphics", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.VectorGraphics) }); //membership property group _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyTypeGroup, "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Cms.Core.Constants.PropertyTypeGroups.Membership) }); } private void CreatePropertyTypeData() { - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.ImageCropper, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Upload image", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.ImageCropper, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Image", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 7, UniqueId = 7.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 8, UniqueId = 8.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in pixels", Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 9, UniqueId = 9.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 10, UniqueId = 10.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Upload, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Upload, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "File", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 40, UniqueId = 40.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadVideo, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Video", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 41, UniqueId = 41.ToGuid(), DataTypeId = -92, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 42, UniqueId = 42.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1034, PropertyTypeGroupId = 52, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 43, UniqueId = 43.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadAudio, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Audio", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 44, UniqueId = 44.ToGuid(), DataTypeId = -92, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 45, UniqueId = 45.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1035, PropertyTypeGroupId = 53, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 46, UniqueId = 46.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadArticle, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Article", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 47, UniqueId = 47.ToGuid(), DataTypeId = -92, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 48, UniqueId = 48.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1036, PropertyTypeGroupId = 54, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 49, UniqueId = 49.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.File, Name = "Vector Graphics", SortOrder = 0, Mandatory = true, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 50, UniqueId = 50.ToGuid(), DataTypeId = -92, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 51, UniqueId = 51.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelBigint, ContentTypeId = 1037, PropertyTypeGroupId = 55, Alias = Cms.Core.Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = "in bytes", Variations = (byte) ContentVariation.Nothing }); + //membership property types _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.Textarea, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.Comments, Name = Cms.Core.Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = Cms.Core.Constants.DataTypes.LabelInt, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Cms.Core.Constants.Conventions.Member.FailedPasswordAttempts, Name = Cms.Core.Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing }); @@ -249,6 +291,10 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1031 }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1032 }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1033 }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1034 }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1035 }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1036 }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.ContentChildType, "Id", false, new ContentTypeAllowedContentTypeDto { Id = 1031, AllowedId = 1037 }); } private void CreateDataTypeData() @@ -312,6 +358,64 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1049, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker, DbType = "Ntext", Configuration = "{\"multiPicker\":1}" }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 1050, EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MultiUrlPicker, DbType = "Ntext" }); + + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadVideo, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp4\"}, {\"id\":1, \"value\":\"webm\"}, {\"id\":2, \"value\":\"ogv\"}]}" + }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadAudio, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"mp3\"}, {\"id\":1, \"value\":\"weba\"}, {\"id\":2, \"value\":\"oga\"}, {\"id\":3, \"value\":\"opus\"}]}" + }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadArticle, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"pdf\"}, {\"id\":1, \"value\":\"docx\"}, {\"id\":2, \"value\":\"doc\"}]}" + }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto + { + NodeId = Cms.Core.Constants.DataTypes.UploadVectorGraphics, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.UploadField, + DbType = "Nvarchar", + Configuration = "{\"fileExtensions\":[{\"id\":0, \"value\":\"svg\"}]}" + }); + + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1051, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" + }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1052, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"multiple\": true}" + }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1053, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"filter\":\"" + Cms.Core.Constants.Conventions.MediaTypes.Image + "\", \"multiple\": false, \"validationLimit\":{\"min\":0,\"max\":1}}" + }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { + NodeId = 1054, + EditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.MediaPicker3, + DbType = "Ntext", + Configuration = "{\"filter\":\"" + Cms.Core.Constants.Conventions.MediaTypes.Image + "\", \"multiple\": true}" + }); } private void CreateRelationTypeData() diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs index 61c32ea1d0..4052a8ae56 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs @@ -5,7 +5,8 @@ using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Infrastructure.Migrations.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Infrastructure.Migrations.Notifications; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; using Umbraco.Cms.Infrastructure.Persistence.Dtos; @@ -319,7 +320,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install #region Notifications /// - /// Publishes the notification. + /// Publishes the notification. /// /// Cancelable notification marking the creation having begun. internal virtual void FireBeforeCreation(DatabaseSchemaCreatingNotification notification) => diff --git a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatedNotification.cs b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatedNotification.cs similarity index 74% rename from src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatedNotification.cs rename to src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatedNotification.cs index 4da6b5fabb..2c2888c19d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatedNotification.cs +++ b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatedNotification.cs @@ -1,6 +1,7 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; -namespace Umbraco.Cms.Infrastructure.Migrations.Events +namespace Umbraco.Cms.Infrastructure.Migrations.Notifications { internal class DatabaseSchemaCreatedNotification : StatefulNotification { diff --git a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatingNotification.cs b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatingNotification.cs similarity index 70% rename from src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatingNotification.cs rename to src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatingNotification.cs index 9ca079acbe..1be96c9a9a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Events/DatabaseSchemaCreatingNotification.cs +++ b/src/Umbraco.Infrastructure/Migrations/Notifications/DatabaseSchemaCreatingNotification.cs @@ -1,6 +1,7 @@ using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; -namespace Umbraco.Cms.Infrastructure.Migrations.Events +namespace Umbraco.Cms.Infrastructure.Migrations.Notifications { internal class DatabaseSchemaCreatingNotification : CancelableNotification { diff --git a/src/Umbraco.Infrastructure/Migrations/PostMigrations/RebuildPublishedSnapshot.cs b/src/Umbraco.Infrastructure/Migrations/PostMigrations/RebuildPublishedSnapshot.cs index ca39939ac9..bbf366882f 100644 --- a/src/Umbraco.Infrastructure/Migrations/PostMigrations/RebuildPublishedSnapshot.cs +++ b/src/Umbraco.Infrastructure/Migrations/PostMigrations/RebuildPublishedSnapshot.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.PostMigrations /// /// Initializes a new instance of the class. /// - public RebuildPublishedSnapshot(IPublishedSnapshotRebuilder rebuilder) + public RebuildPublishedSnapshot(IMigrationContext context, IPublishedSnapshotRebuilder rebuilder) { _rebuilder = rebuilder; } diff --git a/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs b/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs new file mode 100644 index 0000000000..e8d7e89522 --- /dev/null +++ b/src/Umbraco.Infrastructure/Models/MediaWithCrops.cs @@ -0,0 +1,17 @@ + + +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors.ValueConverters; + +namespace Umbraco.Core.Models +{ + /// + /// Model used in Razor Views for rendering + /// + public class MediaWithCrops + { + public IPublishedContent MediaItem { get; set; } + + public ImageCropperValue LocalCrops { get; set; } + } +} diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs b/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs similarity index 75% rename from src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs rename to src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs index 9ff03e4d45..8017cfbf8e 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/LiveModelsProvider.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/AutoModelsNotificationHandler.cs @@ -2,33 +2,39 @@ using System; using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Extensions; +using Umbraco.Cms.Core.Configuration; namespace Umbraco.Cms.Infrastructure.ModelsBuilder { - // supports LiveAppData - but not PureLive - public sealed class LiveModelsProvider : INotificationHandler, - INotificationHandler, + /// + /// Notification handlers used by . + /// + /// + /// supports mode but not mode. + /// + public sealed class AutoModelsNotificationHandler : INotificationHandler, + INotificationHandler, INotificationHandler, INotificationHandler { private static int s_req; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly ModelsBuilderSettings _config; private readonly ModelsGenerator _modelGenerator; private readonly ModelsGenerationError _mbErrors; private readonly IMainDom _mainDom; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public LiveModelsProvider( - ILogger logger, + public AutoModelsNotificationHandler( + ILogger logger, IOptions config, ModelsGenerator modelGenerator, ModelsGenerationError mbErrors, @@ -41,13 +47,13 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder _mainDom = mainDom; } - // we do not manage pure live here - internal bool IsEnabled => _config.ModelsMode.IsLiveNotPure(); + // we do not manage InMemory models here + internal bool IsEnabled => _config.ModelsMode.IsAutoNotInMemory(); /// - /// Handles the notification + /// Handles the notification /// - public void Handle(UmbracoApplicationStarting notification) => Install(); + public void Handle(UmbracoApplicationStartingNotification notification) => Install(); private void Install() { @@ -73,6 +79,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder } _logger.LogDebug("Requested to generate models."); + Interlocked.Exchange(ref s_req, 1); } @@ -112,7 +119,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder } } - public void Handle(UmbracoRequestEnd notification) + public void Handle(UmbracoRequestEndNotification notification) { if (IsEnabled && _mainDom.IsMainDom) { diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs index e5d1b9d51f..bb157e5c7a 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/Builder.cs @@ -93,12 +93,12 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building { TypeModel.MapModelTypes(_typeModels, ModelsNamespace); - var pureLive = Config.ModelsMode == ModelsMode.PureLive; + var isInMemoryMode = Config.ModelsMode == ModelsMode.InMemoryAuto; // for the first two of these two tests, - // always throw, even in purelive: cannot happen unless ppl start fidling with attributes to rename + // always throw, even in InMemory mode: cannot happen unless ppl start fidling with attributes to rename // things, and then they should pay attention to the generation error log - there's no magic here - // for the last one, don't throw in purelive, see comment + // for the last one, don't throw in InMemory mode, see comment // ensure we have no duplicates type names foreach (var xx in _typeModels.GroupBy(x => x.ClrName).Where(x => x.Count() > 1)) @@ -118,12 +118,12 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building { foreach (var xx in typeModel.Properties.Where(x => x.ClrName == typeModel.ClrName)) { - if (!pureLive) + if (!isInMemoryMode) throw new InvalidOperationException($"The model class for content type with alias \"{typeModel.Alias}\" is named \"{xx.ClrName}\"." + $" CSharp does not support using the same name for the property with alias \"{xx.Alias}\"." + " Consider using an attribute to assign a different name to the property."); - // for purelive, will we generate a commented out properties with an error message, + // in InMemory mode we generate commented out properties with an error message, // instead of throwing, because then it kills the sites and ppl don't understand why xx.AddError($"The class {typeModel.ClrName} cannot implement this property, because" + $" CSharp does not support naming the property with alias \"{xx.Alias}\" with the same name as content type with alias \"{typeModel.Alias}\"." diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs index fbb29e3ebd..30f5ecd945 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/PropertyModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Cms.Core.Configuration; namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building { @@ -46,7 +47,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building /// /// This should be null, unless something prevents the property from being /// generated, and then the value should explain what. This can be used to generate - /// commented out code eg in PureLive. + /// commented out code eg in mode. public List Errors; /// diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs index d9f37fd2bd..9084cc902f 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs @@ -195,7 +195,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder.Building sb.Append("\t\tprivate IPublishedValueFallback _publishedValueFallback;"); // write the ctor - sb.AppendFormat("\n\n\t\t// ctor\n\t\tpublic {0}(IPublished{1} content, IPublishedValueFallback publishedValueFallback)\n\t\t\t: base(content)\n\t\t{{\n\t\t\t_publishedValueFallback = publishedValueFallback;\n\t\t}}\n\n", + sb.AppendFormat("\n\n\t\t// ctor\n\t\tpublic {0}(IPublished{1} content, IPublishedValueFallback publishedValueFallback)\n\t\t\t: base(content, publishedValueFallback)\n\t\t{{\n\t\t\t_publishedValueFallback = publishedValueFallback;\n\t\t}}\n\n", type.ClrName, type.IsElement ? "Element" : "Content"); // write the properties diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs b/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs index 6754060d6f..37199c0990 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/ModelsBuilderAssemblyAttribute.cs @@ -9,10 +9,10 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder public sealed class ModelsBuilderAssemblyAttribute : Attribute { /// - /// Gets or sets a value indicating whether the assembly is a PureLive assembly. + /// Gets or sets a value indicating whether the assembly is a InMemory assembly. /// - /// A Models Builder assembly can be either PureLive or normal Dll. - public bool PureLive { get; set; } + /// A Models Builder assembly can be either InMemory or a normal Dll. + public bool IsInMemory { get; set; } /// /// Gets or sets a hash value representing the state of the custom source code files diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs b/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs index 8b14a6030b..3816db52bf 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/OutOfDateModelsStatus.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.ModelsBuilder diff --git a/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs b/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs index b4963639c4..a02d974b1c 100644 --- a/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs +++ b/src/Umbraco.Infrastructure/ModelsBuilder/UmbracoServices.cs @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder { var types = new List(); - // TODO: this will require 3 rather large SQL queries on startup in PureLive. I know that these will be cached after lookup but it will slow + // TODO: this will require 3 rather large SQL queries on startup in ModelsMode.InMemoryAuto mode. I know that these will be cached after lookup but it will slow // down startup time ... BUT these queries are also used in NuCache on startup so we can't really avoid them. Maybe one day we can // load all of these in in one query and still have them cached per service, and/or somehow improve the perf of these since they are used on startup // in more than one place. diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 16007069c6..b912fd9120 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Collections; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Packaging; @@ -23,8 +24,8 @@ namespace Umbraco.Cms.Core.Packaging { public class PackageDataInstallation { + private readonly IDataValueEditorFactory _dataValueEditorFactory; private readonly ILogger _logger; - private readonly ILoggerFactory _loggerFactory; private readonly IFileService _fileService; private readonly IMacroService _macroService; private readonly ILocalizationService _localizationService; @@ -33,18 +34,16 @@ namespace Umbraco.Cms.Core.Packaging private readonly IScopeProvider _scopeProvider; private readonly IShortStringHelper _shortStringHelper; private readonly GlobalSettings _globalSettings; - private readonly ILocalizedTextService _localizedTextService; private readonly IConfigurationEditorJsonSerializer _serializer; private readonly IMediaService _mediaService; private readonly IMediaTypeService _mediaTypeService; - private readonly IJsonSerializer _jsonSerializer; private readonly IEntityService _entityService; private readonly IContentTypeService _contentTypeService; private readonly IContentService _contentService; public PackageDataInstallation( + IDataValueEditorFactory dataValueEditorFactory, ILogger logger, - ILoggerFactory loggerFactory, IFileService fileService, IMacroService macroService, ILocalizationService localizationService, @@ -56,14 +55,12 @@ namespace Umbraco.Cms.Core.Packaging IScopeProvider scopeProvider, IShortStringHelper shortStringHelper, IOptions globalSettings, - ILocalizedTextService localizedTextService, IConfigurationEditorJsonSerializer serializer, IMediaService mediaService, - IMediaTypeService mediaTypeService, - IJsonSerializer jsonSerializer) + IMediaTypeService mediaTypeService) { + _dataValueEditorFactory = dataValueEditorFactory; _logger = logger; - _loggerFactory = loggerFactory; _fileService = fileService; _macroService = macroService; _localizationService = localizationService; @@ -72,9 +69,7 @@ namespace Umbraco.Cms.Core.Packaging _scopeProvider = scopeProvider; _shortStringHelper = shortStringHelper; _globalSettings = globalSettings.Value; - _localizedTextService = localizedTextService; _serializer = serializer; - _jsonSerializer = jsonSerializer; _mediaService = mediaService; _mediaTypeService = mediaTypeService; _entityService = entityService; @@ -1087,7 +1082,7 @@ namespace Umbraco.Cms.Core.Packaging var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); if (!_propertyEditors.TryGet(editorAlias, out var editor)) - editor = new VoidEditor(_loggerFactory, _dataTypeService, _localizationService, _localizedTextService, _shortStringHelper, _jsonSerializer) { Alias = editorAlias }; + editor = new VoidEditor(_dataValueEditorFactory) { Alias = editorAlias }; var dataType = new DataType(editor, _serializer) { diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs index a19b6a7231..9d000bc771 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeDto.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.ContentType; [Column("pk")] - [PrimaryKeyColumn(IdentitySeed = 535)] + [PrimaryKeyColumn(IdentitySeed = 700)] public int PrimaryKey { get; set; } [Column("nodeId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs index 70c87c175c..d8a8d97a97 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs @@ -1,4 +1,4 @@ -using NPoco; +using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [TableName(Cms.Core.Constants.DatabaseSchema.Tables.DataType)] [PrimaryKey("nodeId", AutoIncrement = false)] [ExplicitColumns] - internal class DataTypeDto + public class DataTypeDto { [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false)] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs index 50691720c1..f63b0e3de5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [TableName(TableName)] [PrimaryKey("pk")] [ExplicitColumns] - internal class DictionaryDto + public class DictionaryDto // public as required to be accessible from Deploy for the RepairDictionaryIdsWorkItem. { public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.DictionaryEntry; [Column("pk")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs index 4bf349da8e..e7b3857582 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs @@ -1,4 +1,4 @@ -using System; +using System; using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [TableName(Cms.Core.Constants.DatabaseSchema.Tables.DictionaryValue)] [PrimaryKey("pk")] [ExplicitColumns] - internal class LanguageTextDto + public class LanguageTextDto { [Column("pk")] [PrimaryKeyColumn] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs index 62ca6a3250..e1505ba05e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos internal class PropertyTypeDto { [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 50)] + [PrimaryKeyColumn(IdentitySeed = 100)] public int Id { get; set; } [Column("dataTypeId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs index 7a8322f561..db3d5b4e74 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs @@ -1,11 +1,11 @@ -using NPoco; +using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos { [TableName(Cms.Core.Constants.DatabaseSchema.Tables.User2UserGroup)] [ExplicitColumns] - internal class User2UserGroupDto + public class User2UserGroupDto { [Column("userId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2userGroup", OnColumns = "userId, userGroupId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserDto.cs index 63090f6c29..9a51672689 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserDto.cs @@ -9,7 +9,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [TableName(TableName)] [PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] - internal class UserDto + public class UserDto { public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.User; diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs index 053a520d76..fd2456c943 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs @@ -1,4 +1,4 @@ -using System; +using System; using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [TableName(Cms.Core.Constants.DatabaseSchema.Tables.UserStartNode)] [PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] - internal class UserStartNodeDto : IEquatable + public class UserStartNodeDto : IEquatable { [Column("id")] [PrimaryKeyColumn(Name = "PK_userStartNode")] diff --git a/src/Umbraco.Infrastructure/Persistence/LocalDb.cs b/src/Umbraco.Infrastructure/Persistence/LocalDb.cs index 0c5137d199..1a24a30066 100644 --- a/src/Umbraco.Infrastructure/Persistence/LocalDb.cs +++ b/src/Umbraco.Infrastructure/Persistence/LocalDb.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence /// Studio which can be used to connect to LocalDB databases. /// See also https://github.com/ritterim/automation-sql which is a somewhat simpler version of this. /// - internal class LocalDb + public class LocalDb { private int _version; private bool _hasVersion; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 0aca22950d..790f82e0a2 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index dd30f85872..b246839440 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs index 742f4ab94c..23365939e0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/FileRepository.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using Umbraco.Cms.Core.IO; @@ -12,46 +12,35 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement internal abstract class FileRepository : IReadRepository, IWriteRepository where TEntity : IFile { - protected FileRepository(IFileSystem fileSystem) - { - FileSystem = fileSystem; - } + protected FileRepository(IFileSystem fileSystem) => FileSystem = fileSystem; protected IFileSystem FileSystem { get; } - public virtual void AddFolder(string folderPath) - { - PersistNewItem(new Folder(folderPath)); - } + public virtual void AddFolder(string folderPath) => PersistNewItem(new Folder(folderPath)); - public virtual void DeleteFolder(string folderPath) - { - PersistDeletedItem(new Folder(folderPath)); - } + public virtual void DeleteFolder(string folderPath) => PersistDeletedItem(new Folder(folderPath)); #region Implementation of IRepository public virtual void Save(TEntity entity) { if (FileSystem.FileExists(entity.OriginalPath) == false) + { PersistNewItem(entity); + } else + { PersistUpdatedItem(entity); + } } - public virtual void Delete(TEntity entity) - { - PersistDeletedItem(entity); - } + public virtual void Delete(TEntity entity) => PersistDeletedItem(entity); public abstract TEntity Get(TId id); public abstract IEnumerable GetMany(params TId[] ids); - public virtual bool Exists(TId id) - { - return FileSystem.FileExists(id.ToString()); - } + public virtual bool Exists(TId id) => FileSystem.FileExists(id.ToString()); #endregion @@ -60,8 +49,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public void PersistNewItem(IEntity entity) { //special case for folder - var folder = entity as Folder; - if (folder != null) + if (entity is Folder folder) { PersistNewFolder(folder); } @@ -71,16 +59,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - public void PersistUpdatedItem(IEntity entity) - { - PersistUpdatedItem((TEntity)entity); - } + public void PersistUpdatedItem(IEntity entity) => PersistUpdatedItem((TEntity)entity); public void PersistDeletedItem(IEntity entity) { //special case for folder - var folder = entity as Folder; - if (folder != null) + if (entity is Folder folder) { PersistDeletedFolder(folder); } @@ -92,21 +76,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement #endregion - internal virtual void PersistNewFolder(Folder entity) - { - FileSystem.CreateFolder(entity.Path); - } + internal virtual void PersistNewFolder(Folder entity) => FileSystem.CreateFolder(entity.Path); - internal virtual void PersistDeletedFolder(Folder entity) - { - FileSystem.DeleteDirectory(entity.Path); - } + internal virtual void PersistDeletedFolder(Folder entity) => FileSystem.DeleteDirectory(entity.Path); #region Abstract IUnitOfWorkRepository Methods protected virtual void PersistNewItem(TEntity entity) { - using (var stream = GetContentStream(entity.Content)) + using (Stream stream = GetContentStream(entity.Content)) { FileSystem.AddFile(entity.Path, stream, true); entity.CreateDate = FileSystem.GetCreated(entity.Path).UtcDateTime; @@ -120,7 +98,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected virtual void PersistUpdatedItem(TEntity entity) { - using (var stream = GetContentStream(entity.Content)) + using (Stream stream = GetContentStream(entity.Content)) { FileSystem.AddFile(entity.Path, stream, true); entity.CreateDate = FileSystem.GetCreated(entity.Path).UtcDateTime; @@ -156,10 +134,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// /// /// - protected virtual Stream GetContentStream(string content) - { - return new MemoryStream(Encoding.UTF8.GetBytes(content)); - } + protected virtual Stream GetContentStream(string content) => new MemoryStream(Encoding.UTF8.GetBytes(content)); /// /// Returns all files in the file system @@ -179,7 +154,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var list = new List(); list.AddRange(FileSystem.GetFiles(path, filter)); - var directories = FileSystem.GetDirectories(path); + IEnumerable directories = FileSystem.GetDirectories(path); foreach (var directory in directories) { list.AddRange(FindAllFiles(directory, filter)); @@ -191,11 +166,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected string GetFileContent(string filename) { if (FileSystem.FileExists(filename) == false) + { return null; + } try { - using (var stream = FileSystem.OpenFile(filename)) + using (Stream stream = FileSystem.OpenFile(filename)) using (var reader = new StreamReader(stream, Encoding.UTF8, true)) { return reader.ReadToEnd(); @@ -207,10 +184,31 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } + public Stream GetFileContentStream(string filepath) + { + if (FileSystem.FileExists(filepath) == false) + { + return null; + } + + try + { + return FileSystem.OpenFile(filepath); + } + catch + { + return null; // deal with race conds + } + } + + public void SetFileContent(string filepath, Stream content) => FileSystem.AddFile(filepath, content, true); + public long GetFileSize(string filename) { if (FileSystem.FileExists(filename) == false) + { return -1; + } try { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index 80804004b8..b69907d71b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -7,6 +7,7 @@ using NPoco; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs index b372c3b479..5ad27b7506 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs @@ -6,10 +6,10 @@ using NPoco; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -203,7 +203,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .Select(x => MemberGroupFactory.BuildEntity(x)); } - + public void ReplaceRoles(int[] memberIds, string[] roleNames) => AssignRolesInternal(memberIds, roleNames, true); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs index 45c904eff0..698d0fffa7 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs index d13e4c6945..b4c8ce4f6c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs @@ -6,8 +6,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { internal class PartialViewMacroRepository : PartialViewRepository, IPartialViewMacroRepository { - public PartialViewMacroRepository(IFileSystems fileSystems, IIOHelper ioHelper) - : base(fileSystems.MacroPartialsFileSystem, ioHelper) + public PartialViewMacroRepository(FileSystems fileSystems) + : base(fileSystems.MacroPartialsFileSystem) { } protected override PartialViewType ViewType => PartialViewType.PartialViewMacro; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs index eee199e252..751c84ac56 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs @@ -11,18 +11,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { internal class PartialViewRepository : FileRepository, IPartialViewRepository { - private readonly IIOHelper _ioHelper; - - public PartialViewRepository(IFileSystems fileSystems, IIOHelper ioHelper) + public PartialViewRepository(FileSystems fileSystems) : base(fileSystems.PartialViewsFileSystem) { - _ioHelper = ioHelper; } - protected PartialViewRepository(IFileSystem fileSystem, IIOHelper ioHelper) + protected PartialViewRepository(IFileSystem fileSystem) : base(fileSystem) { - _ioHelper = ioHelper; } protected virtual PartialViewType ViewType => PartialViewType.PartialView; @@ -93,29 +89,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - private static readonly List ValidExtensions = new List { "cshtml" }; - - public virtual bool ValidatePartialView(IPartialView partialView) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - fullPath = FileSystem.GetFullPath(partialView.Path); - } - catch - { - return false; - } - - // validate path & extension - var validDir = Cms.Core.Constants.SystemDirectories.MvcViews; - var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); - var isValidExtension = _ioHelper.VerifyFileExtension(fullPath, ValidExtensions); - return isValidPath && isValidExtension; - } - public Stream GetFileContentStream(string filepath) { if (FileSystem.FileExists(filepath) == false) return null; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs index 161b7a90a8..711a43f716 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs @@ -1,9 +1,6 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -16,14 +13,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// internal class ScriptRepository : FileRepository, IScriptRepository { - private readonly IIOHelper _ioHelper; - private readonly GlobalSettings _globalSettings; - - public ScriptRepository(IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public ScriptRepository(FileSystems fileSystems) : base(fileSystems.ScriptsFileSystem) { - _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); - _globalSettings = globalSettings.Value; } #region Implementation of IRepository @@ -93,47 +85,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - public bool ValidateScript(IScript script) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - fullPath = FileSystem.GetFullPath(script.Path); - } - catch - { - return false; - } - - // validate path & extension - var validDir = _globalSettings.UmbracoScriptsPath; - var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); - var validExts = new[] {"js"}; - var isValidExtension = _ioHelper.VerifyFileExtension(script.Path, validExts); - return isValidPath && isValidExtension; - } - - public Stream GetFileContentStream(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return null; - - try - { - return FileSystem.OpenFile(filepath); - } - catch - { - return null; // deal with race conds - } - } - - public void SetFileContent(string filepath, Stream content) - { - FileSystem.AddFile(filepath, content, true); - } - #endregion } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs index 03c729b51c..9c0aa6f92a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -1,10 +1,5 @@ -using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; @@ -17,16 +12,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// internal class StylesheetRepository : FileRepository, IStylesheetRepository { - private readonly ILogger _logger; - private readonly IIOHelper _ioHelper; - private readonly GlobalSettings _globalSettings; - - public StylesheetRepository(ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public StylesheetRepository(FileSystems fileSystems) : base(fileSystems.StylesheetsFileSystem) { - _logger = logger; - _ioHelper = ioHelper; - _globalSettings = globalSettings.Value; } #region Overrides of FileRepository @@ -110,80 +98,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - /// - /// Gets a list of all that exist at the relative path specified. - /// - /// - /// If null or not specified, will return the stylesheets at the root path relative to the IFileSystem - /// - /// - public IEnumerable GetStylesheetsAtPath(string rootPath = null) - { - return FileSystem.GetFiles(rootPath ?? string.Empty, "*.css").Select(Get); - } - - private static readonly List ValidExtensions = new List { "css" }; - - public bool ValidateStylesheet(IStylesheet stylesheet) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - - fullPath = FileSystem.GetFullPath(stylesheet.Path); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Can't get full path"); - return false; - } - - // validate path and extension - var validDir = _globalSettings.UmbracoCssPath; - _logger.LogWarning("fullPath {fullPath}, validDir: {validDir}",fullPath, validDir); - - var isValidPath = _ioHelper.VerifyEditPath(fullPath, validDir); - var isValidExtension = _ioHelper.VerifyFileExtension(stylesheet.Path, ValidExtensions); - - _logger.LogWarning("isValidPath {isValidPath}, isValidExtension: {isValidExtension}",isValidPath, isValidExtension); - return isValidPath && isValidExtension; - } - - public Stream GetFileContentStream(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return null; - - try - { - return FileSystem.OpenFile(filepath); - } - catch - { - return null; // deal with race conds - } - } - - public void SetFileContent(string filepath, Stream content) - { - FileSystem.AddFile(filepath, content, true); - } - - public new long GetFileSize(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return -1; - - try - { - return FileSystem.GetSize(filepath); - } - catch - { - return -1; // deal with race conds - } - } - #endregion } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index ec54a6bef8..4e4f52c93a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IFileSystem _viewsFileSystem; private readonly ViewHelper _viewHelper; - public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, FileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper) : base(scopeAccessor, cache, logger) { _ioHelper = ioHelper; @@ -39,39 +39,38 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement _viewHelper = new ViewHelper(_viewsFileSystem); } - protected override IRepositoryCachePolicy CreateCachePolicy() - { - return new FullDataSetRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false); - } + protected override IRepositoryCachePolicy CreateCachePolicy() => + new FullDataSetRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false); #region Overrides of RepositoryBase - protected override ITemplate PerformGet(int id) - { + protected override ITemplate PerformGet(int id) => //use the underlying GetAll which will force cache all templates - return base.GetMany().FirstOrDefault(x => x.Id == id); - } + base.GetMany().FirstOrDefault(x => x.Id == id); protected override IEnumerable PerformGetAll(params int[] ids) { - var sql = GetBaseQuery(false); + Sql sql = GetBaseQuery(false); if (ids.Any()) { - sql.Where("umbracoNode.id in (@ids)", new { ids = ids }); + sql.Where("umbracoNode.id in (@ids)", new { ids }); } else { sql.Where(x => x.NodeObjectType == NodeObjectTypeId); } - var dtos = Database.Fetch(sql); + List dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); + if (dtos.Count == 0) + { + return Enumerable.Empty(); + } //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIds = (ids.Any() + IUmbracoEntity[] childIds = (ids.Any() ? GetAxisDefinitions(dtos.ToArray()) : dtos.Select(x => new EntitySlim { @@ -85,17 +84,20 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override IEnumerable PerformGetByQuery(IQuery query) { - var sqlClause = GetBaseQuery(false); + Sql sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); + Sql sql = translator.Translate(); - var dtos = Database.Fetch(sql); + List dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); + if (dtos.Count == 0) + { + return Enumerable.Empty(); + } //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIds = GetAxisDefinitions(dtos.ToArray()).ToArray(); + IUmbracoEntity[] childIds = GetAxisDefinitions(dtos.ToArray()).ToArray(); return dtos.Select(d => MapFromDto(d, childIds)); } @@ -106,7 +108,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override Sql GetBaseQuery(bool isCount) { - var sql = SqlContext.Sql(); + Sql sql = SqlContext.Sql(); sql = isCount ? sql.SelectCount() @@ -121,10 +123,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return sql; } - protected override string GetBaseWhereClause() - { - return Cms.Core.Constants.DatabaseSchema.Tables.Node + ".id = @id"; - } + protected override string GetBaseWhereClause() => Cms.Core.Constants.DatabaseSchema.Tables.Node + ".id = @id"; protected override IEnumerable GetDeleteClauses() { @@ -150,15 +149,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var template = (Template)entity; template.AddingEntity(); - var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, template.Id); + TemplateDto dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, template.Id); //Create the (base) node data - umbracoNode - var nodeDto = dto.NodeDto; + NodeDto nodeDto = dto.NodeDto; nodeDto.Path = "-1," + dto.NodeDto.NodeId; - var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto); + int o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto); //Update with new correct path - var parent = Get(template.MasterTemplateId.Value); + ITemplate parent = Get(template.MasterTemplateId.Value); if (parent != null) { nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); @@ -184,7 +183,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // ensure that from now on, content is lazy-loaded if (template.GetFileContent == null) + { template.GetFileContent = file => GetFileContent((Template) file, false); + } } protected override void PersistUpdatedItem(ITemplate entity) @@ -192,11 +193,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement EnsureValidAlias(entity); //store the changed alias if there is one for use with updating files later - var originalAlias = entity.Alias; + string originalAlias = entity.Alias; if (entity.IsPropertyDirty("Alias")) { //we need to check what it currently is before saving and remove that file - var current = Get(entity.Id); + ITemplate current = Get(entity.Id); originalAlias = current.Alias; } @@ -204,7 +205,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement if (entity.IsPropertyDirty("MasterTemplateId")) { - var parent = Get(template.MasterTemplateId.Value); + ITemplate parent = Get(template.MasterTemplateId.Value); if (parent != null) { entity.Path = string.Concat(parent.Path, ",", entity.Id); @@ -218,17 +219,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } //Get TemplateDto from db to get the Primary key of the entity - var templateDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { Id = entity.Id }); + TemplateDto templateDto = Database.SingleOrDefault("WHERE nodeId = @Id", new { entity.Id }); + //Save updated entity to db - template.UpdateDate = DateTime.Now; - var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, templateDto.PrimaryKey); - + TemplateDto dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, templateDto.PrimaryKey); Database.Update(dto.NodeDto); Database.Update(dto); //re-update if this is a master template, since it could have changed! - var axisDefs = GetAxisDefinitions(dto); + IEnumerable axisDefs = GetAxisDefinitions(dto); template.IsMasterTemplate = axisDefs.Any(x => x.ParentId == dto.NodeId); //now do the file work @@ -238,15 +238,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // ensure that from now on, content is lazy-loaded if (template.GetFileContent == null) + { template.GetFileContent = file => GetFileContent((Template) file, false); + } } private void SaveFile(Template template, string originalAlias = null) { string content; - var templateOnDisk = template as TemplateOnDisk; - if (templateOnDisk != null && templateOnDisk.IsOnDisk) + if (template is TemplateOnDisk templateOnDisk && templateOnDisk.IsOnDisk) { // if "template on disk" load content from disk content = _viewHelper.GetFileContents(template); @@ -266,7 +267,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected override void PersistDeletedItem(ITemplate entity) { - var deletes = GetDeleteClauses().ToArray(); + string[] deletes = GetDeleteClauses().ToArray(); var descendants = GetDescendants(entity.Id).ToList(); @@ -274,21 +275,21 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement descendants.Reverse(); //delete the hierarchy - foreach (var descendant in descendants) + foreach (ITemplate descendant in descendants) { - foreach (var delete in deletes) + foreach (string delete in deletes) { Database.Execute(delete, new { id = GetEntityId(descendant) }); } } //now we can delete this one - foreach (var delete in deletes) + foreach (string delete in deletes) { Database.Execute(delete, new { id = GetEntityId(entity) }); } - var viewName = string.Concat(entity.Alias, ".cshtml"); + string viewName = string.Concat(entity.Alias, ".cshtml"); _viewsFileSystem.DeleteFile(viewName); entity.DeleteDate = DateTime.Now; @@ -300,7 +301,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties - var childIdsSql = SqlContext.Sql() + Sql childIdsSql = SqlContext.Sql() .Select("nodeId,alias,parentID") .From() .InnerJoin() @@ -309,7 +310,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .Where("umbracoNode." + SqlContext.SqlSyntax.GetQuotedColumnName("id") + " IN (@parentIds) OR umbracoNode.parentID IN (@childIds)", new {parentIds = templates.Select(x => x.NodeDto.ParentId), childIds = templates.Select(x => x.NodeId)}); - var childIds = Database.Fetch(childIdsSql) + IEnumerable childIds = Database.Fetch(childIdsSql) .Select(x => new EntitySlim { Id = x.nodeId, @@ -330,11 +331,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private ITemplate MapFromDto(TemplateDto dto, IUmbracoEntity[] axisDefinitions) { - var template = TemplateFactory.BuildEntity(_shortStringHelper, dto, axisDefinitions, file => GetFileContent((Template) file, false)); + Template template = TemplateFactory.BuildEntity(_shortStringHelper, dto, axisDefinitions, file => GetFileContent((Template) file, false)); if (dto.NodeDto.ParentId > 0) { - var masterTemplate = axisDefinitions.FirstOrDefault(x => x.Id == dto.NodeDto.ParentId); + IUmbracoEntity masterTemplate = axisDefinitions.FirstOrDefault(x => x.Id == dto.NodeDto.ParentId); if (masterTemplate != null) { template.MasterTemplateAlias = masterTemplate.Name; @@ -354,7 +355,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private void SetVirtualPath(ITemplate template) { - var path = template.OriginalPath; + string path = template.OriginalPath; if (string.IsNullOrWhiteSpace(path)) { // we need to discover the path @@ -382,16 +383,21 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private string GetFileContent(ITemplate template, bool init) { - var path = template.OriginalPath; + string path = template.OriginalPath; if (string.IsNullOrWhiteSpace(path)) { // we need to discover the path path = string.Concat(template.Alias, ".cshtml"); if (_viewsFileSystem.FileExists(path)) + { return GetFileContent(template, _viewsFileSystem, path, init); + } + path = string.Concat(template.Alias, ".vbhtml"); if (_viewsFileSystem.FileExists(path)) + { return GetFileContent(template, _viewsFileSystem, path, init); + } } else { @@ -427,7 +433,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private string GetFileContent(IFileSystem fs, string filename) { - using (var stream = fs.OpenFile(filename)) + using (Stream stream = fs.OpenFile(filename)) using (var reader = new StreamReader(stream, Encoding.UTF8, true)) { return reader.ReadToEnd(); @@ -436,12 +442,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public Stream GetFileContentStream(string filepath) { - var fs = GetFileSystem(filepath); - if (fs.FileExists(filepath) == false) return null; + IFileSystem fileSystem = GetFileSystem(filepath); + if (fileSystem.FileExists(filepath) == false) + { + return null; + } try { - return GetFileSystem(filepath).OpenFile(filepath); + return fileSystem.OpenFile(filepath); } catch { @@ -449,19 +458,29 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } - public void SetFileContent(string filepath, Stream content) - { - GetFileSystem(filepath).AddFile(filepath, content, true); - } + public void SetFileContent(string filepath, Stream content) => GetFileSystem(filepath).AddFile(filepath, content, true); - public long GetFileSize(string filepath) + public long GetFileSize(string filename) { - return GetFileSystem(filepath).GetSize(filepath); + IFileSystem fileSystem = GetFileSystem(filename); + if (fileSystem.FileExists(filename) == false) + { + return -1; + } + + try + { + return fileSystem.GetSize(filename); + } + catch + { + return -1; // deal with race conds + } } private IFileSystem GetFileSystem(string filepath) { - var ext = Path.GetExtension(filepath); + string ext = Path.GetExtension(filepath); IFileSystem fs; switch (ext) { @@ -477,17 +496,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement #region Implementation of ITemplateRepository - public ITemplate Get(string alias) - { - return GetAll(alias).FirstOrDefault(); - } + public ITemplate Get(string alias) => GetAll(alias).FirstOrDefault(); public IEnumerable GetAll(params string[] aliases) { //We must call the base (normal) GetAll method // which is cached. This is a specialized method and unfortunately with the params[] it // overlaps with the normal GetAll method. - if (aliases.Any() == false) return base.GetMany(); + if (aliases.Any() == false) + { + return base.GetMany(); + } //return from base.GetAll, this is all cached return base.GetMany().Where(x => aliases.InvariantContains(x.Alias)); @@ -496,41 +515,43 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement public IEnumerable GetChildren(int masterTemplateId) { //return from base.GetAll, this is all cached - var all = base.GetMany().ToArray(); + ITemplate[] all = base.GetMany().ToArray(); - if (masterTemplateId <= 0) return all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()); + if (masterTemplateId <= 0) + { + return all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()); + } - var parent = all.FirstOrDefault(x => x.Id == masterTemplateId); - if (parent == null) return Enumerable.Empty(); + ITemplate parent = all.FirstOrDefault(x => x.Id == masterTemplateId); + if (parent == null) + { + return Enumerable.Empty(); + } - var children = all.Where(x => x.MasterTemplateAlias.InvariantEquals(parent.Alias)); + IEnumerable children = all.Where(x => x.MasterTemplateAlias.InvariantEquals(parent.Alias)); return children; } - public IEnumerable GetChildren(string alias) - { - //return from base.GetAll, this is all cached - return base.GetMany().Where(x => alias.IsNullOrWhiteSpace() - ? x.MasterTemplateAlias.IsNullOrWhiteSpace() - : x.MasterTemplateAlias.InvariantEquals(alias)); - } - public IEnumerable GetDescendants(int masterTemplateId) { //return from base.GetAll, this is all cached - var all = base.GetMany().ToArray(); + ITemplate[] all = base.GetMany().ToArray(); var descendants = new List(); if (masterTemplateId > 0) { - var parent = all.FirstOrDefault(x => x.Id == masterTemplateId); - if (parent == null) return Enumerable.Empty(); + ITemplate parent = all.FirstOrDefault(x => x.Id == masterTemplateId); + if (parent == null) + { + return Enumerable.Empty(); + } + //recursively add all children with a level AddChildren(all, descendants, parent.Alias); } else { descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace())); - foreach (var parent in descendants) + foreach (ITemplate parent in descendants) { //recursively add all children with a level AddChildren(all, descendants, parent.Alias); @@ -541,92 +562,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return descendants; } - public IEnumerable GetDescendants(string alias) - { - var all = base.GetMany().ToArray(); - var descendants = new List(); - if (alias.IsNullOrWhiteSpace() == false) - { - var parent = all.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - if (parent == null) return Enumerable.Empty(); - //recursively add all children - AddChildren(all, descendants, parent.Alias); - } - else - { - descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace())); - foreach (var parent in descendants) - { - //recursively add all children with a level - AddChildren(all, descendants, parent.Alias); - } - } - //return the list - it will be naturally ordered by level - return descendants; - } - private void AddChildren(ITemplate[] all, List descendants, string masterAlias) { - var c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray(); + ITemplate[] c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray(); descendants.AddRange(c); - if (c.Any() == false) return; + if (c.Any() == false) + { + return; + } + //recurse through all children - foreach (var child in c) + foreach (ITemplate child in c) { AddChildren(all, descendants, child.Alias); } } - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - public bool ValidateTemplate(ITemplate template) - { - // get path - // TODO: templates should have a real Path somehow - but anyways - // are we using Path for something else?! - var path = template.VirtualPath; - - // get valid paths - var validDirs = new[] { Cms.Core.Constants.SystemDirectories.MvcViews }; - - // get valid extensions - var validExts = new List(); - validExts.Add("cshtml"); - validExts.Add("vbhtml"); - - // validate path and extension - var validFile = _ioHelper.VerifyEditPath(path, validDirs); - var validExtension = _ioHelper.VerifyFileExtension(path, validExts); - return validFile && validExtension; - } - - private static IEnumerable CreateChildren(TemplateNode parent, IEnumerable childTemplates, ITemplate[] allTemplates) - { - var children = new List(); - foreach (var childTemplate in childTemplates) - { - var template = allTemplates.Single(x => x.Id == childTemplate.Id); - var child = new TemplateNode(template) - { - Parent = parent - }; - - //add to our list - children.Add(child); - - //get this node's children - var local = childTemplate; - var kids = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(local.Alias)); - - //recurse - child.Children = CreateChildren(child, kids, allTemplates); - } - return children; - } - #endregion /// @@ -639,7 +590,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement template.Alias = template.Alias.ToCleanString(_shortStringHelper, CleanStringType.UnderscoreAlias); if (template.Alias.Length > 100) + { template.Alias = template.Alias.Substring(0, 95); + } if (AliasAlreadExists(template)) { @@ -649,8 +602,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private bool AliasAlreadExists(ITemplate template) { - var sql = GetBaseQuery(true).Where(x => x.Alias.InvariantEquals(template.Alias) && x.NodeId != template.Id); - var count = Database.ExecuteScalar(sql); + Sql sql = GetBaseQuery(true).Where(x => x.Alias.InvariantEquals(template.Alias) && x.NodeId != template.Id); + int count = Database.ExecuteScalar(sql); return count > 0; } @@ -658,7 +611,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { // TODO: This is ported from the old data layer... pretty crap way of doing this but it works for now. if (AliasAlreadExists(template)) + { return template.Alias + attempts; + } + attempts++; return EnsureUniqueAlias(template, attempts); } diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs index 944195cb82..10d6fa2278 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs @@ -112,27 +112,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence Configure(settings.ConnectionString, settings.ProviderName); } - /// - /// Initializes a new instance of the . - /// - /// Used in tests. - public UmbracoDatabaseFactory(ILogger logger, ILoggerFactory loggerFactory, string connectionString, string providerName, Lazy mappers, IDbProviderFactoryCreator dbProviderFactoryCreator, DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory) - { - _mappers = mappers ?? throw new ArgumentNullException(nameof(mappers)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _loggerFactory = loggerFactory; - _dbProviderFactoryCreator = dbProviderFactoryCreator ?? throw new ArgumentNullException(nameof(dbProviderFactoryCreator)); - _databaseSchemaCreatorFactory = databaseSchemaCreatorFactory ?? throw new ArgumentNullException(nameof(databaseSchemaCreatorFactory)); - - if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName)) - { - logger.LogDebug("Missing connection string or provider name, defer configuration."); - return; // not configured - } - - Configure(connectionString, providerName); - } - #endregion /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs index 4e8483d5fc..1e195ec1b2 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs @@ -7,6 +7,8 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; using Umbraco.Cms.Core.Models.Editors; @@ -24,26 +26,14 @@ namespace Umbraco.Cms.Core.PropertyEditors { public const string ContentTypeKeyPropertyKey = "contentTypeKey"; public const string UdiPropertyKey = "udi"; - private readonly ILocalizedTextService _localizedTextService; private readonly Lazy _propertyEditors; - private readonly IDataTypeService _dataTypeService; - private readonly IContentTypeService _contentTypeService; public BlockEditorPropertyEditor( - ILoggerFactory loggerFactory, - Lazy propertyEditors, - IDataTypeService dataTypeService, - IContentTypeService contentTypeService, - ILocalizedTextService localizedTextService, - ILocalizationService localizationService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService,localizationService,localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + Lazy propertyEditors) + : base(dataValueEditorFactory) { - _localizedTextService = localizedTextService; _propertyEditors = propertyEditors; - _dataTypeService = dataTypeService; - _contentTypeService = contentTypeService; } // has to be lazy else circular dep in ctor @@ -51,7 +41,7 @@ namespace Umbraco.Cms.Core.PropertyEditors #region Value Editor - protected override IDataValueEditor CreateValueEditor() => new BlockEditorPropertyValueEditor(Attribute, PropertyEditors, _dataTypeService, _contentTypeService, _localizedTextService, LoggerFactory.CreateLogger(), LocalizationService,ShortStringHelper, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); internal class BlockEditorPropertyValueEditor : DataValueEditor, IDataValueReference { @@ -60,14 +50,24 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly ILogger _logger; private readonly BlockEditorValues _blockEditorValues; - public BlockEditorPropertyValueEditor(DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IContentTypeService contentTypeService, ILocalizedTextService textService, ILogger logger, ILocalizationService localizationService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, textService, shortStringHelper, jsonSerializer, attribute) + public BlockEditorPropertyValueEditor( + DataEditorAttribute attribute, + PropertyEditorCollection propertyEditors, + IDataTypeService dataTypeService, + IContentTypeService contentTypeService, + ILocalizedTextService textService, + ILogger logger, + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + IPropertyValidationService propertyValidationService) + : base(textService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _propertyEditors = propertyEditors; _dataTypeService = dataTypeService; _logger = logger; _blockEditorValues = new BlockEditorValues(new BlockListEditorDataConverter(), contentTypeService, _logger); - Validators.Add(new BlockEditorValidator(_blockEditorValues, propertyEditors, dataTypeService, textService, contentTypeService)); + Validators.Add(new BlockEditorValidator(propertyValidationService, _blockEditorValues,contentTypeService)); Validators.Add(new MinMaxValidator(_blockEditorValues, textService)); } @@ -285,8 +285,8 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly BlockEditorValues _blockEditorValues; private readonly IContentTypeService _contentTypeService; - public BlockEditorValidator(BlockEditorValues blockEditorValues, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService, IContentTypeService contentTypeService) - : base(propertyEditors, dataTypeService, textService) + public BlockEditorValidator(IPropertyValidationService propertyValidationService, BlockEditorValues blockEditorValues, IContentTypeService contentTypeService) + : base(propertyValidationService) { _blockEditorValues = blockEditorValues; _contentTypeService = contentTypeService; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs index 6d2bbd4eff..469d908cb3 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs @@ -3,6 +3,7 @@ using System; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; @@ -25,16 +26,10 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IIOHelper _ioHelper; public BlockListPropertyEditor( - ILoggerFactory loggerFactory, + IDataValueEditorFactory dataValueEditorFactory, Lazy propertyEditors, - IDataTypeService dataTypeService, - IContentTypeService contentTypeService, - ILocalizedTextService localizedTextService, - IIOHelper ioHelper, - ILocalizationService localizationService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, propertyEditors, dataTypeService, contentTypeService, localizedTextService, localizationService, shortStringHelper, jsonSerializer) + IIOHelper ioHelper) + : base(dataValueEditorFactory, propertyEditors) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs index b7a825b01b..e4cc46fda4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/CheckBoxListPropertyEditor.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; @@ -22,30 +23,25 @@ namespace Umbraco.Cms.Core.PropertyEditors public class CheckBoxListPropertyEditor : DataEditor { private readonly ILocalizedTextService _textService; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; - private readonly IShortStringHelper _shortStringHelper; private readonly IIOHelper _ioHelper; - private readonly ILocalizedTextService _localizedTextService; /// /// The constructor will setup the property editor based on the attribute if one is found /// - public CheckBoxListPropertyEditor(ILoggerFactory loggerFactory, ILocalizedTextService textService, IDataTypeService dataTypeService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, IIOHelper ioHelper, ILocalizedTextService localizedTextService, IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer) + public CheckBoxListPropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, + ILocalizedTextService textService, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _textService = textService; - _dataTypeService = dataTypeService; - _localizationService = localizationService; - _shortStringHelper = shortStringHelper; _ioHelper = ioHelper; - _localizedTextService = localizedTextService; } /// protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(_textService, _ioHelper); /// - protected override IDataValueEditor CreateValueEditor() => new MultipleValueEditor(LoggerFactory.CreateLogger(), _dataTypeService, _localizationService, _localizedTextService, _shortStringHelper, JsonSerializer, Attribute); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs index a80ea7c322..71eef56225 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ColorPickerPropertyEditor.cs @@ -4,8 +4,6 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Serialization; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; namespace Umbraco.Cms.Core.PropertyEditors { @@ -18,14 +16,19 @@ namespace Umbraco.Cms.Core.PropertyEditors public class ColorPickerPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; + private readonly IJsonSerializer _jsonSerializer; - public ColorPickerPropertyEditor(ILoggerFactory loggerFactory, IDataTypeService dataTypeService, ILocalizationService localizationService, IIOHelper ioHelper, IShortStringHelper shortStringHelper, ILocalizedTextService localizedTextService, IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + public ColorPickerPropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper, + IJsonSerializer jsonSerializer) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; + _jsonSerializer = jsonSerializer; } /// - protected override IConfigurationEditor CreateConfigurationEditor() => new ColorPickerConfigurationEditor(_ioHelper, JsonSerializer); + protected override IConfigurationEditor CreateConfigurationEditor() => new ColorPickerConfigurationEditor(_ioHelper, _jsonSerializer); } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexEditorValidator.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexEditorValidator.cs index cfac709d47..6983c5ed7d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexEditorValidator.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexEditorValidator.cs @@ -18,11 +18,11 @@ namespace Umbraco.Cms.Core.PropertyEditors /// public abstract class ComplexEditorValidator : IValueValidator { - private readonly PropertyValidationService _propertyValidationService; + private readonly IPropertyValidationService _propertyValidationService; - public ComplexEditorValidator(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService) + public ComplexEditorValidator(IPropertyValidationService propertyValidationService) { - _propertyValidationService = new PropertyValidationService(propertyEditors, dataTypeService, textService); + _propertyValidationService = propertyValidationService; } /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs index 73748c4a48..e4039b6cee 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentNotificationHandler.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services.Notifications; +using Umbraco.Cms.Core.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs index 318c6ccdcb..b42fb7706e 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ContentPickerPropertyEditor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -24,24 +25,13 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.Pickers)] public class ContentPickerPropertyEditor : DataEditor { - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; - private readonly ILocalizedTextService _localizedTextService; private readonly IIOHelper _ioHelper; public ContentPickerPropertyEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - ILoggerFactory loggerFactory, - IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService,localizationService,localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { - _dataTypeService = dataTypeService; - _localizationService = localizationService; - _localizedTextService = localizedTextService; _ioHelper = ioHelper; } @@ -50,12 +40,17 @@ namespace Umbraco.Cms.Core.PropertyEditors return new ContentPickerConfigurationEditor(_ioHelper); } - protected override IDataValueEditor CreateValueEditor() => new ContentPickerPropertyValueEditor(_dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, JsonSerializer, Attribute); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); internal class ContentPickerPropertyValueEditor : DataValueEditor, IDataValueReference { - public ContentPickerPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + public ContentPickerPropertyValueEditor( + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + DataEditorAttribute attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs index 2cb7b8f0d2..8a6b670435 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DateTimePropertyEditor.cs @@ -28,8 +28,8 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// /// - public DateTimePropertyEditor(ILoggerFactory loggerFactory, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer) + public DateTimePropertyEditor(IDataValueEditorFactory dataValueEditorFactory, IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs index 6e970a9cba..f0b9e827f1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DropDownFlexiblePropertyEditor.cs @@ -19,31 +19,21 @@ namespace Umbraco.Cms.Core.PropertyEditors public class DropDownFlexiblePropertyEditor : DataEditor { private readonly ILocalizedTextService _textService; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; - private readonly IShortStringHelper _shortStringHelper; private readonly IIOHelper _ioHelper; public DropDownFlexiblePropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, ILocalizedTextService textService, - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IShortStringHelper shortStringHelper, - IIOHelper ioHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, textService, shortStringHelper, jsonSerializer) + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _textService = textService; - _dataTypeService = dataTypeService; - _localizationService = localizationService; - _shortStringHelper = shortStringHelper; _ioHelper = ioHelper; } protected override IDataValueEditor CreateValueEditor() { - return new MultipleValueEditor(LoggerFactory.CreateLogger(), _dataTypeService, _localizationService, _textService, _shortStringHelper, JsonSerializer, Attribute); + return DataValueEditorFactory.Create(Attribute); } protected override IConfigurationEditor CreateConfigurationEditor() => new DropDownFlexibleConfigurationEditor(_textService, _ioHelper); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs index 0f4f7e1834..c821790871 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/EmailAddressPropertyEditor.cs @@ -25,14 +25,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The constructor will setup the property editor based on the attribute if one is found /// public EmailAddressPropertyEditor( - ILoggerFactory loggerFactory, - IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs index 23fdeaeb6d..e37a0e62cd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/EyeDropperColorPickerPropertyEditor.cs @@ -17,22 +17,10 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IIOHelper _ioHelper; public EyeDropperColorPickerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer, + IDataValueEditorFactory dataValueEditorFactory, IIOHelper ioHelper, EditorType type = EditorType.PropertyValue) - : base( - loggerFactory, - dataTypeService, - localizationService, - localizedTextService, - shortStringHelper, - jsonSerializer, - type) + : base(dataValueEditorFactory, type) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadConfigurationEditor.cs new file mode 100644 index 0000000000..a1290624c3 --- /dev/null +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadConfigurationEditor.cs @@ -0,0 +1,15 @@ + +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Represents the configuration editor for the file upload value editor. + /// + public class FileUploadConfigurationEditor : ConfigurationEditor + { + public FileUploadConfigurationEditor(IIOHelper ioHelper) : base(ioHelper) + { + } + } +} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index a0bbf2a7fe..3511a4ce0e 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -11,9 +11,9 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -30,35 +30,33 @@ namespace Umbraco.Cms.Core.PropertyEditors INotificationHandler, INotificationHandler, INotificationHandler { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly UploadAutoFillProperties _uploadAutoFillProperties; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly ILocalizedTextService _localizedTextService; private readonly IContentService _contentService; + private readonly IIOHelper _ioHelper; public FileUploadPropertyEditor( - ILoggerFactory loggerFactory, - IMediaFileSystem mediaFileSystem, + IDataValueEditorFactory dataValueEditorFactory, + MediaFileManager mediaFileManager, IOptions contentSettings, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, UploadAutoFillProperties uploadAutoFillProperties, - IJsonSerializer jsonSerializer, - IContentService contentService) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IContentService contentService, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _localizedTextService = localizedTextService; _uploadAutoFillProperties = uploadAutoFillProperties; _contentService = contentService; + _ioHelper = ioHelper; } + /// + protected override IConfigurationEditor CreateConfigurationEditor() => new FileUploadConfigurationEditor(_ioHelper); + /// /// Creates the corresponding property value editor. @@ -66,7 +64,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The corresponding property value editor. protected override IDataValueEditor CreateValueEditor() { - var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Options.Create(_contentSettings), JsonSerializer); + var editor = DataValueEditorFactory.Create(Attribute); editor.Validators.Add(new UploadFileTypeValidator(_localizedTextService, Options.Create(_contentSettings))); return editor; } @@ -115,12 +113,12 @@ namespace Umbraco.Cms.Core.PropertyEditors //check if the published value contains data and return it var propVal = propertyValue.PublishedValue; if (propVal != null && propVal is string str1 && !str1.IsNullOrWhiteSpace()) - yield return _mediaFileSystem.GetRelativePath(str1); + yield return _mediaFileManager.FileSystem.GetRelativePath(str1); //check if the edited value contains data and return it propVal = propertyValue.EditedValue; if (propVal != null && propVal is string str2 && !str2.IsNullOrWhiteSpace()) - yield return _mediaFileSystem.GetRelativePath(str2); + yield return _mediaFileManager.FileSystem.GetRelativePath(str2); } } @@ -142,9 +140,9 @@ namespace Umbraco.Cms.Core.PropertyEditors continue; } - var sourcePath = _mediaFileSystem.GetRelativePath(str); - var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); - notification.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); + var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(str); + var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath); + notification.Copy.SetValue(property.Alias, _mediaFileManager.FileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } } @@ -165,7 +163,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); - _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + _mediaFileManager.DeleteMediaFiles(filePathsToDelete); } public void Handle(MediaSavingNotification notification) @@ -194,7 +192,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (string.IsNullOrWhiteSpace(svalue)) _uploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); + _uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileManager.FileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs index 3f892dd000..3892071e57 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -5,6 +5,7 @@ using System; using System.IO; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models.Editors; using Umbraco.Cms.Core.Serialization; @@ -19,21 +20,20 @@ namespace Umbraco.Cms.Core.PropertyEditors /// internal class FileUploadPropertyValueEditor : DataValueEditor { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; public FileUploadPropertyValueEditor( DataEditorAttribute attribute, - IMediaFileSystem mediaFileSystem, - IDataTypeService dataTypeService, - ILocalizationService localizationService, + MediaFileManager mediaFileManager, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IOptions contentSettings, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + IJsonSerializer jsonSerializer, + IIOHelper ioHelper) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings)); } @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Core.PropertyEditors { var currentPath = currentValue as string; if (!currentPath.IsNullOrWhiteSpace()) - currentPath = _mediaFileSystem.GetRelativePath(currentPath); + currentPath = _mediaFileManager.FileSystem.GetRelativePath(currentPath); string editorFile = null; if (editorValue.Value != null) @@ -84,7 +84,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // value is unchanged. if (string.IsNullOrWhiteSpace(editorFile) && string.IsNullOrWhiteSpace(currentPath) == false) { - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); return null; // clear } @@ -100,11 +100,11 @@ namespace Umbraco.Cms.Core.PropertyEditors // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); // update json and return if (editorFile == null) return null; - return filepath == null ? string.Empty : _mediaFileSystem.GetUrl(filepath); + return filepath == null ? string.Empty : _mediaFileManager.FileSystem.GetUrl(filepath); } @@ -118,14 +118,14 @@ namespace Umbraco.Cms.Core.PropertyEditors // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileSystem.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { // TODO: Here it would make sense to do the auto-fill properties stuff but the API doesn't allow us to do that right // since we'd need to be able to return values for other properties from these methods - _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! + _mediaFileManager.FileSystem.AddFile(filepath, filestream, true); // must overwrite! } return filepath; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index 1722fb1d2d..e3a6987110 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; @@ -40,19 +41,14 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IImageUrlGenerator _imageUrlGenerator; public GridPropertyEditor( - ILoggerFactory loggerFactory, + IDataValueEditorFactory dataValueEditorFactory, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages, HtmlLocalLinkParser localLinkParser, IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - IImageUrlGenerator imageUrlGenerator, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IImageUrlGenerator imageUrlGenerator) + : base(dataValueEditorFactory) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _ioHelper = ioHelper; @@ -68,7 +64,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Overridden to ensure that the value is validated /// /// - protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _backOfficeSecurityAccessor, DataTypeService, LocalizationService, LocalizedTextService, _imageSourceParser, _pastedImages, _localLinkParser, ShortStringHelper, _imageUrlGenerator, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor(_ioHelper); @@ -82,24 +78,25 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IImageUrlGenerator _imageUrlGenerator; public GridPropertyValueEditor( + IDataValueEditorFactory dataValueEditorFactory, DataEditorAttribute attribute, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, HtmlImageSourceParser imageSourceParser, RichTextEditorPastedImages pastedImages, - HtmlLocalLinkParser localLinkParser, IShortStringHelper shortStringHelper, IImageUrlGenerator imageUrlGenerator, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + IJsonSerializer jsonSerializer, + IIOHelper ioHelper) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _imageSourceParser = imageSourceParser; _pastedImages = pastedImages; - _richTextPropertyValueEditor = new RichTextPropertyEditor.RichTextPropertyValueEditor(attribute, backOfficeSecurityAccessor, dataTypeService, localizationService, localizedTextService, shortStringHelper, imageSourceParser, localLinkParser, pastedImages, imageUrlGenerator, jsonSerializer); - _mediaPickerPropertyValueEditor = new MediaPickerPropertyEditor.MediaPickerPropertyValueEditor(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute); + _richTextPropertyValueEditor = + dataValueEditorFactory.Create(attribute); + _mediaPickerPropertyValueEditor = + dataValueEditorFactory.Create(attribute); _imageUrlGenerator = imageUrlGenerator; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index e138e98618..f9425cbade 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -13,9 +13,9 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.PropertyEditors INotificationHandler, INotificationHandler, INotificationHandler { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IDataTypeService _dataTypeService; private readonly IIOHelper _ioHelper; @@ -49,20 +49,17 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public ImageCropperPropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, ILoggerFactory loggerFactory, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IDataTypeService dataTypeService, - ILocalizationService localizationService, IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - ILocalizedTextService localizedTextService, UploadAutoFillProperties uploadAutoFillProperties, - IJsonSerializer jsonSerializer, IContentService contentService) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + : base(dataValueEditorFactory) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings)); _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); @@ -86,7 +83,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Creates the corresponding property value editor. /// /// The corresponding property value editor. - protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, LoggerFactory.CreateLogger(), _mediaFileSystem, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _contentSettings, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); /// /// Creates the corresponding preValue editor. @@ -151,11 +148,11 @@ namespace Umbraco.Cms.Core.PropertyEditors { //check if the published value contains data and return it var src = GetFileSrcFromPropertyValue(propertyValue.PublishedValue, out var _); - if (src != null) yield return _mediaFileSystem.GetRelativePath(src); + if (src != null) yield return _mediaFileManager.FileSystem.GetRelativePath(src); //check if the edited value contains data and return it src = GetFileSrcFromPropertyValue(propertyValue.EditedValue, out var _); - if (src != null) yield return _mediaFileSystem.GetRelativePath(src); + if (src != null) yield return _mediaFileManager.FileSystem.GetRelativePath(src); } } @@ -174,7 +171,7 @@ namespace Umbraco.Cms.Core.PropertyEditors deserializedValue = GetJObject(str, true); if (deserializedValue?["src"] == null) return null; var src = deserializedValue["src"].Value(); - return relative ? _mediaFileSystem.GetRelativePath(src) : src; + return relative ? _mediaFileManager.FileSystem.GetRelativePath(src) : src; } /// @@ -198,9 +195,9 @@ namespace Umbraco.Cms.Core.PropertyEditors { continue; } - var sourcePath = _mediaFileSystem.GetRelativePath(src); - var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); - jo["src"] = _mediaFileSystem.GetUrl(copyPath); + var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(src); + var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath); + jo["src"] = _mediaFileManager.FileSystem.GetUrl(copyPath); notification.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } @@ -221,7 +218,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); - _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + _mediaFileManager.DeleteMediaFiles(filePathsToDelete); } public void Handle(MediaSavingNotification notification) @@ -282,7 +279,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (src == null) _autoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _autoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(src), pvalue.Culture, pvalue.Segment); + _autoFillProperties.Populate(model, autoFillConfig, _mediaFileManager.FileSystem.GetRelativePath(src), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs index 5cbbdf8e31..c375b6f576 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -3,9 +3,11 @@ using System; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -24,24 +26,26 @@ namespace Umbraco.Cms.Core.PropertyEditors internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core vs web? { private readonly ILogger _logger; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; + private readonly IDataTypeService _dataTypeService; public ImageCropperPropertyValueEditor( DataEditorAttribute attribute, ILogger logger, - IMediaFileSystem mediaFileSystem, - IDataTypeService dataTypeService, - ILocalizationService localizationService, + MediaFileManager mediaFileSystem, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, - ContentSettings contentSettings, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + IOptions contentSettings, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + IDataTypeService dataTypeService) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); - _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); + _mediaFileManager = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _contentSettings = contentSettings.Value; + _dataTypeService = dataTypeService; } /// @@ -63,7 +67,7 @@ namespace Umbraco.Cms.Core.PropertyEditors value = new ImageCropperValue { Src = val.ToString() }; } - var dataType = DataTypeService.GetDataType(property.PropertyType.DataTypeId); + var dataType = _dataTypeService.GetDataType(property.PropertyType.DataTypeId); if (dataType?.Configuration != null) value.ApplyConfiguration(dataType.ConfigurationAs()); @@ -97,7 +101,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _logger.LogWarning(ex, "Could not parse current db value to a JObject."); } if (string.IsNullOrWhiteSpace(currentPath) == false) - currentPath = _mediaFileSystem.GetRelativePath(currentPath); + currentPath = _mediaFileManager.FileSystem.GetRelativePath(currentPath); // get the new json and path JObject editorJson = null; @@ -130,7 +134,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // value is unchanged. if (string.IsNullOrWhiteSpace(editorFile) && string.IsNullOrWhiteSpace(currentPath) == false) { - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); return null; // clear } @@ -146,11 +150,11 @@ namespace Umbraco.Cms.Core.PropertyEditors // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); // update json and return if (editorJson == null) return null; - editorJson["src"] = filepath == null ? string.Empty : _mediaFileSystem.GetUrl(filepath); + editorJson["src"] = filepath == null ? string.Empty : _mediaFileManager.FileSystem.GetUrl(filepath); return editorJson.ToString(); } @@ -163,21 +167,21 @@ namespace Umbraco.Cms.Core.PropertyEditors // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileSystem.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { // TODO: Here it would make sense to do the auto-fill properties stuff but the API doesn't allow us to do that right // since we'd need to be able to return values for other properties from these methods - _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! + _mediaFileManager.FileSystem.AddFile(filepath, filestream, true); // must overwrite! } return filepath; } - public override string ConvertDbToString(IPropertyType propertyType, object value, IDataTypeService dataTypeService) + public override string ConvertDbToString(IPropertyType propertyType, object value) { if (value == null || string.IsNullOrEmpty(value.ToString())) return null; @@ -188,7 +192,7 @@ namespace Umbraco.Cms.Core.PropertyEditors return val; // more magic here ;-( - var configuration = DataTypeService.GetDataType(propertyType.DataTypeId).ConfigurationAs(); + var configuration = _dataTypeService.GetDataType(propertyType.DataTypeId).ConfigurationAs(); var crops = configuration?.Crops ?? Array.Empty(); return JsonConvert.SerializeObject(new diff --git a/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs index c08d64dd50..f965db9a40 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/LabelPropertyEditor.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Serialization; @@ -26,14 +27,15 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// Initializes a new instance of the class. /// - public LabelPropertyEditor(ILoggerFactory loggerFactory, IIOHelper ioHelper, IDataTypeService dataTypeService, ILocalizedTextService localizedTextService, ILocalizationService localizationService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + public LabelPropertyEditor(IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } /// - protected override IDataValueEditor CreateValueEditor() => new LabelPropertyValueEditor(DataTypeService, LocalizationService,LocalizedTextService, ShortStringHelper, Attribute, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); /// protected override IConfigurationEditor CreateConfigurationEditor() => new LabelConfigurationEditor(_ioHelper); @@ -41,8 +43,13 @@ namespace Umbraco.Cms.Core.PropertyEditors // provides the property value editor internal class LabelPropertyValueEditor : DataValueEditor { - public LabelPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + public LabelPropertyValueEditor( + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + DataEditorAttribute attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { } /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs index 2592c52513..f9b862c9f0 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ListViewPropertyEditor.cs @@ -27,20 +27,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public ListViewPropertyEditor( - ILoggerFactory loggerFactory, - IIOHelper iioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base( - loggerFactory, - dataTypeService, - localizationService, - localizedTextService, - shortStringHelper, - jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper iioHelper) + : base(dataValueEditorFactory) { _iioHelper = iioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs index fca74a7873..01dab994f9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MarkdownPropertyEditor.cs @@ -27,14 +27,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public MarkdownPropertyEditor( - ILoggerFactory loggerFactory, - IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3ConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3ConfigurationEditor.cs new file mode 100644 index 0000000000..faa171eb40 --- /dev/null +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3ConfigurationEditor.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Represents the configuration editor for the media picker value editor. + /// + public class MediaPicker3ConfigurationEditor : ConfigurationEditor + { + /// + /// Initializes a new instance of the class. + /// + public MediaPicker3ConfigurationEditor(IIOHelper ioHelper) : base(ioHelper) + { + // configure fields + // this is not part of ContentPickerConfiguration, + // but is required to configure the UI editor (when editing the configuration) + + Field(nameof(MediaPicker3Configuration.StartNodeId)) + .Config = new Dictionary { { "idType", "udi" } }; + + Field(nameof(MediaPicker3Configuration.Filter)) + .Config = new Dictionary { { "itemType", "media" } }; + } + } +} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs new file mode 100644 index 0000000000..0b80116c2e --- /dev/null +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Editors; +using Umbraco.Cms.Core.PropertyEditors.ValueConverters; +using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; + +namespace Umbraco.Cms.Core.PropertyEditors +{ + /// + /// Represents a media picker property editor. + /// + [DataEditor( + Constants.PropertyEditors.Aliases.MediaPicker3, + EditorType.PropertyValue, + "Media Picker", + "mediapicker3", + ValueType = ValueTypes.Json, + Group = Constants.PropertyEditors.Groups.Media, + Icon = Constants.Icons.MediaImage)] + public class MediaPicker3PropertyEditor : DataEditor + { + private readonly IIOHelper _ioHelper; + + public MediaPicker3PropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper, + EditorType type = EditorType.PropertyValue) + : base(dataValueEditorFactory, type) + { + _ioHelper = ioHelper; + } + + /// + protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPicker3ConfigurationEditor(_ioHelper); + + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); + + internal class MediaPicker3PropertyValueEditor : DataValueEditor, IDataValueReference + { + private readonly IJsonSerializer _jsonSerializer; + + public MediaPicker3PropertyValueEditor( + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + DataEditorAttribute attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) + { + _jsonSerializer = jsonSerializer; + } + + /// + /// Note: no FromEditor() and ToEditor() methods + /// We do not want to transform the way the data is stored in the DB and would like to keep a raw JSON string + /// + + public IEnumerable GetReferences(object value) + { + var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString(); + + if (string.IsNullOrWhiteSpace(rawJson)) + yield break; + + var mediaWithCropsDtos = _jsonSerializer.Deserialize(rawJson); + + foreach (var mediaWithCropsDto in mediaWithCropsDtos) + { + yield return new UmbracoEntityReference(GuidUdi.Create(Constants.UdiEntityType.Media, mediaWithCropsDto.MediaKey)); + } + } + + } + } +} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs index 3ebf64e965..a3cd7278a6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -18,11 +19,12 @@ namespace Umbraco.Cms.Core.PropertyEditors [DataEditor( Constants.PropertyEditors.Aliases.MediaPicker, EditorType.PropertyValue | EditorType.MacroParameter, - "Media Picker", + "(Obsolete)Media Picker", "mediapicker", ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.Media, - Icon = Constants.Icons.MediaImage)] + Icon = Constants.Icons.MediaImage, + IsDeprecated = true)] public class MediaPickerPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; @@ -31,14 +33,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public MediaPickerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - ILocalizedTextService localizedTextService, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } @@ -46,18 +43,17 @@ namespace Umbraco.Cms.Core.PropertyEditors /// protected override IConfigurationEditor CreateConfigurationEditor() => new MediaPickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MediaPickerPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, JsonSerializer, Attribute); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); public class MediaPickerPropertyValueEditor : DataValueEditor, IDataValueReference { public MediaPickerPropertyValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, + IIOHelper ioHelper, DataEditorAttribute attribute) - : base(dataTypeService,localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index e8973d1d89..735aca9829 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -24,32 +25,26 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IIOHelper _ioHelper; public MultiNodeTreePickerPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } protected override IConfigurationEditor CreateConfigurationEditor() => new MultiNodePickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MultiNodeTreePickerPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, JsonSerializer, Attribute); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); public class MultiNodeTreePickerPropertyValueEditor : DataValueEditor, IDataValueReference { public MultiNodeTreePickerPropertyValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, + IIOHelper ioHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs index 59f9ce8c86..dada3e64fc 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerPropertyEditor.cs @@ -3,6 +3,7 @@ using System; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PublishedCache; @@ -24,35 +25,18 @@ namespace Umbraco.Cms.Core.PropertyEditors Icon = "icon-link")] public class MultiUrlPickerPropertyEditor : DataEditor { - private readonly Lazy _entityService; - private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IIOHelper _ioHelper; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IPublishedUrlProvider _publishedUrlProvider; public MultiUrlPickerPropertyEditor( - ILoggerFactory loggerFactory, - Lazy entityService, - IPublishedSnapshotAccessor publishedSnapshotAccessor, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - IUmbracoContextAccessor umbracoContextAccessor, - IPublishedUrlProvider publishedUrlProvider, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, EditorType.PropertyValue) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory, EditorType.PropertyValue) { - _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); - _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); _ioHelper = ioHelper; - _umbracoContextAccessor = umbracoContextAccessor; - _publishedUrlProvider = publishedUrlProvider; } protected override IConfigurationEditor CreateConfigurationEditor() => new MultiUrlPickerConfigurationEditor(_ioHelper); - protected override IDataValueEditor CreateValueEditor() => new MultiUrlPickerValueEditor(_entityService.Value, _publishedSnapshotAccessor, LoggerFactory.CreateLogger(), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, Attribute, _umbracoContextAccessor, _publishedUrlProvider, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs index 4f03cf33a8..11d49b5bf9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultiUrlPickerValueEditor.cs @@ -7,6 +7,8 @@ using System.Linq; using System.Runtime.Serialization; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.Editors; @@ -25,7 +27,6 @@ namespace Umbraco.Cms.Core.PropertyEditors { private readonly IEntityService _entityService; private readonly ILogger _logger; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; @@ -33,20 +34,17 @@ namespace Umbraco.Cms.Core.PropertyEditors IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, - IUmbracoContextAccessor umbracoContextAccessor, IPublishedUrlProvider publishedUrlProvider, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + IJsonSerializer jsonSerializer, + IIOHelper ioHelper) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _publishedUrlProvider = publishedUrlProvider; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs index 7c23be6428..c3a5bae383 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -8,6 +8,7 @@ using System.Linq; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using Umbraco.Cms.Core.Exceptions; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -31,31 +32,20 @@ namespace Umbraco.Cms.Core.PropertyEditors public class MultipleTextStringPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; - private readonly ILocalizedTextService _localizedTextService; /// /// Initializes a new instance of the class. /// public MultipleTextStringPropertyEditor( - ILoggerFactory loggerFactory, IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; - _dataTypeService = dataTypeService; - _localizationService = localizationService; - _localizedTextService = localizedTextService; } /// - protected override IDataValueEditor CreateValueEditor() => new MultipleTextStringPropertyValueEditor(_dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, JsonSerializer, Attribute); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); /// protected override IConfigurationEditor CreateConfigurationEditor() => new MultipleTextStringConfigurationEditor(_ioHelper); @@ -68,13 +58,12 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly ILocalizedTextService _localizedTextService; public MultipleTextStringPropertyValueEditor( - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, + IIOHelper ioHelper, DataEditorAttribute attribute) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _localizedTextService = localizedTextService; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs index b222956657..c0e544b50c 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MultipleValueEditor.cs @@ -6,6 +6,8 @@ using System.Linq; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; using Umbraco.Cms.Core.Serialization; @@ -22,20 +24,15 @@ namespace Umbraco.Cms.Core.PropertyEditors /// public class MultipleValueEditor : DataValueEditor { - private readonly ILogger _logger; - public MultipleValueEditor( - ILogger logger, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, + IIOHelper ioHelper, DataEditorAttribute attribute ) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { - _logger = logger; } /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index bf64b3b334..452c27c096 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -7,11 +7,13 @@ using System.Linq; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -29,34 +31,19 @@ namespace Umbraco.Cms.Core.PropertyEditors Icon = "icon-thumbnail-list")] public class NestedContentPropertyEditor : DataEditor { - private readonly Lazy _propertyEditors; - private readonly IContentTypeService _contentTypeService; private readonly IIOHelper _ioHelper; - private readonly ILocalizedTextService _localizedTextService; + public const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; public NestedContentPropertyEditor( - ILoggerFactory loggerFactory, - Lazy propertyEditors, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IContentTypeService contentTypeService, - IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - ILocalizedTextService localizedTextService, - IJsonSerializer jsonSerializer) - : base (loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base (dataValueEditorFactory) { - _propertyEditors = propertyEditors; - _contentTypeService = contentTypeService; _ioHelper = ioHelper; - _localizedTextService = localizedTextService; } - // has to be lazy else circular dep in ctor - private PropertyEditorCollection PropertyEditors => _propertyEditors.Value; - #region Pre Value Editor protected override IConfigurationEditor CreateConfigurationEditor() => new NestedContentConfigurationEditor(_ioHelper); @@ -65,40 +52,34 @@ namespace Umbraco.Cms.Core.PropertyEditors #region Value Editor - protected override IDataValueEditor CreateValueEditor() => new NestedContentPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, _contentTypeService, ShortStringHelper, Attribute, PropertyEditors, LoggerFactory.CreateLogger(), JsonSerializer); + protected override IDataValueEditor CreateValueEditor() + => DataValueEditorFactory.Create(Attribute); internal class NestedContentPropertyValueEditor : DataValueEditor, IDataValueReference { private readonly PropertyEditorCollection _propertyEditors; - private readonly IContentTypeService _contentTypeService; private readonly IDataTypeService _dataTypeService; private readonly ILogger _logger; private readonly NestedContentValues _nestedContentValues; - private readonly Lazy> _contentTypes; - public NestedContentPropertyValueEditor( IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IContentTypeService contentTypeService, IShortStringHelper shortStringHelper, DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, ILogger logger, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + IPropertyValidationService propertyValidationService) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _propertyEditors = propertyEditors; - _contentTypeService = contentTypeService; _dataTypeService = dataTypeService; _logger = logger; _nestedContentValues = new NestedContentValues(contentTypeService); - Validators.Add(new NestedContentValidator(_nestedContentValues, propertyEditors, dataTypeService, localizedTextService, contentTypeService)); - - _contentTypes = new Lazy>(() => - _contentTypeService.GetAll().ToDictionary(c => c.Alias) - ); + Validators.Add(new NestedContentValidator(propertyValidationService, _nestedContentValues, contentTypeService)); } /// @@ -119,7 +100,7 @@ namespace Umbraco.Cms.Core.PropertyEditors #region DB to String - public override string ConvertDbToString(IPropertyType propertyType, object propertyValue, IDataTypeService dataTypeService) + public override string ConvertDbToString(IPropertyType propertyType, object propertyValue) { var rows = _nestedContentValues.GetPropertyValues(propertyValue); @@ -136,9 +117,9 @@ namespace Umbraco.Cms.Core.PropertyEditors var propEditor = _propertyEditors[prop.Value.PropertyType.PropertyEditorAlias]; if (propEditor == null) continue; - var tempConfig = DataTypeService.GetDataType(prop.Value.PropertyType.DataTypeId).Configuration; + var tempConfig = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeId).Configuration; var valEditor = propEditor.GetValueEditor(tempConfig); - var convValue = valEditor.ConvertDbToString(prop.Value.PropertyType, prop.Value.Value, dataTypeService); + var convValue = valEditor.ConvertDbToString(prop.Value.PropertyType, prop.Value.Value); // update the raw value since this is what will get serialized out row.RawPropertyValues[prop.Key] = convValue; @@ -205,7 +186,7 @@ namespace Umbraco.Cms.Core.PropertyEditors continue; } - var tempConfig = DataTypeService.GetDataType(prop.Value.PropertyType.DataTypeId).Configuration; + var tempConfig = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeId).Configuration; var valEditor = propEditor.GetValueEditor(tempConfig); var convValue = valEditor.ToEditor(tempProp); @@ -306,8 +287,8 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly NestedContentValues _nestedContentValues; private readonly IContentTypeService _contentTypeService; - public NestedContentValidator(NestedContentValues nestedContentValues, PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService, IContentTypeService contentTypeService) - : base(propertyEditors, dataTypeService, textService) + public NestedContentValidator(IPropertyValidationService propertyValidationService, NestedContentValues nestedContentValues, IContentTypeService contentTypeService) + : base(propertyValidationService) { _nestedContentValues = nestedContentValues; _contentTypeService = contentTypeService; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs index d96ffef56d..25b96e3ee6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RadioButtonsPropertyEditor.cs @@ -22,27 +22,25 @@ namespace Umbraco.Cms.Core.PropertyEditors public class RadioButtonsPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; + private readonly ILocalizedTextService _localizedTextService; /// /// The constructor will setup the property editor based on the attribute if one is found /// public RadioButtonsPropertyEditor( - ILoggerFactory loggerFactory, + IDataValueEditorFactory dataValueEditorFactory, IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer) + ILocalizedTextService localizedTextService) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; + _localizedTextService = localizedTextService; } /// /// Return a custom pre-value editor /// /// - protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(LocalizedTextService, _ioHelper); + protected override IConfigurationEditor CreateConfigurationEditor() => new ValueListConfigurationEditor(_localizedTextService, _ioHelper); } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index 94140d00f2..f92b2911dd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -27,21 +27,30 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IHostingEnvironment _hostingEnvironment; private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IShortStringHelper _shortStringHelper; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IJsonSerializer _serializer; const string TemporaryImageDataAttribute = "data-tmpimg"; - public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IHostingEnvironment hostingEnvironment, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IPublishedUrlProvider publishedUrlProvider, IJsonSerializer serializer) + public RichTextEditorPastedImages( + IUmbracoContextAccessor umbracoContextAccessor, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IMediaService mediaService, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + MediaFileManager mediaFileManager, + IShortStringHelper shortStringHelper, + IPublishedUrlProvider publishedUrlProvider, + IJsonSerializer serializer) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _hostingEnvironment = hostingEnvironment; _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _shortStringHelper = shortStringHelper; _publishedUrlProvider = publishedUrlProvider; _serializer = serializer; @@ -98,7 +107,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (fileStream == null) throw new InvalidOperationException("Could not acquire file stream"); using (fileStream) { - mediaFile.SetValue(_mediaFileSystem, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); + mediaFile.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); } _mediaService.Save(mediaFile, userId); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs index 213697ed33..50b6d7a881 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; @@ -43,19 +44,14 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The constructor will setup the property editor based on the attribute if one is found /// public RichTextPropertyEditor( - ILoggerFactory loggerFactory, + IDataValueEditorFactory dataValueEditorFactory, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IDataTypeService dataTypeService, - ILocalizationService localizationService, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser, RichTextEditorPastedImages pastedImages, - IShortStringHelper shortStringHelper, IIOHelper ioHelper, - ILocalizedTextService localizedTextService, - IImageUrlGenerator imageUrlGenerator, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IImageUrlGenerator imageUrlGenerator) + : base(dataValueEditorFactory) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _imageSourceParser = imageSourceParser; @@ -69,7 +65,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _backOfficeSecurityAccessor, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _imageSourceParser, _localLinkParser, _pastedImages, _imageUrlGenerator, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(_ioHelper); @@ -89,16 +85,15 @@ namespace Umbraco.Cms.Core.PropertyEditors public RichTextPropertyValueEditor( DataEditorAttribute attribute, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - IDataTypeService dataTypeService, - ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser, RichTextEditorPastedImages pastedImages, IImageUrlGenerator imageUrlGenerator, - IJsonSerializer jsonSerializer) - : base(dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer, attribute) + IJsonSerializer jsonSerializer, + IIOHelper ioHelper) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _imageSourceParser = imageSourceParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs index dc4942d51b..ca4072950f 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/SliderPropertyEditor.cs @@ -25,14 +25,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public SliderPropertyEditor( - ILoggerFactory loggerFactory, - IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs index 958cd43d7b..4683851936 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TagsPropertyEditor.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -30,30 +31,34 @@ namespace Umbraco.Cms.Core.PropertyEditors { private readonly ManifestValueValidatorCollection _validators; private readonly IIOHelper _ioHelper; + private readonly ILocalizedTextService _localizedTextService; public TagsPropertyEditor( + IDataValueEditorFactory dataValueEditorFactory, ManifestValueValidatorCollection validators, - ILoggerFactory loggerFactory, IIOHelper ioHelper, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + ILocalizedTextService localizedTextService) + : base(dataValueEditorFactory) { _validators = validators; _ioHelper = ioHelper; + _localizedTextService = localizedTextService; } - protected override IDataValueEditor CreateValueEditor() => new TagPropertyValueEditor(DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, JsonSerializer, Attribute); + protected override IDataValueEditor CreateValueEditor() => + DataValueEditorFactory.Create(Attribute); - protected override IConfigurationEditor CreateConfigurationEditor() => new TagConfigurationEditor(_validators, _ioHelper, LocalizedTextService); + protected override IConfigurationEditor CreateConfigurationEditor() => new TagConfigurationEditor(_validators, _ioHelper, _localizedTextService); internal class TagPropertyValueEditor : DataValueEditor { - public TagPropertyValueEditor(IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, DataEditorAttribute attribute) - : base(dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer, attribute) + public TagPropertyValueEditor( + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + IIOHelper ioHelper, + DataEditorAttribute attribute) + : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { } /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs index a742d0abba..838cd7e7fd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextAreaPropertyEditor.cs @@ -22,34 +22,21 @@ namespace Umbraco.Cms.Core.PropertyEditors Icon = "icon-application-window-alt")] public class TextAreaPropertyEditor : DataEditor { - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - private readonly ILocalizedTextService _localizedTextService; - private readonly IShortStringHelper _shortStringHelper; /// /// Initializes a new instance of the class. /// public TextAreaPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IIOHelper ioHelper, - ILocalizedTextService localizedTextService, - IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; - _localizedTextService = localizedTextService; - _shortStringHelper = shortStringHelper; } /// - protected override IDataValueEditor CreateValueEditor() => new TextOnlyValueEditor(_dataTypeService, _localizationService, Attribute, _localizedTextService, _shortStringHelper, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create(Attribute); /// protected override IConfigurationEditor CreateConfigurationEditor() => new TextAreaConfigurationEditor(_ioHelper); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs index 02b49fd339..efea850fcd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TextboxPropertyEditor.cs @@ -21,34 +21,22 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.Common)] public class TextboxPropertyEditor : DataEditor { - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly IIOHelper _ioHelper; - private readonly IShortStringHelper _shortStringHelper; - private readonly ILocalizedTextService _localizedTextService; /// /// Initializes a new instance of the class. /// public TextboxPropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - ILocalizedTextService localizedTextService, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService,localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { - _dataTypeService = dataTypeService; - _localizationService = localizationService; _ioHelper = ioHelper; - _shortStringHelper = shortStringHelper; - _localizedTextService = localizedTextService; } /// - protected override IDataValueEditor CreateValueEditor() => new TextOnlyValueEditor(DataTypeService, LocalizationService, Attribute, LocalizedTextService, ShortStringHelper, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => + DataValueEditorFactory.Create(Attribute); /// protected override IConfigurationEditor CreateConfigurationEditor() => new TextboxConfigurationEditor(_ioHelper); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs index b0f2e3ea97..a594f946d5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/TrueFalsePropertyEditor.cs @@ -28,14 +28,9 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Initializes a new instance of the class. /// public TrueFalsePropertyEditor( - ILoggerFactory loggerFactory, - IDataTypeService dataTypeService, - ILocalizationService localizationService, - IIOHelper ioHelper, - IShortStringHelper shortStringHelper, - ILocalizedTextService localizedTextService, - IJsonSerializer jsonSerializer) - : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) + IDataValueEditorFactory dataValueEditorFactory, + IIOHelper ioHelper) + : base(dataValueEditorFactory) { _ioHelper = ioHelper; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/JsonValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/JsonValueConverter.cs index 28771a09cf..9f78872ec1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/JsonValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/JsonValueConverter.cs @@ -3,6 +3,7 @@ using System; using Microsoft.Extensions.Logging; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Cms.Core.Models.PublishedContent; @@ -22,6 +23,8 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters private readonly PropertyEditorCollection _propertyEditors; private readonly ILogger _logger; + string[] ExcludedPropertyEditors = new string[] { Constants.PropertyEditors.Aliases.MediaPicker3 }; + /// /// Initializes a new instance of the class. /// @@ -33,13 +36,16 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters /// /// It is a converter for any value type that is "JSON" + /// Unless it's in the Excluded Property Editors list + /// The new MediaPicker 3 stores JSON but we want to use its own ValueConvertor /// /// /// public override bool IsConverter(IPublishedPropertyType propertyType) { return _propertyEditors.TryGet(propertyType.EditorAlias, out var editor) - && editor.GetValueEditor().ValueType.InvariantEquals(ValueTypes.Json); + && editor.GetValueEditor().ValueType.InvariantEquals(ValueTypes.Json) + && ExcludedPropertyEditors.Contains(propertyType.EditorAlias) == false; } public override Type GetPropertyValueType(IPublishedPropertyType propertyType) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs new file mode 100644 index 0000000000..5d5bc593a6 --- /dev/null +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MediaPickerWithCropsValueConverter.cs @@ -0,0 +1,124 @@ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Routing; +using Umbraco.Core.Models; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters +{ + [DefaultPropertyValueConverter] + public class MediaPickerWithCropsValueConverter : PropertyValueConverterBase + { + + private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private readonly IPublishedUrlProvider _publishedUrlProvider; + + public MediaPickerWithCropsValueConverter( + IPublishedSnapshotAccessor publishedSnapshotAccessor, + IPublishedUrlProvider publishedUrlProvider) + { + _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + _publishedUrlProvider = publishedUrlProvider; + } + + public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; + + /// + /// Enusre this property value convertor is for the New Media Picker with Crops aka MediaPicker 3 + /// + public override bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals(Core.Constants.PropertyEditors.Aliases.MediaPicker3); + + /// + /// Check if the raw JSON value is not an empty array + /// + public override bool? IsValue(object value, PropertyValueLevel level) => value?.ToString() != "[]"; + + /// + /// What C# model type does the raw JSON return for Models & Views + /// + public override Type GetPropertyValueType(IPublishedPropertyType propertyType) + { + // Check do we want to return IPublishedContent collection still or a NEW model ? + var isMultiple = IsMultipleDataType(propertyType.DataType); + return isMultiple + ? typeof(IEnumerable) + : typeof(MediaWithCrops); + } + + public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) => source?.ToString(); + + public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + { + var mediaItems = new List(); + var isMultiple = IsMultipleDataType(propertyType.DataType); + if (inter == null) + { + return isMultiple ? mediaItems: null; + } + + var dtos = JsonConvert.DeserializeObject>(inter.ToString()); + + foreach(var media in dtos) + { + var item = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(media.MediaKey); + if (item != null) + { + mediaItems.Add(new MediaWithCrops + { + MediaItem = item, + LocalCrops = new ImageCropperValue + { + Crops = media.Crops, + FocalPoint = media.FocalPoint, + Src = item.Url(_publishedUrlProvider) + } + }); + } + } + + return isMultiple ? mediaItems : FirstOrDefault(mediaItems); + } + + /// + /// Is the media picker configured to pick multiple media items + /// + /// + /// + private bool IsMultipleDataType(PublishedDataType dataType) + { + var config = dataType.ConfigurationAs(); + return config.Multiple; + } + + private object FirstOrDefault(IList mediaItems) + { + return mediaItems.Count == 0 ? null : mediaItems[0]; + } + + + /// + /// Model/DTO that represents the JSON that the MediaPicker3 stores + /// + [DataContract] + internal class MediaWithCropsDto + { + [DataMember(Name = "key")] + public Guid Key { get; set; } + + [DataMember(Name = "mediaKey")] + public Guid MediaKey { get; set; } + + [DataMember(Name = "crops")] + public IEnumerable Crops { get; set; } + + [DataMember(Name = "focalPoint")] + public ImageCropperValue.ImageCropperFocalPoint FocalPoint { get; set; } + } + } +} diff --git a/src/Umbraco.Infrastructure/PublishedContentQuery.cs b/src/Umbraco.Infrastructure/PublishedContentQuery.cs index 1d13748aeb..47b98d8dc0 100644 --- a/src/Umbraco.Infrastructure/PublishedContentQuery.cs +++ b/src/Umbraco.Infrastructure/PublishedContentQuery.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Xml.XPath; using Examine; using Examine.Search; @@ -260,7 +261,7 @@ namespace Umbraco.Cms.Infrastructure $"No index found by name {indexName} or is not of type {typeof(IUmbracoIndex)}"); } - var query = umbIndex.GetSearcher().CreateQuery(IndexTypes.Content); + var query = umbIndex.Searcher.CreateQuery(IndexTypes.Content); IQueryExecutor queryExecutor; if (culture == "*") @@ -286,7 +287,7 @@ namespace Umbraco.Cms.Infrastructure var results = skip == 0 && take == 0 ? queryExecutor.Execute() - : queryExecutor.Execute(skip + take); + : queryExecutor.Execute(QueryOptions.SkipTake(skip, take)); totalRecords = results.TotalItemCount; @@ -316,7 +317,7 @@ namespace Umbraco.Cms.Infrastructure var results = skip == 0 && take == 0 ? query.Execute() - : query.Execute(skip + take); + : query.Execute(QueryOptions.SkipTake(skip, take)); totalRecords = results.TotalItemCount; diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs index d71b5570d2..7b3b3f86ed 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs @@ -9,9 +9,9 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index ef1d808b9a..0a262b58c5 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Migrations.Install; @@ -30,6 +31,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime private readonly IHostingEnvironment _hostingEnvironment; private readonly DatabaseBuilder _databaseBuilder; private readonly IUmbracoVersion _umbracoVersion; + private CancellationToken _cancellationToken; /// /// Initializes a new instance of the class. @@ -66,9 +68,17 @@ namespace Umbraco.Cms.Infrastructure.Runtime /// public IRuntimeState State { get; } + /// + public async Task RestartAsync() + { + await StopAsync(_cancellationToken); + await StartAsync(_cancellationToken); + } + /// public async Task StartAsync(CancellationToken cancellationToken) { + _cancellationToken = cancellationToken; StaticApplicationLogging.Initialize(_loggerFactory); AppDomain.CurrentDomain.UnhandledException += (_, args) => @@ -90,6 +100,11 @@ namespace Umbraco.Cms.Infrastructure.Runtime AppDomain.CurrentDomain.SetData("DataDirectory", _hostingEnvironment?.MapPathContentRoot(Constants.SystemDirectories.Data)); + // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate + AcquireMainDom(); + + await _eventAggregator.PublishAsync(new UmbracoApplicationMainDomAcquiredNotification(), cancellationToken); + DoUnattendedInstall(); DetermineRuntimeLevel(); @@ -104,9 +119,6 @@ namespace Umbraco.Cms.Infrastructure.Runtime throw new InvalidOperationException($"An instance of {typeof(IApplicationShutdownRegistry)} could not be resolved from the container, ensure that one if registered in your runtime before calling {nameof(IRuntime)}.{nameof(StartAsync)}"); } - // acquire the main domain - if this fails then anything that should be registered with MainDom will not operate - AcquireMainDom(); - // if level is Run and reason is UpgradeMigrations, that means we need to perform an unattended upgrade if (State.Reason == RuntimeLevelReason.UpgradeMigrations && State.Level == RuntimeLevel.Run) { @@ -115,13 +127,14 @@ namespace Umbraco.Cms.Infrastructure.Runtime // upgrade is done, set reason to Run DetermineRuntimeLevel(); - } - await _eventAggregator.PublishAsync(new UmbracoApplicationStarting(State.Level), cancellationToken); + await _eventAggregator.PublishAsync(new UmbracoApplicationComponentsInstallingNotification(State.Level), cancellationToken); // create & initialize the components _components.Initialize(); + + await _eventAggregator.PublishAsync(new UmbracoApplicationStartingNotification(State.Level), cancellationToken); } private void DoUnattendedUpgrade() @@ -136,15 +149,12 @@ namespace Umbraco.Cms.Infrastructure.Runtime } - private void DoUnattendedInstall() - { - State.DoUnattendedInstall(); - } + private void DoUnattendedInstall() => State.DoUnattendedInstall(); public async Task StopAsync(CancellationToken cancellationToken) { _components.Terminate(); - await _eventAggregator.PublishAsync(new UmbracoApplicationStopping(), cancellationToken); + await _eventAggregator.PublishAsync(new UmbracoApplicationStoppingNotification(), cancellationToken); StaticApplicationLogging.Initialize(null); } diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs index c2c3262047..1c02898334 100644 --- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime dbProviderFactoryCreator, databaseSchemaCreatorFactory); - MainDomKey = MainDomKeyPrefix + "-" + (NetworkHelper.MachineName + MainDom.GetMainDomId(_hostingEnvironment)).GenerateHash(); + MainDomKey = MainDomKeyPrefix + "-" + (Environment.MachineName + MainDom.GetMainDomId(_hostingEnvironment)).GenerateHash(); } public async Task AcquireLockAsync(int millisecondsTimeout) diff --git a/src/Umbraco.Infrastructure/RuntimeState.cs b/src/Umbraco.Infrastructure/RuntimeState.cs index 76700e2001..894536e4e7 100644 --- a/src/Umbraco.Infrastructure/RuntimeState.cs +++ b/src/Umbraco.Infrastructure/RuntimeState.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Semver; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Migrations.Install; diff --git a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs index 12c081ef81..d1f82d2189 100644 --- a/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/IScopeProvider.cs @@ -19,6 +19,7 @@ namespace Umbraco.Cms.Core.Scoping /// The transaction isolation level. /// The repositories cache mode. /// An optional events dispatcher. + /// An optional notification publisher. /// A value indicating whether to scope the filesystems. /// A value indicating whether this scope should always be registered in the call context. /// A value indicating whether this scope is auto-completed. @@ -35,6 +36,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false); @@ -46,6 +48,7 @@ namespace Umbraco.Cms.Core.Scoping /// The transaction isolation level. /// The repositories cache mode. /// An optional events dispatcher. + /// An option notification publisher. /// A value indicating whether to scope the filesystems. /// /// A detached scope is not ambient and has no parent. @@ -56,6 +59,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null); /// diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 386bd7c06e..b50b8ec0e3 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Core.Scoping { private readonly ScopeProvider _scopeProvider; private readonly CoreDebugSettings _coreDebugSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IEventAggregator _eventAggregator; private readonly ILogger _logger; @@ -39,7 +39,6 @@ namespace Umbraco.Cms.Core.Scoping private EventMessages _messages; private ICompletable _fscope; private IEventDispatcher _eventDispatcher; - // eventually this may need to be injectable - for now we'll create it explicitly and let future needs determine if it should be injectable private IScopedNotificationPublisher _notificationPublisher; private readonly object _dictionaryLocker; @@ -55,7 +54,7 @@ namespace Umbraco.Cms.Core.Scoping private Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -65,13 +64,14 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) { _scopeProvider = scopeProvider; _coreDebugSettings = coreDebugSettings; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _eventAggregator = eventAggregator; _logger = logger; @@ -80,6 +80,7 @@ namespace Umbraco.Cms.Core.Scoping _isolationLevel = isolationLevel; _repositoryCacheMode = repositoryCacheMode; _eventDispatcher = eventDispatcher; + _notificationPublisher = notificationPublisher; _scopeFileSystem = scopeFileSystems; _callContext = callContext; _autoComplete = autoComplete; @@ -140,6 +141,12 @@ namespace Umbraco.Cms.Core.Scoping throw new ArgumentException("Value cannot be specified on nested scope.", nameof(eventDispatcher)); } + // Only the outermost scope can specify the notification publisher + if (_notificationPublisher != null) + { + throw new ArgumentException("Value cannot be specified on nested scope.", nameof(notificationPublisher)); + } + // cannot specify a different fs scope! // can be 'true' only on outer scope (and false does not make much sense) if (scopeFileSystems != null && parent._scopeFileSystem != scopeFileSystems) @@ -163,7 +170,7 @@ namespace Umbraco.Cms.Core.Scoping public Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -172,17 +179,18 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopedNotificationPublisher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent public Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -190,10 +198,11 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -397,7 +406,7 @@ namespace Umbraco.Cms.Core.Scoping return ParentScope.Events; } - return _eventDispatcher ??= new QueuingEventDispatcher(_mediaFileSystem); + return _eventDispatcher ??= new QueuingEventDispatcher(_mediaFileManager); } } @@ -406,7 +415,11 @@ namespace Umbraco.Cms.Core.Scoping get { EnsureNotDisposed(); - if (ParentScope != null) return ParentScope.Notifications; + if (ParentScope != null) + { + return ParentScope.Notifications; + } + return _notificationPublisher ?? (_notificationPublisher = new ScopedNotificationPublisher(_eventAggregator)); } } diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index c9e8b39947..1da8b7bf1c 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -29,19 +29,19 @@ namespace Umbraco.Cms.Core.Scoping private readonly IRequestCache _requestCache; private readonly FileSystems _fileSystems; private readonly CoreDebugSettings _coreDebugSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private static readonly AsyncLocal> s_scopeStack = new AsyncLocal>(); private static readonly AsyncLocal> s_scopeContextStack = new AsyncLocal>(); private static readonly string s_scopeItemKey = typeof(Scope).FullName; private static readonly string s_contextItemKey = typeof(ScopeProvider).FullName; private readonly IEventAggregator _eventAggregator; - public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) + public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, MediaFileManager mediaFileManager, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) { DatabaseFactory = databaseFactory; _fileSystems = fileSystems; _coreDebugSettings = coreDebugSettings.Value; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _logger = logger; _loggerFactory = loggerFactory; _requestCache = requestCache; @@ -380,8 +380,9 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher scopedNotificationPublisher = null, bool? scopeFileSystems = null) - => new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + => new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopedNotificationPublisher, scopeFileSystems); /// public void AttachScope(IScope other, bool callContext = false) @@ -451,6 +452,7 @@ namespace Umbraco.Cms.Core.Scoping IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, + IScopedNotificationPublisher notificationPublisher = null, bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) @@ -460,7 +462,7 @@ namespace Umbraco.Cms.Core.Scoping { IScopeContext ambientContext = AmbientContext; ScopeContext newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! PushAmbientScope(scope); if (newContext != null) @@ -470,7 +472,7 @@ namespace Umbraco.Cms.Core.Scoping return scope; } - var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete); PushAmbientScope(nested); return nested; } diff --git a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs b/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs deleted file mode 100644 index 2fbceb2f9a..0000000000 --- a/src/Umbraco.Infrastructure/Search/BackgroundIndexRebuilder.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Cms.Infrastructure.HostedServices; - -namespace Umbraco.Cms.Infrastructure.Search -{ - /// - /// Utility to rebuild all indexes on a background thread - /// - public class BackgroundIndexRebuilder - { - private readonly IndexRebuilder _indexRebuilder; - private readonly IBackgroundTaskQueue _backgroundTaskQueue; - - private readonly IMainDom _mainDom; - private readonly ILogger _logger; - - private volatile bool _isRunning = false; - private static readonly object s_rebuildLocker = new object(); - - /// - /// Initializes a new instance of the class. - /// - public BackgroundIndexRebuilder( - IMainDom mainDom, - ILogger logger, - IndexRebuilder indexRebuilder, - IBackgroundTaskQueue backgroundTaskQueue) - { - _mainDom = mainDom; - _logger = logger; - _indexRebuilder = indexRebuilder; - _backgroundTaskQueue = backgroundTaskQueue; - } - - - /// - /// Called to rebuild empty indexes on startup - /// - public virtual void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null) - { - - lock (s_rebuildLocker) - { - if (_isRunning) - { - _logger.LogWarning("Call was made to RebuildIndexes but the task runner for rebuilding is already running"); - return; - } - - _logger.LogInformation("Starting initialize async background thread."); - - _backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken => RebuildIndexes(onlyEmptyIndexes, delay ?? TimeSpan.Zero, cancellationToken)); - - } - } - - private Task RebuildIndexes(bool onlyEmptyIndexes, TimeSpan delay, CancellationToken cancellationToken) - { - if (!_mainDom.IsMainDom) - { - return Task.CompletedTask; - } - - if (delay > TimeSpan.Zero) - { - Thread.Sleep(delay); - } - - _isRunning = true; - _indexRebuilder.RebuildIndexes(onlyEmptyIndexes); - _isRunning = false; - return Task.CompletedTask; - } - } -} diff --git a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs b/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs deleted file mode 100644 index d22acb87e2..0000000000 --- a/src/Umbraco.Infrastructure/Search/ExamineNotificationHandler.cs +++ /dev/null @@ -1,837 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Sync; -using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Search -{ - public sealed class ExamineNotificationHandler : - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - private readonly IExamineManager _examineManager; - private readonly IContentValueSetBuilder _contentValueSetBuilder; - private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; - private readonly IValueSetBuilder _mediaValueSetBuilder; - private readonly IValueSetBuilder _memberValueSetBuilder; - private readonly BackgroundIndexRebuilder _backgroundIndexRebuilder; - private readonly TaskHelper _taskHelper; - private readonly IRuntimeState _runtimeState; - private readonly IScopeProvider _scopeProvider; - private readonly ServiceContext _services; - private readonly IMainDom _mainDom; - private readonly IProfilingLogger _profilingLogger; - private readonly ILogger _logger; - private readonly IUmbracoIndexesCreator _indexCreator; - private static bool s_deactivate_handlers; - - // the default enlist priority is 100 - // enlist with a lower priority to ensure that anything "default" runs after us - // but greater that SafeXmlReaderWriter priority which is 60 - private const int EnlistPriority = 80; - - public ExamineNotificationHandler(IMainDom mainDom, - IExamineManager examineManager, - IProfilingLogger profilingLogger, - ILogger logger, - IScopeProvider scopeProvider, - IUmbracoIndexesCreator indexCreator, - ServiceContext services, - IContentValueSetBuilder contentValueSetBuilder, - IPublishedContentValueSetBuilder publishedContentValueSetBuilder, - IValueSetBuilder mediaValueSetBuilder, - IValueSetBuilder memberValueSetBuilder, - BackgroundIndexRebuilder backgroundIndexRebuilder, - TaskHelper taskHelper, - IRuntimeState runtimeState) - { - _services = services; - _scopeProvider = scopeProvider; - _examineManager = examineManager; - _contentValueSetBuilder = contentValueSetBuilder; - _publishedContentValueSetBuilder = publishedContentValueSetBuilder; - _mediaValueSetBuilder = mediaValueSetBuilder; - _memberValueSetBuilder = memberValueSetBuilder; - _backgroundIndexRebuilder = backgroundIndexRebuilder; - _taskHelper = taskHelper; - _runtimeState = runtimeState; - _mainDom = mainDom; - _profilingLogger = profilingLogger; - _logger = logger; - _indexCreator = indexCreator; - } - public void Handle(UmbracoApplicationStarting notification) - { - //let's deal with shutting down Examine with MainDom - var examineShutdownRegistered = _mainDom.Register(release: () => - { - using (_profilingLogger.TraceDuration("Examine shutting down")) - { - _examineManager.Dispose(); - } - }); - - if (!examineShutdownRegistered) - { - _logger.LogInformation("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled"); - - //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! - Suspendable.ExamineEvents.SuspendIndexers(_logger); - return; //exit, do not continue - } - - //create the indexes and register them with the manager - foreach (IIndex index in _indexCreator.Create()) - { - _examineManager.AddIndex(index); - } - - _logger.LogDebug("Examine shutdown registered with MainDom"); - - var registeredIndexers = _examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); - - _logger.LogInformation("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); - - // don't bind event handlers if we're not suppose to listen - if (registeredIndexers == 0) - { - s_deactivate_handlers = true; - } - - if (_mainDom.IsMainDom && _runtimeState.Level >= RuntimeLevel.Run) - { - _backgroundIndexRebuilder.RebuildIndexes(true); - } - } - - - #region Cache refresher updated event handlers - - /// - /// Updates indexes based on content changes - /// - /// - /// - public void Handle(ContentCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - - var contentService = _services.ContentService; - - foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) - { - if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) - { - // delete content entirely (with descendants) - // false: remove entirely from all indexes - DeleteIndexForEntity(payload.Id, false); - } - else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) - { - // ExamineEvents does not support RefreshAll - // just ignore that payload - // so what?! - - // TODO: Rebuild the index at this point? - } - else // RefreshNode or RefreshBranch (maybe trashed) - { - // don't try to be too clever - refresh entirely - // there has to be race conditions in there ;-( - - var content = contentService.GetById(payload.Id); - if (content == null) - { - // gone fishing, remove entirely from all indexes (with descendants) - DeleteIndexForEntity(payload.Id, false); - continue; - } - - IContent published = null; - if (content.Published && contentService.IsPathPublished(content)) - { - published = content; - } - - if (published == null) - { - DeleteIndexForEntity(payload.Id, true); - } - - // just that content - ReIndexForContent(content, published != null); - - // branch - if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) - { - var masked = published == null ? null : new List(); - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - ordering: Ordering.By("Path", Direction.Ascending)); - - foreach (var descendant in descendants) - { - published = null; - if (masked != null) // else everything is masked - { - if (masked.Contains(descendant.ParentId) || !descendant.Published) - { - masked.Add(descendant.Id); - } - else - { - published = descendant; - } - } - - ReIndexForContent(descendant, published != null); - } - } - } - } - - // NOTE - // - // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes - // care of also deleting the descendants - // - // ReIndexForContent is NOT taking care of descendants so we have to reload everything - // again in order to process the branch - we COULD improve that by just reloading the - // XML from database instead of reloading content & re-serializing! - // - // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" - } - } - - public void Handle(MemberCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - switch (args.MessageType) - { - case MessageType.RefreshById: - var c1 = _services.MemberService.GetById((int)args.MessageObject); - if (c1 != null) - { - ReIndexForMember(c1); - } - break; - case MessageType.RemoveById: - - // This is triggered when the item is permanently deleted - - DeleteIndexForEntity((int)args.MessageObject, false); - break; - case MessageType.RefreshByInstance: - if (args.MessageObject is IMember c3) - { - ReIndexForMember(c3); - } - break; - case MessageType.RemoveByInstance: - - // This is triggered when the item is permanently deleted - - if (args.MessageObject is IMember c4) - { - DeleteIndexForEntity(c4.Id, false); - } - break; - case MessageType.RefreshByPayload: - var payload = (MemberCacheRefresher.JsonPayload[])args.MessageObject; - foreach (var p in payload) - { - if (p.Removed) - { - DeleteIndexForEntity(p.Id, false); - } - else - { - var m = _services.MemberService.GetById(p.Id); - if (m != null) - { - ReIndexForMember(m); - } - } - } - break; - case MessageType.RefreshAll: - case MessageType.RefreshByJson: - default: - //We don't support these, these message types will not fire for unpublished content - break; - } - } - - public void Handle(MediaCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - - var mediaService = _services.MediaService; - - foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject) - { - if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) - { - // remove from *all* indexes - DeleteIndexForEntity(payload.Id, false); - } - else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) - { - // ExamineEvents does not support RefreshAll - // just ignore that payload - // so what?! - } - else // RefreshNode or RefreshBranch (maybe trashed) - { - var media = mediaService.GetById(payload.Id); - if (media == null) - { - // gone fishing, remove entirely - DeleteIndexForEntity(payload.Id, false); - continue; - } - - if (media.Trashed) - { - DeleteIndexForEntity(payload.Id, true); - } - - // just that media - ReIndexForMedia(media, !media.Trashed); - - // branch - if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) - { - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - var descendants = mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); - foreach (var descendant in descendants) - { - ReIndexForMedia(descendant, !descendant.Trashed); - } - } - } - } - } - } - - public void Handle(LanguageCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) - { - return; - } - - if (payloads.Length == 0) - { - return; - } - - var removedOrCultureChanged = payloads.Any(x => - x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture - || x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove); - - if (removedOrCultureChanged) - { - //if a lang is removed or it's culture has changed, we need to rebuild the indexes since - //field names and values in the index have a string culture value. - _backgroundIndexRebuilder.RebuildIndexes(false); - } - } - - /// - /// Updates indexes based on content type changes - /// - /// - /// - public void Handle(ContentTypeCacheRefresherNotification args) - { - if (s_deactivate_handlers) - { - return; - } - - if (Suspendable.ExamineEvents.CanIndex == false) - { - return; - } - - if (args.MessageType != MessageType.RefreshByPayload) - { - throw new NotSupportedException(); - } - - var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); - - foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) - { - if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) - { - idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); - changedIds.Add(payload.ItemType, idLists); - } - - if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) - { - idLists.removedIds.Add(payload.Id); - } - else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) - { - idLists.refreshedIds.Add(payload.Id); - } - else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) - { - idLists.otherIds.Add(payload.Id); - } - } - - const int pageSize = 500; - - foreach (var ci in changedIds) - { - if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) - { - switch (ci.Key) - { - case var itemType when itemType == typeof(IContentType).Name: - RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); - break; - case var itemType when itemType == typeof(IMediaType).Name: - RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); - break; - case var itemType when itemType == typeof(IMemberType).Name: - RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); - break; - } - } - - //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs - foreach (var id in ci.Value.removedIds) - { - foreach (var index in _examineManager.Indexes.OfType()) - { - var searcher = index.GetSearcher(); - - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.CreateQuery().Field("nodeType", id.ToInvariantString()).Execute(maxResults: pageSize * (page + 1)); - total = results.TotalItemCount; - var paged = results.Skip(page * pageSize); - - foreach (ISearchResult item in paged) - { - if (int.TryParse(item.Id, out int contentId)) - { - DeleteIndexForEntity(contentId, false); - } - } - - page++; - } - } - } - } - } - - private void RefreshMemberOfMemberTypes(int[] memberTypeIds) - { - const int pageSize = 500; - - IEnumerable memberTypes = _services.MemberTypeService.GetAll(memberTypeIds); - foreach (IMemberType memberType in memberTypes) - { - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - IEnumerable memberToRefresh = _services.MemberService.GetAll( - page++, pageSize, out total, "LoginName", Direction.Ascending, - memberType.Alias); - - foreach (IMember c in memberToRefresh) - { - ReIndexForMember(c); - } - } - } - } - - private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) - { - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - IEnumerable mediaToRefresh = _services.MediaService.GetPagedOfTypes( - //Re-index all content of these types - mediaTypeIds, - page++, pageSize, out total, null, - Ordering.By("Path", Direction.Ascending)); - - foreach (IMedia c in mediaToRefresh) - { - ReIndexForMedia(c, c.Trashed == false); - } - } - } - - private void RefreshContentOfContentTypes(int[] contentTypeIds) - { - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - IEnumerable contentToRefresh = _services.ContentService.GetPagedOfTypes( - //Re-index all content of these types - contentTypeIds, - page++, pageSize, out total, null, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - Ordering.By("Path", Direction.Ascending)); - - //track which Ids have their paths are published - var publishChecked = new Dictionary(); - - foreach (IContent c in contentToRefresh) - { - var isPublished = false; - if (c.Published) - { - if (!publishChecked.TryGetValue(c.ParentId, out isPublished)) - { - //nothing by parent id, so query the service and cache the result for the next child to check against - isPublished = _services.ContentService.IsPathPublished(c); - publishChecked[c.Id] = isPublished; - } - } - - ReIndexForContent(c, isPublished); - } - } - } - - #endregion - - #region ReIndex/Delete for entity - private void ReIndexForContent(IContent sender, bool isPublished) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedReIndexForContent(_taskHelper, this, sender, isPublished)); - } - else - { - DeferedReIndexForContent.Execute(_taskHelper, this, sender, isPublished); - } - } - - private void ReIndexForMember(IMember member) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedReIndexForMember(_taskHelper, this, member)); - } - else - { - DeferedReIndexForMember.Execute(_taskHelper, this, member); - } - } - - private void ReIndexForMedia(IMedia sender, bool isPublished) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedReIndexForMedia(_taskHelper, this, sender, isPublished)); - } - else - { - DeferedReIndexForMedia.Execute(_taskHelper, this, sender, isPublished); - } - } - - /// - /// Remove items from an index - /// - /// - /// - /// If true, indicates that we will only delete this item from indexes that don't support unpublished content. - /// If false it will delete this from all indexes regardless. - /// - private void DeleteIndexForEntity(int entityId, bool keepIfUnpublished) - { - var actions = DeferedActions.Get(_scopeProvider); - if (actions != null) - { - actions.Add(new DeferedDeleteIndex(this, entityId, keepIfUnpublished)); - } - else - { - DeferedDeleteIndex.Execute(this, entityId, keepIfUnpublished); - } - } - #endregion - - #region Deferred Actions - private class DeferedActions - { - private readonly List _actions = new List(); - - public static DeferedActions Get(IScopeProvider scopeProvider) - { - IScopeContext scopeContext = scopeProvider.Context; - - return scopeContext?.Enlist("examineEvents", - () => new DeferedActions(), // creator - (completed, actions) => // action - { - if (completed) - { - actions.Execute(); - } - }, EnlistPriority); - } - - public void Add(DeferedAction action) => _actions.Add(action); - - private void Execute() - { - foreach (DeferedAction action in _actions) - { - action.Execute(); - } - } - } - - /// - /// An action that will execute at the end of the Scope being completed - /// - private abstract class DeferedAction - { - public virtual void Execute() - { } - } - - /// - /// Re-indexes an item on a background thread - /// - private class DeferedReIndexForContent : DeferedAction - { - private readonly TaskHelper _taskHelper; - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly IContent _content; - private readonly bool _isPublished; - - public DeferedReIndexForContent(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished) - { - _taskHelper = taskHelper; - _ExamineNotificationHandler = ExamineNotificationHandler; - _content = content; - _isPublished = isPublished; - } - - public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _content, _isPublished); - - public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished) - => taskHelper.RunBackgroundTask(() => - { - using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - - // for content we have a different builder for published vs unpublished - // we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published - var builders = new Dictionary>> - { - [true] = new Lazy>(() => ExamineNotificationHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()), - [false] = new Lazy>(() => ExamineNotificationHandler._contentValueSetBuilder.GetValueSets(content).ToList()) - }; - - foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => isPublished || !x.PublishedValuesOnly) - .Where(x => x.EnableDefaultEventHandler)) - { - List valueSet = builders[index.PublishedValuesOnly].Value; - index.IndexItems(valueSet); - } - - return Task.CompletedTask; - }); - } - - /// - /// Re-indexes an item on a background thread - /// - private class DeferedReIndexForMedia : DeferedAction - { - private readonly TaskHelper _taskHelper; - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly IMedia _media; - private readonly bool _isPublished; - - public DeferedReIndexForMedia(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) - { - _taskHelper = taskHelper; - _ExamineNotificationHandler = ExamineNotificationHandler; - _media = media; - _isPublished = isPublished; - } - - public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _media, _isPublished); - - public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) => - // perform the ValueSet lookup on a background thread - taskHelper.RunBackgroundTask(() => - { - using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - - var valueSet = ExamineNotificationHandler._mediaValueSetBuilder.GetValueSets(media).ToList(); - - foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => isPublished || !x.PublishedValuesOnly) - .Where(x => x.EnableDefaultEventHandler)) - { - index.IndexItems(valueSet); - } - - return Task.CompletedTask; - }); - } - - /// - /// Re-indexes an item on a background thread - /// - private class DeferedReIndexForMember : DeferedAction - { - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly IMember _member; - private readonly TaskHelper _taskHelper; - - public DeferedReIndexForMember(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) - { - _ExamineNotificationHandler = ExamineNotificationHandler; - _member = member; - _taskHelper = taskHelper; - } - - public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _member); - - public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) => - // perform the ValueSet lookup on a background thread - taskHelper.RunBackgroundTask(() => - { - using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true); - - var valueSet = ExamineNotificationHandler._memberValueSetBuilder.GetValueSets(member).ToList(); - foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType() - //filter the indexers - .Where(x => x.EnableDefaultEventHandler)) - { - index.IndexItems(valueSet); - } - - return Task.CompletedTask; - }); - } - - private class DeferedDeleteIndex : DeferedAction - { - private readonly ExamineNotificationHandler _ExamineNotificationHandler; - private readonly int _id; - private readonly bool _keepIfUnpublished; - - public DeferedDeleteIndex(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished) - { - _ExamineNotificationHandler = ExamineNotificationHandler; - _id = id; - _keepIfUnpublished = keepIfUnpublished; - } - - public override void Execute() => Execute(_ExamineNotificationHandler, _id, _keepIfUnpublished); - - public static void Execute(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished) - { - var strId = id.ToString(CultureInfo.InvariantCulture); - foreach (var index in ExamineNotificationHandler._examineManager.Indexes.OfType() - .Where(x => x.PublishedValuesOnly || !keepIfUnpublished) - .Where(x => x.EnableDefaultEventHandler)) - { - index.DeleteFromIndex(strId); - } - } - } - #endregion - } -} diff --git a/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs b/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs deleted file mode 100644 index 6c39da44c7..0000000000 --- a/src/Umbraco.Infrastructure/Search/ExamineUserComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Runtime; - -namespace Umbraco.Cms.Infrastructure.Search -{ - /// - /// An abstract class for custom index authors to inherit from - /// - public abstract class ExamineUserComponent : IComponent - { - private readonly IMainDom _mainDom; - - public ExamineUserComponent(IMainDom mainDom) - { - _mainDom = mainDom; - } - - /// - /// Initialize the component, eagerly exits if ExamineComponent.ExamineEnabled == false - /// - public void Initialize() - { - if (!_mainDom.IsMainDom) return; - - InitializeComponent(); - } - - /// - /// Abstract method which executes to initialize this component if ExamineComponent.ExamineEnabled == true - /// - protected abstract void InitializeComponent(); - - public virtual void Terminate() - { - } - } -} diff --git a/src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs b/src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs new file mode 100644 index 0000000000..24c82c055d --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IUmbracoIndexingHandler.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public interface IUmbracoIndexingHandler + { + /// + /// Returns true if the indexing handler is enabled + /// + /// + /// If this is false then there will be no data lookups executed to populate indexes + /// when service changes are made. + /// + bool Enabled { get; } + + void ReIndexForContent(IContent sender, bool isPublished); + void ReIndexForMember(IMember member); + void ReIndexForMedia(IMedia sender, bool isPublished); + + /// + /// Deletes all documents for the content type Ids + /// + /// + void DeleteDocumentsForContentTypes(IReadOnlyCollection removedContentTypes); + + /// + /// Remove items from an index + /// + /// + /// + /// If true, indicates that we will only delete this item from indexes that don't support unpublished content. + /// If false it will delete this from all indexes regardless. + /// + void DeleteIndexForEntity(int entityId, bool keepIfUnpublished); + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs new file mode 100644 index 0000000000..ebebdb7f34 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Content.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class ContentIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IContentService _contentService; + + public ContentIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IContentService contentService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + } + + /// + /// Updates indexes based on content changes + /// + /// + /// + public void Handle(ContentCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) + { + // delete content entirely (with descendants) + // false: remove entirely from all indexes + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + } + else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) + { + // ExamineEvents does not support RefreshAll + // just ignore that payload + // so what?! + + // TODO: Rebuild the index at this point? + } + else // RefreshNode or RefreshBranch (maybe trashed) + { + // don't try to be too clever - refresh entirely + // there has to be race conditions in there ;-( + + var content = _contentService.GetById(payload.Id); + if (content == null) + { + // gone fishing, remove entirely from all indexes (with descendants) + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + continue; + } + + IContent published = null; + if (content.Published && _contentService.IsPathPublished(content)) + { + published = content; + } + + if (published == null) + { + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, true); + } + + // just that content + _umbracoIndexingHandler.ReIndexForContent(content, published != null); + + // branch + if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) + { + var masked = published == null ? null : new List(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var descendants = _contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + ordering: Ordering.By("Path", Direction.Ascending)); + + foreach (var descendant in descendants) + { + published = null; + if (masked != null) // else everything is masked + { + if (masked.Contains(descendant.ParentId) || !descendant.Published) + { + masked.Add(descendant.Id); + } + else + { + published = descendant; + } + } + + _umbracoIndexingHandler.ReIndexForContent(descendant, published != null); + } + } + } + } + + // NOTE + // + // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes + // care of also deleting the descendants + // + // ReIndexForContent is NOT taking care of descendants so we have to reload everything + // again in order to process the branch - we COULD improve that by just reloading the + // XML from database instead of reloading content & re-serializing! + // + // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs new file mode 100644 index 0000000000..9bdc9fa3c4 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.ContentType.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class ContentTypeIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IContentService _contentService; + private readonly IMemberService _memberService; + private readonly IMediaService _mediaService; + private readonly IMemberTypeService _memberTypeService; + + public ContentTypeIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IContentService contentService, IMemberService memberService, IMediaService mediaService, IMemberTypeService memberTypeService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + _memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService)); + } + + /// + /// Updates indexes based on content type changes + /// + /// + /// + public void Handle(ContentTypeCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); + + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) + { + if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) + { + idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); + changedIds.Add(payload.ItemType, idLists); + } + + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) + { + idLists.removedIds.Add(payload.Id); + } + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) + { + idLists.refreshedIds.Add(payload.Id); + } + else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) + { + idLists.otherIds.Add(payload.Id); + } + } + + foreach (var ci in changedIds) + { + if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) + { + switch (ci.Key) + { + case var itemType when itemType == typeof(IContentType).Name: + RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMediaType).Name: + RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMemberType).Name: + RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + } + } + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + _umbracoIndexingHandler.DeleteDocumentsForContentTypes(ci.Value.removedIds); + } + } + + private void RefreshMemberOfMemberTypes(int[] memberTypeIds) + { + const int pageSize = 500; + + IEnumerable memberTypes = _memberTypeService.GetAll(memberTypeIds); + foreach (IMemberType memberType in memberTypes) + { + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + IEnumerable memberToRefresh = _memberService.GetAll( + page++, pageSize, out total, "LoginName", Direction.Ascending, + memberType.Alias); + + foreach (IMember c in memberToRefresh) + { + _umbracoIndexingHandler.ReIndexForMember(c); + } + } + } + } + + private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + IEnumerable mediaToRefresh = _mediaService.GetPagedOfTypes( + //Re-index all content of these types + mediaTypeIds, + page++, pageSize, out total, null, + Ordering.By("Path", Direction.Ascending)); + + foreach (IMedia c in mediaToRefresh) + { + _umbracoIndexingHandler.ReIndexForMedia(c, c.Trashed == false); + } + } + } + + private void RefreshContentOfContentTypes(int[] contentTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + IEnumerable contentToRefresh = _contentService.GetPagedOfTypes( + //Re-index all content of these types + contentTypeIds, + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (IContent c in contentToRefresh) + { + var isPublished = false; + if (c.Published) + { + if (!publishChecked.TryGetValue(c.ParentId, out isPublished)) + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = _contentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + } + } + + _umbracoIndexingHandler.ReIndexForContent(c, isPublished); + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs new file mode 100644 index 0000000000..2f7d5f66ca --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Language.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Infrastructure.Examine; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class LanguageIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IIndexRebuilder _indexRebuilder; + + public LanguageIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IIndexRebuilder indexRebuilder) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _indexRebuilder = indexRebuilder ?? throw new ArgumentNullException(nameof(indexRebuilder)); + } + + public void Handle(LanguageCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads)) + { + return; + } + + if (payloads.Length == 0) + { + return; + } + + var removedOrCultureChanged = payloads.Any(x => + x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture + || x.ChangeType == LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove); + + if (removedOrCultureChanged) + { + //if a lang is removed or it's culture has changed, we need to rebuild the indexes since + //field names and values in the index have a string culture value. + _indexRebuilder.RebuildIndexes(false); + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs new file mode 100644 index 0000000000..8b37d047de --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Media.cs @@ -0,0 +1,90 @@ +using System; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class MediaIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IMediaService _mediaService; + + public MediaIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IMediaService mediaService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); + } + + public void Handle(MediaCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + if (args.MessageType != MessageType.RefreshByPayload) + { + throw new NotSupportedException(); + } + + foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) + { + // remove from *all* indexes + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + } + else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) + { + // ExamineEvents does not support RefreshAll + // just ignore that payload + // so what?! + } + else // RefreshNode or RefreshBranch (maybe trashed) + { + var media = _mediaService.GetById(payload.Id); + if (media == null) + { + // gone fishing, remove entirely + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false); + continue; + } + + if (media.Trashed) + { + _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, true); + } + + // just that media + _umbracoIndexingHandler.ReIndexForMedia(media, !media.Trashed); + + // branch + if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var descendants = _mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); + foreach (var descendant in descendants) + { + _umbracoIndexingHandler.ReIndexForMedia(descendant, !descendant.Trashed); + } + } + } + } + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs new file mode 100644 index 0000000000..389b839c67 --- /dev/null +++ b/src/Umbraco.Infrastructure/Search/IndexingNotificationHandler.Member.cs @@ -0,0 +1,90 @@ +using System; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Infrastructure.Search +{ + public sealed class MemberIndexingNotificationHandler : INotificationHandler + { + private readonly IUmbracoIndexingHandler _umbracoIndexingHandler; + private readonly IMemberService _memberService; + + public MemberIndexingNotificationHandler(IUmbracoIndexingHandler umbracoIndexingHandler, IMemberService memberService) + { + _umbracoIndexingHandler = umbracoIndexingHandler ?? throw new ArgumentNullException(nameof(umbracoIndexingHandler)); + _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); + } + + public void Handle(MemberCacheRefresherNotification args) + { + if (!_umbracoIndexingHandler.Enabled) + { + return; + } + + if (Suspendable.ExamineEvents.CanIndex == false) + { + return; + } + + switch (args.MessageType) + { + case MessageType.RefreshById: + var c1 = _memberService.GetById((int)args.MessageObject); + if (c1 != null) + { + _umbracoIndexingHandler.ReIndexForMember(c1); + } + break; + case MessageType.RemoveById: + + // This is triggered when the item is permanently deleted + + _umbracoIndexingHandler.DeleteIndexForEntity((int)args.MessageObject, false); + break; + case MessageType.RefreshByInstance: + if (args.MessageObject is IMember c3) + { + _umbracoIndexingHandler.ReIndexForMember(c3); + } + break; + case MessageType.RemoveByInstance: + + // This is triggered when the item is permanently deleted + + if (args.MessageObject is IMember c4) + { + _umbracoIndexingHandler.DeleteIndexForEntity(c4.Id, false); + } + break; + case MessageType.RefreshByPayload: + var payload = (MemberCacheRefresher.JsonPayload[])args.MessageObject; + foreach (var p in payload) + { + if (p.Removed) + { + _umbracoIndexingHandler.DeleteIndexForEntity(p.Id, false); + } + else + { + var m = _memberService.GetById(p.Id); + if (m != null) + { + _umbracoIndexingHandler.ReIndexForMember(m); + } + } + } + break; + case MessageType.RefreshAll: + case MessageType.RefreshByJson: + default: + //We don't support these, these message types will not fire for unpublished content + break; + } + } + } +} diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs b/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs index 60d913f7c5..c0021bc967 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs @@ -1,17 +1,109 @@ using Microsoft.AspNetCore.Identity; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; namespace Umbraco.Cms.Core.Security { /// /// Umbraco back office specific /// - public class BackOfficeErrorDescriber : IdentityErrorDescriber + public class BackOfficeErrorDescriber : UmbracoErrorDescriberBase { - // TODO: Override all the methods in order to provide our own translated error messages + private readonly ILocalizedTextService _textService; + + public BackOfficeErrorDescriber(ILocalizedTextService textService) + : base(textService) => _textService = textService; + + public override IdentityError DuplicateRoleName(string role) => new IdentityError + { + Code = nameof(DuplicateRoleName), + Description = _textService.Localize("validation/duplicateUserGroupName", new[] { role }) + }; + + public override IdentityError InvalidRoleName(string role) => new IdentityError + { + Code = nameof(InvalidRoleName), + Description = _textService.Localize("validation/invalidUserGroupName") + }; + + public override IdentityError LoginAlreadyAssociated() => new IdentityError + { + Code = nameof(LoginAlreadyAssociated), + Description = _textService.Localize("user/duplicateLogin") + }; + + public override IdentityError UserAlreadyHasPassword() => new IdentityError + { + Code = nameof(UserAlreadyHasPassword), + Description = _textService.Localize("user/userHasPassword") + }; + + public override IdentityError UserAlreadyInRole(string role) => new IdentityError + { + Code = nameof(UserAlreadyInRole), + Description = _textService.Localize("user/userHasGroup", new[] { role }) + }; + + public override IdentityError UserLockoutNotEnabled() => new IdentityError + { + Code = nameof(UserLockoutNotEnabled), + Description = _textService.Localize("user/userLockoutNotEnabled") + }; + + public override IdentityError UserNotInRole(string role) => new IdentityError + { + Code = nameof(UserNotInRole), + Description = _textService.Localize("user/userNotInGroup", new[] { role }) + }; } - public class MembersErrorDescriber : IdentityErrorDescriber + public class MembersErrorDescriber : UmbracoErrorDescriberBase { - // TODO: Override all the methods in order to provide our own translated error messages + private readonly ILocalizedTextService _textService; + + public MembersErrorDescriber(ILocalizedTextService textService) + : base(textService) => _textService = textService; + + public override IdentityError DuplicateRoleName(string role) => new IdentityError + { + Code = nameof(DuplicateRoleName), + Description = _textService.Localize("validation/duplicateMemberGroupName", new[] { role }) + }; + + public override IdentityError InvalidRoleName(string role) => new IdentityError + { + Code = nameof(InvalidRoleName), + Description = _textService.Localize("validation/invalidMemberGroupName") + }; + + public override IdentityError LoginAlreadyAssociated() => new IdentityError + { + Code = nameof(LoginAlreadyAssociated), + Description = _textService.Localize("member/duplicateMemberLogin") + }; + + public override IdentityError UserAlreadyHasPassword() => new IdentityError + { + Code = nameof(UserAlreadyHasPassword), + Description = _textService.Localize("member/memberHasPassword") + }; + + public override IdentityError UserAlreadyInRole(string role) => new IdentityError + { + Code = nameof(UserAlreadyInRole), + Description = _textService.Localize("member/memberHasGroup", new[] { role }) + }; + + public override IdentityError UserLockoutNotEnabled() => new IdentityError + { + Code = nameof(UserLockoutNotEnabled), + Description = _textService.Localize("member/memberLockoutNotEnabled") + }; + + public override IdentityError UserNotInRole(string role) => new IdentityError + { + Code = nameof(UserNotInRole), + Description = _textService.Localize("member/memberNotInGroup", new[] { role }) + }; } } diff --git a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs index 6acf52c3cb..ec364eb850 100644 --- a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs @@ -375,5 +375,13 @@ namespace Umbraco.Cms.Core.Security /// A user can only support a phone number if the BackOfficeUserStore is replaced with another that implements IUserPhoneNumberStore /// Task GetPhoneNumberAsync(TUser user); + + /// + /// Validates that a user's credentials are correct without actually logging them in. + /// + /// The user name. + /// The password. + /// True if the credentials are valid. + Task ValidateCredentialsAsync(string username, string password); } } diff --git a/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs b/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs index 459417b289..724eb77030 100644 --- a/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs +++ b/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Core.Security /// /// Used to construct a new instance without an identity /// - public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, string name = null) + public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, bool isApproved, string name = null) { if (string.IsNullOrWhiteSpace(username)) { @@ -46,6 +46,7 @@ namespace Umbraco.Cms.Core.Security user.UserName = username; user.Email = email; user.MemberTypeAlias = memberTypeAlias; + user.IsApproved = isApproved; user.Id = null; user.HasIdentity = false; user.Name = name; diff --git a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs index 59f6686f68..fe12a349f8 100644 --- a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Data; using System.Linq; -using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; @@ -17,13 +15,12 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Security { - /// /// A custom user store that uses Umbraco member data /// public class MemberUserStore : UmbracoUserStore, IMemberUserStore { - private const string genericIdentityErrorCode = "IdentityErrorUserStore"; + private const string GenericIdentityErrorCode = "IdentityErrorUserStore"; private readonly IMemberService _memberService; private readonly IUmbracoMapper _mapper; private readonly IScopeProvider _scopeProvider; @@ -103,7 +100,7 @@ namespace Umbraco.Cms.Core.Security } catch (Exception ex) { - return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message })); + return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message })); } } @@ -134,7 +131,7 @@ namespace Umbraco.Cms.Core.Security // we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it. var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.Logins)); - var memberChangeType = UpdateMemberProperties(found, user); + MemberDataChangeType memberChangeType = UpdateMemberProperties(found, user); if (memberChangeType == MemberDataChangeType.FullSave) { _memberService.Save(found); @@ -163,7 +160,7 @@ namespace Umbraco.Cms.Core.Security } catch (Exception ex) { - return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message })); + return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message })); } } @@ -192,7 +189,7 @@ namespace Umbraco.Cms.Core.Security } catch (Exception ex) { - return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message })); + return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message })); } } @@ -505,7 +502,7 @@ namespace Umbraco.Cms.Core.Security private MemberDataChangeType UpdateMemberProperties(IMember member, MemberIdentityUser identityUser) { - var changeType = MemberDataChangeType.None; + MemberDataChangeType changeType = MemberDataChangeType.None; // don't assign anything if nothing has changed as this will trigger the track changes of the model if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.LastLoginDateUtc)) diff --git a/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs b/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs new file mode 100644 index 0000000000..0214811274 --- /dev/null +++ b/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs @@ -0,0 +1,103 @@ +using Microsoft.AspNetCore.Identity; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Security +{ + public abstract class UmbracoErrorDescriberBase : IdentityErrorDescriber + { + private readonly ILocalizedTextService _textService; + + protected UmbracoErrorDescriberBase(ILocalizedTextService textService) => _textService = textService; + + public override IdentityError ConcurrencyFailure() => new IdentityError + { + Code = nameof(ConcurrencyFailure), + Description = _textService.Localize("errors/concurrencyError") + }; + + public override IdentityError DefaultError() => new IdentityError + { + Code = nameof(DefaultError), + Description = _textService.Localize("errors/defaultError") + }; + + public override IdentityError DuplicateEmail(string email) => new IdentityError + { + Code = nameof(DuplicateEmail), + Description = _textService.Localize("validation/duplicateEmail", new[] { email }) + }; + + public override IdentityError DuplicateUserName(string userName) => new IdentityError + { + Code = nameof(DuplicateUserName), + Description = _textService.Localize("validation/duplicateUsername", new[] { userName }) + }; + + public override IdentityError InvalidEmail(string email) => new IdentityError + { + Code = nameof(InvalidEmail), + Description = _textService.Localize("validation/invalidEmail") + }; + + public override IdentityError InvalidToken() => new IdentityError + { + Code = nameof(InvalidToken), + Description = _textService.Localize("validation/invalidToken") + }; + + public override IdentityError InvalidUserName(string userName) => new IdentityError + { + Code = nameof(InvalidUserName), + Description = _textService.Localize("validation/invalidUsername") + }; + + public override IdentityError PasswordMismatch() => new IdentityError + { + Code = nameof(PasswordMismatch), + Description = _textService.Localize("user/passwordMismatch") + }; + + public override IdentityError PasswordRequiresDigit() => new IdentityError + { + Code = nameof(PasswordRequiresDigit), + Description = _textService.Localize("user/passwordRequiresDigit") + }; + + public override IdentityError PasswordRequiresLower() => new IdentityError + { + Code = nameof(PasswordRequiresLower), + Description = _textService.Localize("user/passwordRequiresLower") + }; + + public override IdentityError PasswordRequiresNonAlphanumeric() => new IdentityError + { + Code = nameof(PasswordRequiresNonAlphanumeric), + Description = _textService.Localize("user/passwordRequiresNonAlphanumeric") + }; + + public override IdentityError PasswordRequiresUniqueChars(int uniqueChars) => new IdentityError + { + Code = nameof(PasswordRequiresUniqueChars), + Description = _textService.Localize("user/passwordRequiresUniqueChars", new[] { uniqueChars.ToString() }) + }; + + public override IdentityError PasswordRequiresUpper() => new IdentityError + { + Code = nameof(PasswordRequiresUpper), + Description = _textService.Localize("user/passwordRequiresUpper") + }; + + public override IdentityError PasswordTooShort(int length) => new IdentityError + { + Code = nameof(PasswordTooShort), + Description = _textService.Localize("user/passwordTooShort", new[] { length.ToString() }) + }; + + public override IdentityError RecoveryCodeRedemptionFailed() => new IdentityError + { + Code = nameof(RecoveryCodeRedemptionFailed), + Description = _textService.Localize("login/resetCodeExpired") + }; + } +} diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs index 7f77b9d8c6..bf198af1c3 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs @@ -75,11 +75,9 @@ namespace Umbraco.Cms.Core.Security /// True if the session is valid, else false public virtual async Task ValidateSessionIdAsync(string userId, string sessionId) { - var userSessionStore = Store as IUserSessionStore; - // if this is not set, for backwards compat (which would be super rare), we'll just approve it // TODO: This should be removed after members supports this - if (userSessionStore == null) + if (Store is not IUserSessionStore userSessionStore) { return true; } @@ -221,8 +219,7 @@ namespace Umbraco.Cms.Core.Security throw new ArgumentNullException(nameof(user)); } - var lockoutStore = Store as IUserLockoutStore; - if (lockoutStore == null) + if (Store is not IUserLockoutStore lockoutStore) { throw new NotSupportedException("The current user store does not implement " + typeof(IUserLockoutStore<>)); } @@ -241,5 +238,23 @@ namespace Umbraco.Cms.Core.Security return result; } + /// + public async Task ValidateCredentialsAsync(string username, string password) + { + TUser user = await FindByNameAsync(username); + if (user == null) + { + return false; + } + + if (Store is not IUserPasswordStore userPasswordStore) + { + throw new NotSupportedException("The current user store does not implement " + typeof(IUserPasswordStore<>)); + } + + var hash = await userPasswordStore.GetPasswordHashAsync(user, new CancellationToken()); + + return await VerifyPasswordAsync(userPasswordStore, user, password) == PasswordVerificationResult.Success; + } } } diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs index 1db823cc78..3884ee31a1 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserStore.cs @@ -10,7 +10,8 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Security { - public abstract class UmbracoUserStore : UserStoreBase, IdentityUserRole, IdentityUserLogin, IdentityUserToken, IdentityRoleClaim> + public abstract class UmbracoUserStore + : UserStoreBase, IdentityUserRole, IdentityUserLogin, IdentityUserToken, IdentityRoleClaim> where TUser : UmbracoIdentityUser where TRole : IdentityRole { diff --git a/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs b/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs index 0a6e945a23..e0c0f56244 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/CacheInstructionService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -22,8 +23,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// public class CacheInstructionService : RepositoryService, ICacheInstructionService { - private readonly IServerRoleAccessor _serverRoleAccessor; - private readonly CacheRefresherCollection _cacheRefreshers; private readonly ICacheInstructionRepository _cacheInstructionRepository; private readonly IProfilingLogger _profilingLogger; private readonly ILogger _logger; @@ -36,16 +35,12 @@ namespace Umbraco.Cms.Core.Services.Implement IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, - IServerRoleAccessor serverRoleAccessor, - CacheRefresherCollection cacheRefreshers, ICacheInstructionRepository cacheInstructionRepository, IProfilingLogger profilingLogger, ILogger logger, IOptions globalSettings) : base(provider, loggerFactory, eventMessagesFactory) { - _serverRoleAccessor = serverRoleAccessor; - _cacheRefreshers = cacheRefreshers; _cacheInstructionRepository = cacheInstructionRepository; _profilingLogger = profilingLogger; _logger = logger; @@ -57,7 +52,7 @@ namespace Umbraco.Cms.Core.Services.Implement { using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - if (lastId == 0) + if (lastId <= 0) { var count = _cacheInstructionRepository.CountAll(); @@ -79,7 +74,6 @@ namespace Umbraco.Cms.Core.Services.Implement return false; } } - /// public bool IsInstructionCountOverLimit(int lastId, int limit, out int count) { @@ -133,22 +127,28 @@ namespace Umbraco.Cms.Core.Services.Implement new CacheInstruction(0, DateTime.UtcNow, JsonConvert.SerializeObject(instructions, Formatting.None), localIdentity, instructions.Sum(x => x.JsonIdCount)); /// - public CacheInstructionServiceProcessInstructionsResult ProcessInstructions(bool released, string localIdentity, DateTime lastPruned, int lastId) + public ProcessInstructionsResult ProcessInstructions( + CacheRefresherCollection cacheRefreshers, + ServerRole serverRole, + CancellationToken cancellationToken, + string localIdentity, + DateTime lastPruned, + int lastId) { using (_profilingLogger.DebugDuration("Syncing from database...")) using (IScope scope = ScopeProvider.CreateScope()) { - var numberOfInstructionsProcessed = ProcessDatabaseInstructions(released, localIdentity, ref lastId); + var numberOfInstructionsProcessed = ProcessDatabaseInstructions(cacheRefreshers, cancellationToken, localIdentity, ref lastId); // Check for pruning throttling. - if (released || (DateTime.UtcNow - lastPruned) <= _globalSettings.DatabaseServerMessenger.TimeBetweenPruneOperations) + if (cancellationToken.IsCancellationRequested || (DateTime.UtcNow - lastPruned) <= _globalSettings.DatabaseServerMessenger.TimeBetweenPruneOperations) { scope.Complete(); - return CacheInstructionServiceProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); + return ProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); } var instructionsWerePruned = false; - switch (_serverRoleAccessor.CurrentServerRole) + switch (serverRole) { case ServerRole.Single: case ServerRole.Master: @@ -160,8 +160,8 @@ namespace Umbraco.Cms.Core.Services.Implement scope.Complete(); return instructionsWerePruned - ? CacheInstructionServiceProcessInstructionsResult.AsCompletedAndPruned(numberOfInstructionsProcessed, lastId) - : CacheInstructionServiceProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); + ? ProcessInstructionsResult.AsCompletedAndPruned(numberOfInstructionsProcessed, lastId) + : ProcessInstructionsResult.AsCompleted(numberOfInstructionsProcessed, lastId); } } @@ -172,7 +172,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. /// /// Number of instructions processed. - private int ProcessDatabaseInstructions(bool released, string localIdentity, ref int lastId) + private int ProcessDatabaseInstructions(CacheRefresherCollection cacheRefreshers, CancellationToken cancellationToken, string localIdentity, ref int lastId) { // NOTE: // We 'could' recurse to ensure that no remaining instructions are pending in the table before proceeding but I don't think that @@ -205,7 +205,7 @@ namespace Umbraco.Cms.Core.Services.Implement { // If this flag gets set it means we're shutting down! In this case, we need to exit asap and cannot // continue processing anything otherwise we'll hold up the app domain shutdown. - if (released) + if (cancellationToken.IsCancellationRequested) { break; } @@ -227,7 +227,7 @@ namespace Umbraco.Cms.Core.Services.Implement List instructionBatch = GetAllInstructions(jsonInstructions); // Process as per-normal. - var success = ProcessDatabaseInstructions(instructionBatch, instruction, processed, released, ref lastId); + var success = ProcessDatabaseInstructions(cacheRefreshers, instructionBatch, instruction, processed, cancellationToken, ref lastId); // If they couldn't be all processed (i.e. we're shutting down) then exit. if (success == false) @@ -295,12 +295,18 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Returns true if all instructions in the batch were processed, otherwise false if they could not be due to the app being shut down /// - private bool ProcessDatabaseInstructions(IReadOnlyCollection instructionBatch, CacheInstruction instruction, HashSet processed, bool released, ref int lastId) + private bool ProcessDatabaseInstructions( + CacheRefresherCollection cacheRefreshers, + IReadOnlyCollection instructionBatch, + CacheInstruction instruction, + HashSet processed, + CancellationToken cancellationToken, + ref int lastId) { // Execute remote instructions & update lastId. try { - var result = NotifyRefreshers(instructionBatch, processed, released); + var result = NotifyRefreshers(cacheRefreshers, instructionBatch, processed, cancellationToken); if (result) { // If all instructions were processed, set the last id. @@ -330,12 +336,16 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Returns true if all instructions were processed, otherwise false if the processing was interrupted (i.e. by app shutdown). /// - private bool NotifyRefreshers(IEnumerable instructions, HashSet processed, bool released) + private bool NotifyRefreshers( + CacheRefresherCollection cacheRefreshers, + IEnumerable instructions, + HashSet processed, + CancellationToken cancellationToken) { foreach (RefreshInstruction instruction in instructions) { // Check if the app is shutting down, we need to exit if this happens. - if (released) + if (cancellationToken.IsCancellationRequested) { return false; } @@ -349,22 +359,22 @@ namespace Umbraco.Cms.Core.Services.Implement switch (instruction.RefreshType) { case RefreshMethodType.RefreshAll: - RefreshAll(instruction.RefresherId); + RefreshAll(cacheRefreshers, instruction.RefresherId); break; case RefreshMethodType.RefreshByGuid: - RefreshByGuid(instruction.RefresherId, instruction.GuidId); + RefreshByGuid(cacheRefreshers, instruction.RefresherId, instruction.GuidId); break; case RefreshMethodType.RefreshById: - RefreshById(instruction.RefresherId, instruction.IntId); + RefreshById(cacheRefreshers, instruction.RefresherId, instruction.IntId); break; case RefreshMethodType.RefreshByIds: - RefreshByIds(instruction.RefresherId, instruction.JsonIds); + RefreshByIds(cacheRefreshers, instruction.RefresherId, instruction.JsonIds); break; case RefreshMethodType.RefreshByJson: - RefreshByJson(instruction.RefresherId, instruction.JsonPayload); + RefreshByJson(cacheRefreshers, instruction.RefresherId, instruction.JsonPayload); break; case RefreshMethodType.RemoveById: - RemoveById(instruction.RefresherId, instruction.IntId); + RemoveById(cacheRefreshers, instruction.RefresherId, instruction.IntId); break; } @@ -374,48 +384,48 @@ namespace Umbraco.Cms.Core.Services.Implement return true; } - private void RefreshAll(Guid uniqueIdentifier) + private void RefreshAll(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.RefreshAll(); } - private void RefreshByGuid(Guid uniqueIdentifier, Guid id) + private void RefreshByGuid(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, Guid id) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.Refresh(id); } - private void RefreshById(Guid uniqueIdentifier, int id) + private void RefreshById(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, int id) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.Refresh(id); } - private void RefreshByIds(Guid uniqueIdentifier, string jsonIds) + private void RefreshByIds(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, string jsonIds) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); foreach (var id in JsonConvert.DeserializeObject(jsonIds)) { refresher.Refresh(id); } } - private void RefreshByJson(Guid uniqueIdentifier, string jsonPayload) + private void RefreshByJson(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, string jsonPayload) { - IJsonCacheRefresher refresher = GetJsonRefresher(uniqueIdentifier); + IJsonCacheRefresher refresher = GetJsonRefresher(cacheRefreshers, uniqueIdentifier); refresher.Refresh(jsonPayload); } - private void RemoveById(Guid uniqueIdentifier, int id) + private void RemoveById(CacheRefresherCollection cacheRefreshers, Guid uniqueIdentifier, int id) { - ICacheRefresher refresher = GetRefresher(uniqueIdentifier); + ICacheRefresher refresher = GetRefresher(cacheRefreshers, uniqueIdentifier); refresher.Remove(id); } - private ICacheRefresher GetRefresher(Guid id) + private ICacheRefresher GetRefresher(CacheRefresherCollection cacheRefreshers, Guid id) { - ICacheRefresher refresher = _cacheRefreshers[id]; + ICacheRefresher refresher = cacheRefreshers[id]; if (refresher == null) { throw new InvalidOperationException("Cache refresher with ID \"" + id + "\" does not exist."); @@ -424,7 +434,7 @@ namespace Umbraco.Cms.Core.Services.Implement return refresher; } - private IJsonCacheRefresher GetJsonRefresher(Guid id) => GetJsonRefresher(GetRefresher(id)); + private IJsonCacheRefresher GetJsonRefresher(CacheRefresherCollection cacheRefreshers, Guid id) => GetJsonRefresher(GetRefresher(cacheRefreshers, id)); private static IJsonCacheRefresher GetJsonRefresher(ICacheRefresher refresher) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 63edbb6ed8..85bc2c7f4d 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -7,11 +7,11 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs index 7a947a420c..3d8dcf33bf 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeService.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index f04793fe8e..942c9c1e5e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs index 4bb1e46c29..b20be692df 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; @@ -21,6 +22,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// public class DataTypeService : RepositoryService, IDataTypeService { + private readonly IDataValueEditorFactory _dataValueEditorFactory; private readonly IDataTypeRepository _dataTypeRepository; private readonly IDataTypeContainerRepository _dataTypeContainerRepository; private readonly IContentTypeRepository _contentTypeRepository; @@ -32,7 +34,9 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IShortStringHelper _shortStringHelper; private readonly IJsonSerializer _jsonSerializer; - public DataTypeService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, + public DataTypeService( + IDataValueEditorFactory dataValueEditorFactory, + IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IDataTypeRepository dataTypeRepository, IDataTypeContainerRepository dataTypeContainerRepository, IAuditRepository auditRepository, IEntityRepository entityRepository, IContentTypeRepository contentTypeRepository, IIOHelper ioHelper, ILocalizedTextService localizedTextService, ILocalizationService localizationService, @@ -40,6 +44,7 @@ namespace Umbraco.Cms.Core.Services.Implement IJsonSerializer jsonSerializer) : base(provider, loggerFactory, eventMessagesFactory) { + _dataValueEditorFactory = dataValueEditorFactory; _dataTypeRepository = dataTypeRepository; _dataTypeContainerRepository = dataTypeContainerRepository; _auditRepository = auditRepository; @@ -339,7 +344,7 @@ namespace Umbraco.Cms.Core.Services.Implement .Where(x => x.Editor is MissingPropertyEditor); foreach (var dataType in dataTypesWithMissingEditors) { - dataType.Editor = new LabelPropertyEditor(LoggerFactory, _ioHelper, this, _localizedTextService, _localizationService, _shortStringHelper, _jsonSerializer); + dataType.Editor = new LabelPropertyEditor(_dataValueEditorFactory, _ioHelper); } } @@ -371,7 +376,7 @@ namespace Umbraco.Cms.Core.Services.Implement moveInfo.AddRange(_dataTypeRepository.Move(toMove, container)); scope.Notifications.Publish(new DataTypeMovedNotification(moveEventInfo, evtMsgs).WithStateFrom(movingDataTypeNotification)); - + scope.Complete(); } catch (DataOperationException ex) diff --git a/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs b/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs index d3937b5bb0..fc5d26e4df 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DomainService.cs @@ -2,9 +2,9 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Services.Implement { diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index b63040a4a0..859a2b6327 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -586,7 +586,7 @@ namespace Umbraco.Cms.Core.Services.Implement var propertyEditor = _propertyEditors[propertyType.PropertyEditorAlias]; return propertyEditor == null ? Array.Empty() - : propertyEditor.GetValueEditor().ConvertDbToXml(property, _dataTypeService, _localizationService, published); + : propertyEditor.GetValueEditor().ConvertDbToXml(property, published); } // exports an IContent item descendants. diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 68a02fa478..6d7d5b4490 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -9,9 +9,10 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -57,7 +58,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IEnumerable GetStylesheets(params string[] names) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _stylesheetRepository.GetMany(names); } @@ -66,14 +67,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IStylesheet GetStylesheetByName(string name) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _stylesheetRepository.Get(name); } } /// - public void SaveStylesheet(IStylesheet stylesheet, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveStylesheet(IStylesheet stylesheet, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -95,7 +96,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public void DeleteStylesheet(string path, int userId = Cms.Core.Constants.Security.SuperUserId) + public void DeleteStylesheet(string path, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -124,52 +125,48 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public bool ValidateStylesheet(IStylesheet stylesheet) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _stylesheetRepository.ValidateStylesheet(stylesheet); - } - } - public void CreateStyleSheetFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _stylesheetRepository.AddFolder(folderPath); scope.Complete(); } } + /// public void DeleteStyleSheetFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _stylesheetRepository.DeleteFolder(folderPath); scope.Complete(); } } + /// public Stream GetStylesheetFileContentStream(string filepath) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _stylesheetRepository.GetFileContentStream(filepath); } } + /// public void SetStylesheetFileContent(string filepath, Stream content) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _stylesheetRepository.SetFileContent(filepath, content); scope.Complete(); } } + /// public long GetStylesheetFileSize(string filepath) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _stylesheetRepository.GetFileSize(filepath); } @@ -179,26 +176,17 @@ namespace Umbraco.Cms.Core.Services.Implement #region Scripts - /// - public IEnumerable GetScripts(params string[] names) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _scriptRepository.GetMany(names); - } - } - /// public IScript GetScriptByName(string name) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _scriptRepository.Get(name); } } /// - public void SaveScript(IScript script, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveScript(IScript script, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -219,7 +207,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public void DeleteScript(string path, int userId = Cms.Core.Constants.Security.SuperUserId) + public void DeleteScript(string path, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -247,52 +235,48 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public bool ValidateScript(IScript script) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _scriptRepository.ValidateScript(script); - } - } - public void CreateScriptFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _scriptRepository.AddFolder(folderPath); scope.Complete(); } } + /// public void DeleteScriptFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _scriptRepository.DeleteFolder(folderPath); scope.Complete(); } } + /// public Stream GetScriptFileContentStream(string filepath) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _scriptRepository.GetFileContentStream(filepath); } } + /// public void SetScriptFileContent(string filepath, Stream content) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _scriptRepository.SetFileContent(filepath, content); scope.Complete(); } } + /// public long GetScriptFileSize(string filepath) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _scriptRepository.GetFileSize(filepath); } @@ -311,7 +295,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// The template created /// - public Attempt> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = Cms.Core.Constants.Security.SuperUserId) + public Attempt> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = Constants.Security.SuperUserId) { var template = new Template(_shortStringHelper, contentTypeName, //NOTE: We are NOT passing in the content type alias here, we want to use it's name since we don't @@ -363,7 +347,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - public ITemplate CreateTemplateWithIdentity(string name, string alias, string content, ITemplate masterTemplate = null, int userId = Cms.Core.Constants.Security.SuperUserId) + public ITemplate CreateTemplateWithIdentity(string name, string alias, string content, ITemplate masterTemplate = null, int userId = Constants.Security.SuperUserId) { if (name == null) { @@ -402,7 +386,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// An enumerable list of objects public IEnumerable GetTemplates(params string[] aliases) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.GetAll(aliases).OrderBy(x => x.Name); } @@ -414,7 +398,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// An enumerable list of objects public IEnumerable GetTemplates(int masterTemplateId) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.GetChildren(masterTemplateId).OrderBy(x => x.Name); } @@ -427,7 +411,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// The object matching the alias, or null. public ITemplate GetTemplate(string alias) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.Get(alias); } @@ -440,7 +424,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// The object matching the identifier, or null. public ITemplate GetTemplate(int id) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.Get(id); } @@ -453,21 +437,13 @@ namespace Umbraco.Cms.Core.Services.Implement /// The object matching the identifier, or null. public ITemplate GetTemplate(Guid id) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - var query = Query().Where(x => x.Key == id); + IQuery query = Query().Where(x => x.Key == id); return _templateRepository.Get(query).SingleOrDefault(); } } - public IEnumerable GetTemplateDescendants(string alias) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetDescendants(alias); - } - } - /// /// Gets the template descendants /// @@ -475,44 +451,18 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IEnumerable GetTemplateDescendants(int masterTemplateId) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _templateRepository.GetDescendants(masterTemplateId); } } - /// - /// Gets the template children - /// - /// - /// - public IEnumerable GetTemplateChildren(string alias) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetChildren(alias); - } - } - - /// - /// Gets the template children - /// - /// - /// - public IEnumerable GetTemplateChildren(int masterTemplateId) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetChildren(masterTemplateId); - } - } - /// /// Saves a /// /// to save /// - public void SaveTemplate(ITemplate template, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveTemplate(ITemplate template, int userId = Constants.Security.SuperUserId) { if (template == null) { @@ -549,7 +499,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// List of to save /// Optional id of the user - public void SaveTemplate(IEnumerable templates, int userId = Cms.Core.Constants.Security.SuperUserId) + public void SaveTemplate(IEnumerable templates, int userId = Constants.Security.SuperUserId) { ITemplate[] templatesA = templates.ToArray(); using (IScope scope = ScopeProvider.CreateScope()) @@ -579,7 +529,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Alias of the to delete /// - public void DeleteTemplate(string alias, int userId = Cms.Core.Constants.Security.SuperUserId) + public void DeleteTemplate(string alias, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -607,63 +557,61 @@ namespace Umbraco.Cms.Core.Services.Implement } } - /// - /// Validates a - /// - /// to validate - /// True if Script is valid, otherwise false - public bool ValidateTemplate(ITemplate template) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.ValidateTemplate(template); - } - } - - public Stream GetTemplateFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetFileContentStream(filepath); - } - } - - public void SetTemplateFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _templateRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public long GetTemplateFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _templateRepository.GetFileSize(filepath); - } - } - private string GetViewContent(string fileName) { if (fileName.IsNullOrWhiteSpace()) + { throw new ArgumentNullException(nameof(fileName)); + } if (!fileName.EndsWith(".cshtml")) + { fileName = $"{fileName}.cshtml"; + } + + Stream fs = _templateRepository.GetFileContentStream(fileName); + if (fs == null) + { + return null; + } - var fs = _templateRepository.GetFileContentStream(fileName); - if (fs == null) return null; using (var view = new StreamReader(fs)) { return view.ReadToEnd().Trim(); } } -#endregion + /// + public Stream GetTemplateFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _templateRepository.GetFileContentStream(filepath); + } + } -#region Partial Views + /// + public void SetTemplateFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _templateRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetTemplateFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _templateRepository.GetFileSize(filepath); + } + } + + #endregion + + #region Partial Views public IEnumerable GetPartialViewSnippetNames(params string[] filterNames) { @@ -683,7 +631,7 @@ namespace Umbraco.Cms.Core.Services.Implement public void DeletePartialViewFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewRepository.DeleteFolder(folderPath); scope.Complete(); @@ -692,7 +640,7 @@ namespace Umbraco.Cms.Core.Services.Implement public void DeletePartialViewMacroFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewMacroRepository.DeleteFolder(folderPath); scope.Complete(); @@ -701,7 +649,7 @@ namespace Umbraco.Cms.Core.Services.Implement public IPartialView GetPartialView(string path) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _partialViewRepository.Get(path); } @@ -709,31 +657,19 @@ namespace Umbraco.Cms.Core.Services.Implement public IPartialView GetPartialViewMacro(string path) { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { return _partialViewMacroRepository.Get(path); } } - public IEnumerable GetPartialViewMacros(params string[] names) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.GetMany(names).OrderBy(x => x.Name); - } - } + public Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId) => + CreatePartialViewMacro(partialView, PartialViewType.PartialView, snippetName, userId); - public Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return CreatePartialViewMacro(partialView, PartialViewType.PartialView, snippetName, userId); - } + public Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId) => + CreatePartialViewMacro(partialView, PartialViewType.PartialViewMacro, snippetName, userId); - public Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return CreatePartialViewMacro(partialView, PartialViewType.PartialViewMacro, snippetName, userId); - } - - private Attempt CreatePartialViewMacro(IPartialView partialView, PartialViewType partialViewType, string snippetName = null, int userId = Cms.Core.Constants.Security.SuperUserId) + private Attempt CreatePartialViewMacro(IPartialView partialView, PartialViewType partialViewType, string snippetName = null, int userId = Constants.Security.SuperUserId) { string partialViewHeader; switch (partialViewType) @@ -803,17 +739,13 @@ namespace Umbraco.Cms.Core.Services.Implement return Attempt.Succeed(partialView); } - public bool DeletePartialView(string path, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return DeletePartialViewMacro(path, PartialViewType.PartialView, userId); - } + public bool DeletePartialView(string path, int userId = Constants.Security.SuperUserId) => + DeletePartialViewMacro(path, PartialViewType.PartialView, userId); - public bool DeletePartialViewMacro(string path, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return DeletePartialViewMacro(path, PartialViewType.PartialViewMacro, userId); - } + public bool DeletePartialViewMacro(string path, int userId = Constants.Security.SuperUserId) => + DeletePartialViewMacro(path, PartialViewType.PartialViewMacro, userId); - private bool DeletePartialViewMacro(string path, PartialViewType partialViewType, int userId = Cms.Core.Constants.Security.SuperUserId) + private bool DeletePartialViewMacro(string path, PartialViewType partialViewType, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -843,17 +775,13 @@ namespace Umbraco.Cms.Core.Services.Implement return true; } - public Attempt SavePartialView(IPartialView partialView, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return SavePartialView(partialView, PartialViewType.PartialView, userId); - } + public Attempt SavePartialView(IPartialView partialView, int userId = Constants.Security.SuperUserId) => + SavePartialView(partialView, PartialViewType.PartialView, userId); - public Attempt SavePartialViewMacro(IPartialView partialView, int userId = Cms.Core.Constants.Security.SuperUserId) - { - return SavePartialView(partialView, PartialViewType.PartialViewMacro, userId); - } + public Attempt SavePartialViewMacro(IPartialView partialView, int userId = Constants.Security.SuperUserId) => + SavePartialView(partialView, PartialViewType.PartialViewMacro, userId); - private Attempt SavePartialView(IPartialView partialView, PartialViewType partialViewType, int userId = Cms.Core.Constants.Security.SuperUserId) + private Attempt SavePartialView(IPartialView partialView, PartialViewType partialViewType, int userId = Constants.Security.SuperUserId) { using (IScope scope = ScopeProvider.CreateScope()) { @@ -877,22 +805,6 @@ namespace Umbraco.Cms.Core.Services.Implement return Attempt.Succeed(partialView); } - public bool ValidatePartialView(IPartialView partialView) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewRepository.ValidatePartialView(partialView); - } - } - - public bool ValidatePartialViewMacro(IPartialView partialView) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.ValidatePartialView(partialView); - } - } - internal string StripPartialViewHeader(string contents) { var headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline); @@ -912,43 +824,9 @@ namespace Umbraco.Cms.Core.Services.Implement : Attempt.Fail(); } - public Stream GetPartialViewMacroFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.GetFileContentStream(filepath); - } - } - - public void SetPartialViewMacroFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _partialViewMacroRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public Stream GetPartialViewFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewRepository.GetFileContentStream(filepath); - } - } - - public void SetPartialViewFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _partialViewRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - public void CreatePartialViewFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewRepository.AddFolder(folderPath); scope.Complete(); @@ -957,29 +835,13 @@ namespace Umbraco.Cms.Core.Services.Implement public void CreatePartialViewMacroFolder(string folderPath) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { _partialViewMacroRepository.AddFolder(folderPath); scope.Complete(); } } - public long GetPartialViewMacroFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewMacroRepository.GetFileSize(filepath); - } - } - - public long GetPartialViewFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _partialViewRepository.GetFileSize(filepath); - } - } - private IPartialViewRepository GetPartialViewRepository(PartialViewType partialViewType) { switch (partialViewType) @@ -993,24 +855,76 @@ namespace Umbraco.Cms.Core.Services.Implement } } + /// + public Stream GetPartialViewFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewRepository.GetFileContentStream(filepath); + } + } + + /// + public void SetPartialViewFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _partialViewRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetPartialViewFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewRepository.GetFileSize(filepath); + } + } + + /// + public Stream GetPartialViewMacroFileContentStream(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewMacroRepository.GetFileContentStream(filepath); + } + } + + /// + public void SetPartialViewMacroFileContent(string filepath, Stream content) + { + using (IScope scope = ScopeProvider.CreateScope()) + { + _partialViewMacroRepository.SetFileContent(filepath, content); + scope.Complete(); + } + } + + /// + public long GetPartialViewMacroFileSize(string filepath) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewMacroRepository.GetFileSize(filepath); + } + } + #endregion #region Snippets - public string GetPartialViewSnippetContent(string snippetName) - { - return GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialView); - } + public string GetPartialViewSnippetContent(string snippetName) => GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialView); - public string GetPartialViewMacroSnippetContent(string snippetName) - { - return GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialViewMacro); - } + public string GetPartialViewMacroSnippetContent(string snippetName) => GetPartialViewMacroSnippetContent(snippetName, PartialViewType.PartialViewMacro); private string GetPartialViewMacroSnippetContent(string snippetName, PartialViewType partialViewType) { if (snippetName.IsNullOrWhiteSpace()) + { throw new ArgumentNullException(nameof(snippetName)); + } string partialViewHeader; switch (partialViewType) @@ -1026,7 +940,7 @@ namespace Umbraco.Cms.Core.Services.Implement } // Try and get the snippet path - var snippetPathAttempt = TryGetSnippetPath(snippetName); + Attempt snippetPathAttempt = TryGetSnippetPath(snippetName); if (snippetPathAttempt.Success == false) { throw new InvalidOperationException("Could not load snippet with name " + snippetName); @@ -1054,10 +968,7 @@ namespace Umbraco.Cms.Core.Services.Implement #endregion - private void Audit(AuditType type, int userId, int objectId, string entityType) - { - _auditRepository.Save(new AuditItem(objectId, type, userId, entityType)); - } + private void Audit(AuditType type, int userId, int objectId, string entityType) => _auditRepository.Save(new AuditItem(objectId, type, userId, entityType)); // TODO: Method to change name and/or alias of view template } diff --git a/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs b/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs index 0e6b89693d..b024b8935e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/LocalizationService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs b/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs index 6d053eb1d1..a79d9fddce 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MacroService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Services.Implement { diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 831cbb1b01..34d1c2a5ce 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -7,11 +7,11 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; @@ -29,16 +29,16 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IEntityRepository _entityRepository; private readonly IShortStringHelper _shortStringHelper; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; #region Constructors - public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, + public MediaService(IScopeProvider provider, MediaFileManager mediaFileManager, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, IEntityRepository entityRepository, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _mediaRepository = mediaRepository; _auditRepository = auditRepository; _mediaTypeRepository = mediaTypeRepository; @@ -1183,12 +1183,12 @@ namespace Umbraco.Cms.Core.Services.Implement public Stream GetMediaFileContentStream(string filepath) { - if (_mediaFileSystem.FileExists(filepath) == false) + if (_mediaFileManager.FileSystem.FileExists(filepath) == false) return null; try { - return _mediaFileSystem.OpenFile(filepath); + return _mediaFileManager.FileSystem.OpenFile(filepath); } catch { @@ -1198,17 +1198,17 @@ namespace Umbraco.Cms.Core.Services.Implement public void SetMediaFileContent(string filepath, Stream stream) { - _mediaFileSystem.AddFile(filepath, stream, true); + _mediaFileManager.FileSystem.AddFile(filepath, stream, true); } public void DeleteMediaFile(string filepath) { - _mediaFileSystem.DeleteFile(filepath); + _mediaFileManager.FileSystem.DeleteFile(filepath); } public long GetMediaFileSize(string filepath) { - return _mediaFileSystem.GetSize(filepath); + return _mediaFileManager.FileSystem.GetSize(filepath); } #endregion diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs index ede72f77a5..205e5b176e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaTypeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs index a41a19b541..096ff164a0 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; namespace Umbraco.Cms.Core.Services.Implement { diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs index 78f6d410a0..94476ff1e1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs @@ -5,10 +5,10 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs index 7f9e6e96e3..ad0498d0b2 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberTypeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Changes; diff --git a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs index f3b199751e..3df297b47a 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PackagingService.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Packaging; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.Semver; using Umbraco.Extensions; diff --git a/src/Umbraco.Infrastructure/Services/Implement/PropertyValidationService.cs b/src/Umbraco.Infrastructure/Services/Implement/PropertyValidationService.cs index e196f2d70d..5fd7971976 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PropertyValidationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PropertyValidationService.cs @@ -21,6 +21,7 @@ namespace Umbraco.Cms.Core.Services.Implement _textService = textService; } + /// public IEnumerable ValidatePropertyValue( IPropertyType propertyType, object postedValue) @@ -35,6 +36,7 @@ namespace Umbraco.Cms.Core.Services.Implement return ValidatePropertyValue(editor, dataType, postedValue, propertyType.Mandatory, propertyType.ValidationRegExp, propertyType.MandatoryMessage, propertyType.ValidationRegExpMessage); } + /// public IEnumerable ValidatePropertyValue( IDataEditor editor, IDataType dataType, diff --git a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs index dadee3b24a..419881fe1c 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/PublicAccessService.cs @@ -4,9 +4,9 @@ using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs b/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs index 668544af02..7a5d10c222 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/RelationService.cs @@ -5,10 +5,10 @@ using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services.Implement diff --git a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs index 9c03f9aabc..248de428a8 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ServerRegistrationService.cs @@ -154,7 +154,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Gets the local server identity. /// - private string GetCurrentServerIdentity() => NetworkHelper.MachineName // eg DOMAIN\SERVER + private string GetCurrentServerIdentity() => Environment.MachineName // eg DOMAIN\SERVER + "/" + _hostingEnvironment.ApplicationId; // eg /LM/S3SVC/11/ROOT; } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs index 956370a056..743b4816da 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs @@ -9,10 +9,10 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; using Umbraco.Extensions; @@ -24,23 +24,25 @@ namespace Umbraco.Cms.Core.Services.Implement /// internal class UserService : RepositoryService, IUserService { + private readonly IRuntimeState _runtimeState; private readonly IUserRepository _userRepository; private readonly IUserGroupRepository _userGroupRepository; private readonly GlobalSettings _globalSettings; - private readonly bool _isUpgrading; private readonly ILogger _logger; public UserService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IRuntimeState runtimeState, IUserRepository userRepository, IUserGroupRepository userGroupRepository, IOptions globalSettings) : base(provider, loggerFactory, eventMessagesFactory) { + _runtimeState = runtimeState; _userRepository = userRepository; _userGroupRepository = userGroupRepository; _globalSettings = globalSettings.Value; - _isUpgrading = runtimeState.Level == RuntimeLevel.Install || runtimeState.Level == RuntimeLevel.Upgrade; _logger = loggerFactory.CreateLogger(); } + private bool IsUpgrading => _runtimeState.Level == RuntimeLevel.Install || _runtimeState.Level == RuntimeLevel.Upgrade; + #region Implementation of IMembershipUserService /// @@ -205,7 +207,7 @@ namespace Umbraco.Cms.Core.Services.Implement // currently kinda accepting anything on upgrade, but that won't deal with all cases // so we need to do it differently, see the custom UmbracoPocoDataBuilder which should // be better BUT requires that the app restarts after the upgrade! - if (_isUpgrading) + if (IsUpgrading) { //NOTE: this will not be cached return _userRepository.GetByUsername(username, includeSecurityData: false); @@ -305,7 +307,7 @@ namespace Umbraco.Cms.Core.Services.Implement catch (DbException ex) { // if we are upgrading and an exception occurs, log and swallow it - if (_isUpgrading == false) throw; + if (IsUpgrading == false) throw; _logger.LogWarning(ex, "An error occurred attempting to save a user instance during upgrade, normally this warning can be ignored"); @@ -681,7 +683,7 @@ namespace Umbraco.Cms.Core.Services.Implement // currently kinda accepting anything on upgrade, but that won't deal with all cases // so we need to do it differently, see the custom UmbracoPocoDataBuilder which should // be better BUT requires that the app restarts after the upgrade! - if (_isUpgrading) + if (IsUpgrading) { //NOTE: this will not be cached return _userRepository.Get(id, includeSecurityData: false); diff --git a/src/Umbraco.Infrastructure/Suspendable.cs b/src/Umbraco.Infrastructure/Suspendable.cs index 73798f8cf1..022a641094 100644 --- a/src/Umbraco.Infrastructure/Suspendable.cs +++ b/src/Umbraco.Infrastructure/Suspendable.cs @@ -1,48 +1,55 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Cms.Infrastructure.Search; namespace Umbraco.Cms.Infrastructure { - internal static class Suspendable + public static class Suspendable { public static class PageCacheRefresher { - private static bool _tried, _suspended; + private static bool s_tried, s_suspended; public static bool CanRefreshDocumentCacheFromDatabase { get { // trying a full refresh - if (_suspended == false) return true; - _tried = true; // remember we tried + if (s_suspended == false) + { + return true; + } + + s_tried = true; // remember we tried return false; } } // trying a partial update // ok if not suspended, or if we haven't done a full already - public static bool CanUpdateDocumentCache => _suspended == false || _tried == false; + public static bool CanUpdateDocumentCache => s_suspended == false || s_tried == false; public static void SuspendDocumentCache() { StaticApplicationLogging.Logger.LogInformation("Suspend document cache."); - _suspended = true; + s_suspended = true; } public static void ResumeDocumentCache(CacheRefresherCollection cacheRefresherCollection) { - _suspended = false; + s_suspended = false; - StaticApplicationLogging.Logger.LogInformation("Resume document cache (reload:{Tried}).", _tried); + StaticApplicationLogging.Logger.LogInformation("Resume document cache (reload:{Tried}).", s_tried); - if (_tried == false) return; - _tried = false; + if (s_tried == false) + { + return; + } - var pageRefresher = cacheRefresherCollection[ContentCacheRefresher.UniqueId]; + s_tried = false; + + ICacheRefresher pageRefresher = cacheRefresherCollection[ContentCacheRefresher.UniqueId]; pageRefresher.RefreshAll(); } } @@ -51,14 +58,18 @@ namespace Umbraco.Cms.Infrastructure // AHH... but Deploy probably uses this? public static class ExamineEvents { - private static bool _tried, _suspended; + private static bool s_tried, s_suspended; public static bool CanIndex { get { - if (_suspended == false) return true; - _tried = true; // remember we tried + if (s_suspended == false) + { + return true; + } + + s_tried = true; // remember we tried return false; } } @@ -66,17 +77,21 @@ namespace Umbraco.Cms.Infrastructure public static void SuspendIndexers(ILogger logger) { logger.LogInformation("Suspend indexers."); - _suspended = true; + s_suspended = true; } - public static void ResumeIndexers(IndexRebuilder indexRebuilder, ILogger logger, BackgroundIndexRebuilder backgroundIndexRebuilder) + public static void ResumeIndexers(ExamineIndexRebuilder backgroundIndexRebuilder) { - _suspended = false; + s_suspended = false; - StaticApplicationLogging.Logger.LogInformation("Resume indexers (rebuild:{Tried}).", _tried); + StaticApplicationLogging.Logger.LogInformation("Resume indexers (rebuild:{Tried}).", s_tried); - if (_tried == false) return; - _tried = false; + if (s_tried == false) + { + return; + } + + s_tried = false; backgroundIndexRebuilder.RebuildIndexes(false); } @@ -84,20 +99,20 @@ namespace Umbraco.Cms.Infrastructure public static class ScheduledPublishing { - private static bool _suspended; + private static bool s_suspended; - public static bool CanRun => _suspended == false; + public static bool CanRun => s_suspended == false; public static void Suspend() { StaticApplicationLogging.Logger.LogInformation("Suspend scheduled publishing."); - _suspended = true; + s_suspended = true; } public static void Resume() { StaticApplicationLogging.Logger.LogInformation("Resume scheduled publishing."); - _suspended = false; + s_suspended = false; } } } diff --git a/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs index 940ebfe0cd..9bae34cf3e 100644 --- a/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/BatchedDatabaseServerMessenger.cs @@ -27,15 +27,18 @@ namespace Umbraco.Cms.Infrastructure.Sync /// public BatchedDatabaseServerMessenger( IMainDom mainDom, + CacheRefresherCollection cacheRefreshers, + IServerRoleAccessor serverRoleAccessor, ILogger logger, - DatabaseServerMessengerCallbacks callbacks, + ISyncBootStateAccessor syncBootStateAccessor, IHostingEnvironment hostingEnvironment, ICacheInstructionService cacheInstructionService, IJsonSerializer jsonSerializer, IRequestCache requestCache, IRequestAccessor requestAccessor, + LastSyncedFileManager lastSyncedFileManager, IOptions globalSettings) - : base(mainDom, logger, true, callbacks, hostingEnvironment, cacheInstructionService, jsonSerializer, globalSettings) + : base(mainDom, cacheRefreshers, serverRoleAccessor, logger, true, syncBootStateAccessor, hostingEnvironment, cacheInstructionService, jsonSerializer, lastSyncedFileManager, globalSettings) { _requestCache = requestCache; _requestAccessor = requestAccessor; diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index 0b2076a3a7..ee8793f5c9 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; -using System.IO; using System.Linq; using System.Threading; using Microsoft.Extensions.Logging; @@ -15,7 +13,6 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Sync { @@ -31,57 +28,61 @@ namespace Umbraco.Cms.Infrastructure.Sync */ private readonly IMainDom _mainDom; + private readonly CacheRefresherCollection _cacheRefreshers; + private readonly IServerRoleAccessor _serverRoleAccessor; + private readonly ISyncBootStateAccessor _syncBootStateAccessor; private readonly ManualResetEvent _syncIdle; private readonly object _locko = new object(); private readonly IHostingEnvironment _hostingEnvironment; - - private readonly Lazy _distCacheFilePath; - private int _lastId = -1; + private readonly LastSyncedFileManager _lastSyncedFileManager; private DateTime _lastSync; private DateTime _lastPruned; - private readonly Lazy _initialized; + private readonly Lazy _initialized; private bool _syncing; - private bool _released; + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationToken _cancellationToken; /// /// Initializes a new instance of the class. /// protected DatabaseServerMessenger( IMainDom mainDom, + CacheRefresherCollection cacheRefreshers, + IServerRoleAccessor serverRoleAccessor, ILogger logger, bool distributedEnabled, - DatabaseServerMessengerCallbacks callbacks, + ISyncBootStateAccessor syncBootStateAccessor, IHostingEnvironment hostingEnvironment, ICacheInstructionService cacheInstructionService, IJsonSerializer jsonSerializer, + LastSyncedFileManager lastSyncedFileManager, IOptions globalSettings) : base(distributedEnabled) { + _cancellationToken = _cancellationTokenSource.Token; _mainDom = mainDom; + _cacheRefreshers = cacheRefreshers; + _serverRoleAccessor = serverRoleAccessor; _hostingEnvironment = hostingEnvironment; Logger = logger; - Callbacks = callbacks ?? throw new ArgumentNullException(nameof(callbacks)); + _syncBootStateAccessor = syncBootStateAccessor; CacheInstructionService = cacheInstructionService; JsonSerializer = jsonSerializer; + _lastSyncedFileManager = lastSyncedFileManager; GlobalSettings = globalSettings.Value; _lastPruned = _lastSync = DateTime.UtcNow; _syncIdle = new ManualResetEvent(true); - _distCacheFilePath = new Lazy(() => GetDistCacheFilePath(hostingEnvironment)); // See notes on _localIdentity - LocalIdentity = NetworkHelper.MachineName // eg DOMAIN\SERVER + LocalIdentity = Environment.MachineName // eg DOMAIN\SERVER + "/" + hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT + " [P" + Process.GetCurrentProcess().Id // eg 1234 + "/D" + AppDomain.CurrentDomain.Id // eg 22 + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique - _initialized = new Lazy(EnsureInitialized); + _initialized = new Lazy(InitializeWithMainDom); } - private string DistCacheFilePath => _distCacheFilePath.Value; - - public DatabaseServerMessengerCallbacks Callbacks { get; } - public GlobalSettings GlobalSettings { get; } protected ILogger Logger { get; } @@ -102,12 +103,17 @@ namespace Umbraco.Cms.Infrastructure.Sync /// protected string LocalIdentity { get; } + /// + /// Returns true if initialization was successfull (i.e. Is MainDom) + /// + protected bool EnsureInitialized() => _initialized.Value.HasValue; + #region Messenger // we don't care if there are servers listed or not, // if distributed call is enabled we will make the call protected override bool RequiresDistributed(ICacheRefresher refresher, MessageType dispatchType) - => _initialized.Value && DistributedEnabled; + => EnsureInitialized() && DistributedEnabled; protected override void DeliverRemote( ICacheRefresher refresher, @@ -134,7 +140,7 @@ namespace Umbraco.Cms.Infrastructure.Sync /// /// Boots the messenger. /// - private bool EnsureInitialized() + private SyncBootState? InitializeWithMainDom() { // weight:10, must release *before* the published snapshot service, because once released // the service will *not* be able to properly handle our notifications anymore. @@ -145,15 +151,15 @@ namespace Umbraco.Cms.Infrastructure.Sync { lock (_locko) { - _released = true; // no more syncs + _cancellationTokenSource.Cancel(); // no more syncs } - // Wait a max of 5 seconds and then return, so that we don't block + // Wait a max of 3 seconds and then return, so that we don't block // the entire MainDom callbacks chain and prevent the AppDomain from // properly releasing MainDom - a timeout here means that one refresher // is taking too much time processing, however when it's done we will // not update lastId and stop everything. - var idle = _syncIdle.WaitOne(5000); + var idle = _syncIdle.WaitOne(3000); if (idle == false) { Logger.LogWarning("The wait lock timed out, application is shutting down. The current instruction batch will be re-processed."); @@ -163,17 +169,11 @@ namespace Umbraco.Cms.Infrastructure.Sync if (registered == false) { - return false; + // return null if we cannot initialize + return null; } - ReadLastSynced(); // get _lastId - - if (CacheInstructionService.IsColdBootRequired(_lastId)) - { - _lastId = -1; // reset _lastId if instructions are missing - } - - return Initialize(); // boot + return InitializeColdBootState(); } // @@ -183,70 +183,32 @@ namespace Umbraco.Cms.Infrastructure.Sync /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. /// Callers MUST ensure thread-safety. /// - private bool Initialize() + private SyncBootState InitializeColdBootState() { lock (_locko) { - if (_released) + if (_cancellationToken.IsCancellationRequested) { - return false; + return SyncBootState.Unknown; } - var coldboot = false; + SyncBootState syncState = _syncBootStateAccessor.GetSyncBootState(); - // Never synced before. - if (_lastId < 0) - { - // We haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new - // server and it will need to rebuild it's own caches, e.g. Lucene or the XML cache file. - Logger.LogWarning("No last synced Id found, this generally means this is a new server/install." - + " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in" - + " the database and maintain cache updates based on that Id."); - - coldboot = true; - } - else - { - // Check for how many instructions there are to process, each row contains a count of the number of instructions contained in each - // row so we will sum these numbers to get the actual count. - var limit = GlobalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount; - if (CacheInstructionService.IsInstructionCountOverLimit(_lastId, limit, out int count)) - { - // Too many instructions, proceed to cold boot. - Logger.LogWarning( - "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})." - + " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id" - + " to the latest found in the database and maintain cache updates based on that Id.", - count, limit); - - coldboot = true; - } - } - - if (coldboot) + if (syncState == SyncBootState.ColdBoot) { // Get the last id in the db and store it. // Note: Do it BEFORE initializing otherwise some instructions might get lost // when doing it before. Some instructions might run twice but this is not an issue. var maxId = CacheInstructionService.GetMaxInstructionId(); - // If there is a max currently, or if we've never synced. - if (maxId > 0 || _lastId < 0) + // if there is a max currently, or if we've never synced + if (maxId > 0 || _lastSyncedFileManager.LastSyncedId < 0) { - SaveLastSynced(maxId); - } - - // Execute initializing callbacks. - if (Callbacks.InitializingCallbacks != null) - { - foreach (Action callback in Callbacks.InitializingCallbacks) - { - callback(); - } + _lastSyncedFileManager.SaveLastSyncedId(maxId); } } - return true; + return syncState; } } @@ -255,7 +217,7 @@ namespace Umbraco.Cms.Infrastructure.Sync /// public override void Sync() { - if (!_initialized.Value) + if (!EnsureInitialized()) { return; } @@ -268,7 +230,7 @@ namespace Umbraco.Cms.Infrastructure.Sync } // Don't continue if we are released - if (_released) + if (_cancellationToken.IsCancellationRequested) { return; } @@ -286,7 +248,14 @@ namespace Umbraco.Cms.Infrastructure.Sync try { - CacheInstructionServiceProcessInstructionsResult result = CacheInstructionService.ProcessInstructions(_released, LocalIdentity, _lastPruned, _lastId); + ProcessInstructionsResult result = CacheInstructionService.ProcessInstructions( + _cacheRefreshers, + _serverRoleAccessor.CurrentServerRole, + _cancellationToken, + LocalIdentity, + _lastPruned, + _lastSyncedFileManager.LastSyncedId); + if (result.InstructionsWerePruned) { _lastPruned = _lastSync; @@ -294,7 +263,7 @@ namespace Umbraco.Cms.Infrastructure.Sync if (result.LastId > 0) { - SaveLastSynced(result.LastId); + _lastSyncedFileManager.SaveLastSyncedId(result.LastId); } } finally @@ -309,60 +278,6 @@ namespace Umbraco.Cms.Infrastructure.Sync } } - /// - /// Reads the last-synced id from file into memory. - /// - /// - /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. - /// - private void ReadLastSynced() - { - if (File.Exists(DistCacheFilePath) == false) - { - return; - } - - var content = File.ReadAllText(DistCacheFilePath); - if (int.TryParse(content, out var last)) - { - _lastId = last; - } - } - - /// - /// Updates the in-memory last-synced id and persists it to file. - /// - /// The id. - /// - /// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded. - /// - private void SaveLastSynced(int id) - { - File.WriteAllText(DistCacheFilePath, id.ToString(CultureInfo.InvariantCulture)); - _lastId = id; - } - - private string GetDistCacheFilePath(IHostingEnvironment hostingEnvironment) - { - var fileName = _hostingEnvironment.ApplicationId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt"; - - var distCacheFilePath = Path.Combine(hostingEnvironment.LocalTempPath, "DistCache", fileName); - - //ensure the folder exists - var folder = Path.GetDirectoryName(distCacheFilePath); - if (folder == null) - { - throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath); - } - - if (Directory.Exists(folder) == false) - { - Directory.CreateDirectory(folder); - } - - return distCacheFilePath; - } - #endregion } } diff --git a/src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs b/src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs new file mode 100644 index 0000000000..3b3351fd93 --- /dev/null +++ b/src/Umbraco.Infrastructure/Sync/LastSyncedFileManager.cs @@ -0,0 +1,89 @@ +using System; +using System.Globalization; +using System.IO; +using System.Threading; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Sync +{ + public sealed class LastSyncedFileManager + { + private string _distCacheFile; + private bool _lastIdReady; + private object _lastIdLock; + private int _lastId; + private readonly IHostingEnvironment _hostingEnvironment; + + public LastSyncedFileManager(IHostingEnvironment hostingEnvironment) + => _hostingEnvironment = hostingEnvironment; + + /// + /// Persists the last-synced id to file. + /// + /// The id. + public void SaveLastSyncedId(int id) + { + lock (_lastIdLock) + { + if (!_lastIdReady) + { + throw new InvalidOperationException("Cannot save the last synced id before it is read"); + } + + File.WriteAllText(DistCacheFilePath, id.ToString(CultureInfo.InvariantCulture)); + _lastId = id; + } + } + + /// + /// Returns the last-synced id. + /// + public int LastSyncedId => LazyInitializer.EnsureInitialized( + ref _lastId, + ref _lastIdReady, + ref _lastIdLock, + () => + { + // On first load, read from file, else it will return the in-memory _lastId value + + var distCacheFilePath = DistCacheFilePath; + + if (File.Exists(distCacheFilePath)) + { + var content = File.ReadAllText(distCacheFilePath); + if (int.TryParse(content, out var last)) + { + return last; + } + } + + return -1; + }); + + /// + /// Gets the dist cache file path (once). + /// + /// + public string DistCacheFilePath => LazyInitializer.EnsureInitialized(ref _distCacheFile, () => + { + var fileName = (Environment.MachineName + _hostingEnvironment.ApplicationId).GenerateHash() + "-lastsynced.txt"; + + var distCacheFilePath = Path.Combine(_hostingEnvironment.LocalTempPath, "DistCache", fileName); + + //ensure the folder exists + var folder = Path.GetDirectoryName(distCacheFilePath); + if (folder == null) + { + throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath); + } + + if (Directory.Exists(folder) == false) + { + Directory.CreateDirectory(folder); + } + + return distCacheFilePath; + }); + } +} diff --git a/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs b/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs new file mode 100644 index 0000000000..9a77c57965 --- /dev/null +++ b/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs @@ -0,0 +1,84 @@ +using System.Threading; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Infrastructure.Sync +{ + public class SyncBootStateAccessor : ISyncBootStateAccessor + { + private readonly ILogger _logger; + private readonly LastSyncedFileManager _lastSyncedFileManager; + private readonly GlobalSettings _globalSettings; + private readonly ICacheInstructionService _cacheInstructionService; + + private SyncBootState _syncBootState; + private bool _syncBootStateReady; + private object _syncBootStateLock; + + public SyncBootStateAccessor( + ILogger logger, + LastSyncedFileManager lastSyncedFileManager, + IOptions globalSettings, + ICacheInstructionService cacheInstructionService) + { + _logger = logger; + _lastSyncedFileManager = lastSyncedFileManager; + _globalSettings = globalSettings.Value; + _cacheInstructionService = cacheInstructionService; + } + + public SyncBootState GetSyncBootState() + => LazyInitializer.EnsureInitialized( + ref _syncBootState, + ref _syncBootStateReady, + ref _syncBootStateLock, + () => InitializeColdBootState(_lastSyncedFileManager.LastSyncedId)); + + private SyncBootState InitializeColdBootState(int lastId) + { + var coldboot = false; + + // Never synced before. + if (lastId < 0) + { + // We haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new + // server and it will need to rebuild it's own caches, e.g. Lucene or the XML cache file. + _logger.LogWarning("No last synced Id found, this generally means this is a new server/install. " + + "A cold boot will be triggered."); + + coldboot = true; + } + else + { + if (_cacheInstructionService.IsColdBootRequired(lastId)) + { + _logger.LogWarning("Last synced Id found {LastSyncedId} but was not found in the database. This generally means this server/install " + + " has been idle for too long and the instructions in the database have been pruned. A cold boot will be triggered.", lastId); + + coldboot = true; + } + else + { + // Check for how many instructions there are to process, each row contains a count of the number of instructions contained in each + // row so we will sum these numbers to get the actual count. + var limit = _globalSettings.DatabaseServerMessenger.MaxProcessingInstructionCount; + if (_cacheInstructionService.IsInstructionCountOverLimit(lastId, limit, out int count)) + { + // Too many instructions, proceed to cold boot. + _logger.LogWarning( + "The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount}). " + + "A cold boot will be triggered.", + count, limit); + + coldboot = true; + } + } + } + + return coldboot ? SyncBootState.ColdBoot : SyncBootState.WarmBoot; + } + } +} diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 90554b3401..d6ac151277 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -18,8 +18,8 @@ - - + + @@ -27,29 +27,32 @@ - + - + - + - + + - - + + + all + @@ -107,12 +110,12 @@ - - - - + + + + diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index 4ffa549c3e..74668d3090 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -27,6 +27,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets private readonly IRuntimeMinifier _runtimeMinifier; private readonly IManifestParser _parser; private readonly GlobalSettings _globalSettings; + private readonly CustomBackOfficeAssetsCollection _customBackOfficeAssetsCollection; private readonly IHostingEnvironment _hostingEnvironment; private readonly PropertyEditorCollection _propertyEditorCollection; @@ -35,13 +36,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets IManifestParser parser, PropertyEditorCollection propertyEditorCollection, IHostingEnvironment hostingEnvironment, - IOptions globalSettings) + IOptions globalSettings, + CustomBackOfficeAssetsCollection customBackOfficeAssetsCollection) { _runtimeMinifier = runtimeMinifier; _parser = parser; _propertyEditorCollection = propertyEditorCollection; _hostingEnvironment = hostingEnvironment; _globalSettings = globalSettings.Value; + _customBackOfficeAssetsCollection = customBackOfficeAssetsCollection; } public void CreateBundles() @@ -74,17 +77,21 @@ namespace Umbraco.Cms.Infrastructure.WebAssets .GroupBy(x => x.AssetType) .ToDictionary(x => x.Key, x => x.Select(c => c.FilePath)); + var customAssets = _customBackOfficeAssetsCollection.GroupBy(x => x.DependencyType).ToDictionary(x => x.Key, x => x.Select(c => c.FilePath)); + + var jsAssets = (customAssets.TryGetValue(AssetType.Javascript, out var customScripts) ? customScripts : Enumerable.Empty()) + .Union(propertyEditorAssets.TryGetValue(AssetType.Javascript, out var scripts) ? scripts : Enumerable.Empty()); _runtimeMinifier.CreateJsBundle( UmbracoExtensionsJsBundleName, true, FormatPaths( - GetScriptsForBackOfficeExtensions( - propertyEditorAssets.TryGetValue(AssetType.Javascript, out var scripts) ? scripts : Enumerable.Empty()))); + GetScriptsForBackOfficeExtensions(jsAssets))); + var cssAssets = (customAssets.TryGetValue(AssetType.Css, out var customStyles) ? customStyles : Enumerable.Empty()) + .Union(propertyEditorAssets.TryGetValue(AssetType.Css, out var styles) ? styles : Enumerable.Empty()); _runtimeMinifier.CreateCssBundle( UmbracoCssBundleName, true, FormatPaths( - GetStylesheetsForBackOffice( - propertyEditorAssets.TryGetValue(AssetType.Css, out var styles) ? styles : Enumerable.Empty()))); + GetStylesheetsForBackOffice(cssAssets))); } /// diff --git a/src/Umbraco.Infrastructure/WebAssets/PropertyEditorAssetAttribute.cs b/src/Umbraco.Infrastructure/WebAssets/PropertyEditorAssetAttribute.cs index 06913d4204..9acd0a89be 100644 --- a/src/Umbraco.Infrastructure/WebAssets/PropertyEditorAssetAttribute.cs +++ b/src/Umbraco.Infrastructure/WebAssets/PropertyEditorAssetAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; using Umbraco.Cms.Core.WebAssets; namespace Umbraco.Cms.Infrastructure.WebAssets @@ -10,6 +10,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets /// This wraps a CDF asset /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [Obsolete("Use the BackOfficeAssets collection on IUmbracoBuilder instead. Will be removed in the next major version")] public class PropertyEditorAssetAttribute : Attribute { public AssetType AssetType { get; } diff --git a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs b/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs index c5f4eb128d..a7863bdbb5 100644 --- a/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs +++ b/src/Umbraco.Infrastructure/WebAssets/ServerVariablesParser.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; namespace Umbraco.Cms.Infrastructure.WebAssets { @@ -26,7 +27,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets var vars = Resources.ServerVariables; // Raise event for developers to add custom variables - await _eventAggregator.PublishAsync(new ServerVariablesParsing(items)); + await _eventAggregator.PublishAsync(new ServerVariablesParsingNotification(items)); var json = JObject.FromObject(items); return vars.Replace(Token, json.ToString()); diff --git a/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj b/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj index 93622540e4..508bcf3ccb 100644 --- a/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj +++ b/src/Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj @@ -1,4 +1,4 @@ - + net472 @@ -14,7 +14,14 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + diff --git a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs index 06ec368706..b68662644e 100644 --- a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs @@ -1,12 +1,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; @@ -31,9 +30,6 @@ namespace Umbraco.Extensions // must register default options, required in the service ctor builder.Services.TryAddTransient(factory => new PublishedSnapshotServiceOptions()); builder.SetPublishedSnapshotService(); - - // Add as itself - builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); // replace this service since we want to improve the content/media @@ -71,7 +67,8 @@ namespace Umbraco.Extensions .AddNotificationHandler() .AddNotificationHandler() .AddNotificationHandler() - .AddNotificationHandler(); + .AddNotificationHandler() + ; return builder; } diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs index ead279a199..fc4c64d552 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshot.cs @@ -14,9 +14,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache #region Constructors - public PublishedSnapshot(PublishedSnapshotService service, bool defaultPreview) + public PublishedSnapshot(IPublishedSnapshotService service, bool defaultPreview) { - _service = service; + _service = service as PublishedSnapshotService; _defaultPreview = defaultPreview; } @@ -38,7 +38,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - private PublishedSnapshotElements Elements => _elements ?? (_elements = _service.GetElements(_defaultPreview)); + private PublishedSnapshotElements Elements + { + get + { + if (_service == null) + { + throw new InvalidOperationException($"The {typeof(PublishedSnapshot)} cannot be used when the {typeof(IPublishedSnapshotService)} is not the default type {typeof(PublishedSnapshotService)}"); + } + return _elements ??= _service.GetElements(_defaultPreview); + } + } public void Resync() { diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 916fb2da5e..b236f8ccd0 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -19,6 +19,7 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; using Umbraco.Extensions; @@ -29,6 +30,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache { internal class PublishedSnapshotService : IPublishedSnapshotService { + private readonly PublishedSnapshotServiceOptions _options; + private readonly ISyncBootStateAccessor _syncBootStateAccessor; + private readonly IMainDom _mainDom; private readonly ServiceContext _serviceContext; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; @@ -39,7 +43,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly GlobalSettings _globalSettings; - private readonly IEntityXmlSerializer _entitySerializer; private readonly IPublishedModelFactory _publishedModelFactory; private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly IHostingEnvironment _hostingEnvironment; @@ -49,9 +52,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private bool _isReadSet; private object _isReadyLock; - private readonly ContentStore _contentStore; - private readonly ContentStore _mediaStore; - private readonly SnapDictionary _domainStore; + private ContentStore _contentStore; + private ContentStore _mediaStore; + private SnapDictionary _domainStore; private readonly object _storesLock = new object(); private readonly object _elementsLock = new object(); @@ -73,6 +76,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache public PublishedSnapshotService( PublishedSnapshotServiceOptions options, + ISyncBootStateAccessor syncBootStateAccessor, IMainDom mainDom, ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, @@ -84,11 +88,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache INuCacheContentService publishedContentService, IDefaultCultureAccessor defaultCultureAccessor, IOptions globalSettings, - IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory, IHostingEnvironment hostingEnvironment, IOptions config) { + _options = options; + _syncBootStateAccessor = syncBootStateAccessor; + _mainDom = mainDom; _serviceContext = serviceContext; _publishedContentTypeFactory = publishedContentTypeFactory; _publishedSnapshotAccessor = publishedSnapshotAccessor; @@ -102,41 +108,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache _globalSettings = globalSettings.Value; _hostingEnvironment = hostingEnvironment; _config = config.Value; - - // we need an Xml serializer here so that the member cache can support XPath, - // for members this is done by navigating the serialized-to-xml member - _entitySerializer = entitySerializer; _publishedModelFactory = publishedModelFactory; - - // lock this entire call, we only want a single thread to be accessing the stores at once and within - // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease - // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so - // it will not be able to close the stores until we are done populating (if the store is empty) - lock (_storesLock) - { - if (!options.IgnoreLocalDb) - { - mainDom.Register(MainDomRegister, MainDomRelease); - - // stores are created with a db so they can write to it, but they do not read from it, - // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to - // figure out whether it can read the databases or it should populate them from sql - - _logger.LogInformation("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); - _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localContentDb); - _logger.LogInformation("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); - _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localMediaDb); - } - else - { - _logger.LogInformation("Creating the content store (local db ignored)"); - _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); - _logger.LogInformation("Creating the media store (local db ignored)"); - _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); - } - - _domainStore = new SnapDictionary(); - } } protected PublishedSnapshot CurrentPublishedSnapshot => (PublishedSnapshot)_publishedSnapshotAccessor.PublishedSnapshot; @@ -144,13 +116,29 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // NOTE: These aren't used within this object but are made available internally to improve the IdKey lookup performance // when nucache is enabled. // TODO: Does this need to be here? - internal int GetDocumentId(Guid udi) => GetId(_contentStore, udi); + internal int GetDocumentId(Guid udi) + { + EnsureCaches(); + return GetId(_contentStore, udi); + } - internal int GetMediaId(Guid udi) => GetId(_mediaStore, udi); + internal int GetMediaId(Guid udi) + { + EnsureCaches(); + return GetId(_mediaStore, udi); + } - internal Guid GetDocumentUid(int id) => GetUid(_contentStore, id); + internal Guid GetDocumentUid(int id) + { + EnsureCaches(); + return GetUid(_contentStore, id); + } - internal Guid GetMediaUid(int id) => GetUid(_mediaStore, id); + internal Guid GetMediaUid(int id) + { + EnsureCaches(); + return GetUid(_mediaStore, id); + } private int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? 0; @@ -249,7 +237,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } /// - /// Populates the stores + /// Lazily populates the stores only when they are first requested /// internal void EnsureCaches() => LazyInitializer.EnsureInitialized( ref _isReady, @@ -257,15 +245,43 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ref _isReadyLock, () => { - // even though we are ready locked here we want to ensure that the stores lock is also locked + // lock this entire call, we only want a single thread to be accessing the stores at once and within + // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease + // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so + // it will not be able to close the stores until we are done populating (if the store is empty) lock (_storesLock) { + if (!_options.IgnoreLocalDb) + { + _mainDom.Register(MainDomRegister, MainDomRelease); + + // stores are created with a db so they can write to it, but they do not read from it, + // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to + // figure out whether it can read the databases or it should populate them from sql + + _logger.LogInformation("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); + _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localContentDb); + _logger.LogInformation("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); + _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localMediaDb); + } + else + { + _logger.LogInformation("Creating the content store (local db ignored)"); + _contentStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); + _logger.LogInformation("Creating the media store (local db ignored)"); + _mediaStore = new ContentStore(_publishedSnapshotAccessor, _variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); + } + + _domainStore = new SnapDictionary(); + var okContent = false; var okMedia = false; + SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState(); + try { - if (_localContentDbExists) + if (bootState != SyncBootState.ColdBoot && _localContentDbExists) { okContent = LockAndLoadContent(() => LoadContentFromLocalDbLocked(true)); if (!okContent) @@ -274,7 +290,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache } } - if (_localMediaDbExists) + if (bootState != SyncBootState.ColdBoot && _localMediaDbExists) { okMedia = LockAndLoadMedia(() => LoadMediaFromLocalDbLocked(true)); if (!okMedia) @@ -485,7 +501,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // be processed as soon as we are configured and the messenger processes instructions. // note: notifications for content type and data type changes should be invoked with the - // pure live model factory, if any, locked and refreshed - see ContentTypeCacheRefresher and + // InMemoryModelFactory, if any, locked and refreshed - see ContentTypeCacheRefresher and // DataTypeCacheRefresher public void Notify(ContentCacheRefresher.JsonPayload[] payloads, out bool draftChanged, out bool publishedChanged) @@ -705,17 +721,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache if (_publishedModelFactory.IsLiveFactoryEnabled()) { - // In the case of Pure Live - we actually need to refresh all of the content and the media + // In the case of ModelsMode.InMemoryAuto generated models - we actually need to refresh all of the content and the media // see https://github.com/umbraco/Umbraco-CMS/issues/5671 - // The underlying issue is that in Pure Live the ILivePublishedModelFactory will re-compile all of the classes/models + // The underlying issue is that in ModelsMode.InMemoryAuto mode the IAutoPublishedModelFactory will re-compile all of the classes/models // into a new DLL for the application which includes both content types and media types. // Since the models in the cache are based on these actual classes, all of the objects in the cache need to be updated // to use the newest version of the class. // NOTE: Ideally this can be run on background threads here which would prevent blocking the UI - // as is the case when saving a content type. Intially one would think that it won't be any different + // as is the case when saving a content type. Initially one would think that it won't be any different // between running this here or in another background thread immediately after with regards to how the - // UI will respond because we already know between calling `WithSafeLiveFactoryReset` to reset the PureLive models + // UI will respond because we already know between calling `WithSafeLiveFactoryReset` to reset the generated models // and this code here, that many front-end requests could be attempted to be processed. If that is the case, those pages are going to get a // model binding error and our ModelBindingExceptionFilter is going to to its magic to reload those pages so the end user is none the wiser. // So whether or not this executes 'here' or on a background thread immediately wouldn't seem to make any difference except that we can return diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs index f5043b0e53..898f22e989 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotServiceEventHandler.cs @@ -4,10 +4,10 @@ using System.Linq; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Notifications; using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; using Umbraco.Extensions; diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs index df4f803006..6a75e3e021 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotStatus.cs @@ -11,9 +11,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache private readonly PublishedSnapshotService _service; private readonly INuCacheContentService _publishedContentService; - public PublishedSnapshotStatus(PublishedSnapshotService service, INuCacheContentService publishedContentService) + public PublishedSnapshotStatus(IPublishedSnapshotService service, INuCacheContentService publishedContentService) { - _service = service; + _service = service as PublishedSnapshotService; _publishedContentService = publishedContentService; } @@ -23,6 +23,12 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache /// public string GetStatus() { + if (_service == null) + { + return $"The current {typeof(IPublishedSnapshotService)} is not the default type. A status cannot be determined."; + } + + // TODO: This should be private _service.EnsureCaches(); var dbCacheIsOk = _publishedContentService.VerifyContentDbCache() diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 9aea12912c..cf3ad2018f 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -1,12 +1,12 @@ - + netstandard2.0 Umbraco.Cms.Infrastructure.PublishedCache 8 Umbraco.Cms.PublishedCache.NuCache - Umbraco CMS Published Cache - Contains the Published Cache assembly needed to run Umbraco Cms. This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco + Umbraco CMS Published Cache + Contains the Published Cache assembly needed to run Umbraco Cms. This package only contains the assembly, and can be used for package development. Use the template in the Umbraco.Templates package to setup Umbraco @@ -15,7 +15,14 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + diff --git a/src/Umbraco.TestData/LoadTestComponent.cs b/src/Umbraco.TestData/LoadTestComponent.cs new file mode 100644 index 0000000000..cfd923cd07 --- /dev/null +++ b/src/Umbraco.TestData/LoadTestComponent.cs @@ -0,0 +1,35 @@ +using System.Web.Mvc; +using System.Web.Routing; +using System.Configuration; +using Umbraco.Cms.Core.Composing; + +// see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting + +namespace Umbraco.TestData +{ + public class LoadTestComponent : IComponent + { + public void Initialize() + { + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return; + + + + RouteTable.Routes.MapRoute( + name: "LoadTest", + url: "LoadTest/{action}", + defaults: new + { + controller = "LoadTest", + action = "Index" + }, + namespaces: new[] { "Umbraco.TestData" } + ); + } + + public void Terminate() + { + } + } +} diff --git a/src/Umbraco.TestData/LoadTestComposer.cs b/src/Umbraco.TestData/LoadTestComposer.cs new file mode 100644 index 0000000000..e5b16e5ab1 --- /dev/null +++ b/src/Umbraco.TestData/LoadTestComposer.cs @@ -0,0 +1,31 @@ +using System.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Infrastructure.PublishedCache; + +// see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting + +namespace Umbraco.TestData +{ + public class LoadTestComposer : ComponentComposer, IUserComposer + { + public override void Compose(IUmbracoBuilder builder) + { + base.Compose(builder); + + if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true") + return; + + builder.Services.AddScoped(typeof(LoadTestController), typeof(LoadTestController)); + + if (ConfigurationManager.AppSettings["Umbraco.TestData.IgnoreLocalDb"] == "true") + { + builder.Services.AddSingleton(factory => new PublishedSnapshotServiceOptions + { + IgnoreLocalDb = true + }); + } + } + } +} diff --git a/src/Umbraco.TestData/LoadTestController.cs b/src/Umbraco.TestData/LoadTestController.cs index 817b7a1d76..e1494fbdab 100644 --- a/src/Umbraco.TestData/LoadTestController.cs +++ b/src/Umbraco.TestData/LoadTestController.cs @@ -13,6 +13,9 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using System.IO; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Hosting; // see https://github.com/Shazwazza/UmbracoScripts/tree/master/src/LoadTesting @@ -20,10 +23,14 @@ namespace Umbraco.TestData { public class LoadTestController : Controller { - public LoadTestController(ServiceContext serviceContext, IShortStringHelper shortStringHelper) + public LoadTestController( + ServiceContext serviceContext, + IShortStringHelper shortStringHelper, + IHostingEnvironment hostingEnvironment) { _serviceContext = serviceContext; _shortStringHelper = shortStringHelper; + _hostingEnvironment = hostingEnvironment; } private static readonly Random _random = new Random(); @@ -101,6 +108,7 @@ namespace Umbraco.TestData " + FootHtml; private readonly ServiceContext _serviceContext; private readonly IShortStringHelper _shortStringHelper; + private readonly IHostingEnvironment _hostingEnvironment; private ActionResult ContentHtml(string s) { @@ -266,6 +274,15 @@ namespace Umbraco.TestData HttpRuntime.UnloadAppDomain(); } + public ActionResult ColdBootRestart() + { + Directory.Delete(_hostingEnvironment.MapPathContentRoot(Path.Combine(Constants.SystemDirectories.TempData,"DistCache")), true); + + DoRestart(); + + return Content("Cold Boot Restarted."); + } + public ActionResult Restart() { DoRestart(); diff --git a/src/Umbraco.TestData/Umbraco.TestData.csproj b/src/Umbraco.TestData/Umbraco.TestData.csproj index 7980df2205..f658328192 100644 --- a/src/Umbraco.TestData/Umbraco.TestData.csproj +++ b/src/Umbraco.TestData/Umbraco.TestData.csproj @@ -42,6 +42,8 @@ + + @@ -59,6 +61,10 @@ {3ae7bf57-966b-45a5-910a-954d7c554441} Umbraco.Infrastructure + + {f6de8da0-07cc-4ef2-8a59-2bc81dbb3830} + Umbraco.PublishedCache.NuCache + {651e1350-91b6-44b7-bd60-7207006d7003} Umbraco.Web diff --git a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs.bak similarity index 95% rename from src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs rename to src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs.bak index 33d8ba371b..4f471c5e5f 100644 --- a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs.bak @@ -11,6 +11,9 @@ using Umbraco.Cms.Core; namespace Umbraco.Tests.Benchmarks { + // TODO: I very quickly tried converting this to netcore using the new AssemblyBuilder + // but didn't get far enough. I'm unsure where we use the different concepts of this code. + // some conclusions // - ActivatorCreateInstance is slow // - it's faster to get+invoke the ctor @@ -92,13 +95,15 @@ namespace Umbraco.Tests.Benchmarks // dump to disk so we can review IL code with eg DotPeek var assemblyName = new AssemblyName("Umbraco.Tests.Benchmarks.IL"); - var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save); - var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); + var assembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + var module = assembly.DefineDynamicModule("CtorInvoke"); + //var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll"); var typeBuilder = module.DefineType("CtorInvoke", TypeAttributes.Public | TypeAttributes.Abstract); var expressionMethodBuilder = typeBuilder.DefineMethod("ExpressionCtor", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (IFoo), ctorArgTypes); + expr.CompileToMethod(expressionMethodBuilder); var dynamicMethodBuilder = typeBuilder.DefineMethod("DynamicCtor", diff --git a/src/Umbraco.Tests.Benchmarks/ReflectionUtilities-Unused.cs b/src/Umbraco.Tests.Benchmarks/ReflectionUtilities-Unused.cs.bak similarity index 100% rename from src/Umbraco.Tests.Benchmarks/ReflectionUtilities-Unused.cs rename to src/Umbraco.Tests.Benchmarks/ReflectionUtilities-Unused.cs.bak diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs index 907c21f136..54dc414762 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using BenchmarkDotNet.Attributes; using Microsoft.Extensions.Logging.Abstractions; using Umbraco.Cms.Core.Composing; @@ -6,24 +6,35 @@ using Umbraco.Extensions; namespace Umbraco.Tests.Benchmarks { + [MediumRunJob] [MemoryDiagnoser] public class TypeFinderBenchmarks { + private readonly TypeFinder _typeFinder1; + private readonly TypeFinder _typeFinder2; + + public TypeFinderBenchmarks() + { + _typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + _typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()) + { + QueryWithReferencingAssemblies = false + }; + } + + // TODO: This setting seems to make no difference anymore [Benchmark(Baseline = true)] public void WithGetReferencingAssembliesCheck() { - var typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); - var found = typeFinder1.FindClassesOfType().Count(); + var found = _typeFinder1.FindClassesOfType().Count(); } [Benchmark] public void WithoutGetReferencingAssembliesCheck() { - var typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); - typeFinder2.QueryWithReferencingAssemblies = false; - var found = typeFinder2.FindClassesOfType().Count(); + var found = _typeFinder2.FindClassesOfType().Count(); } } diff --git a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs new file mode 100644 index 0000000000..380e3fca99 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using BenchmarkDotNet.Attributes; +using Microsoft.Extensions.Logging.Abstractions; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Logging; + +namespace Umbraco.Tests.Benchmarks +{ + [MediumRunJob] + [MemoryDiagnoser] + public class TypeLoaderBenchmarks + { + private readonly TypeLoader _typeLoader1; + private readonly TypeLoader _typeLoader2; + + public TypeLoaderBenchmarks() + { + var typeFinder1 = new TypeFinder( + new NullLogger(), + new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), + new VaryingRuntimeHash()); + + var cache = new ObjectCacheAppCache(); + _typeLoader1 = new TypeLoader( + typeFinder1, + cache, + null, + new NullLogger(), + new ProfilingLogger(new NullLogger(), new NoopProfiler())); + + // populate the cache + cache.Insert( + TypeLoader.CacheKey, + GetCache, + TimeSpan.FromDays(1)); + + _typeLoader2 = new TypeLoader( + typeFinder1, + NoAppCache.Instance, + null, + new NullLogger(), + new ProfilingLogger(new NullLogger(), new NoopProfiler())); + } + + /// + /// Use a predefined cache of types (names) - this is an example of what is saved to disk and loaded on startup + /// + /// + private Dictionary<(string, string), IEnumerable> GetCache() + => new Dictionary<(string, string), IEnumerable> + { + [(typeof(ITypeLoaderBenchmarkPlugin).FullName, null)] = new List + { + typeof(CustomAssemblyProvider1).FullName, + typeof(CustomAssemblyProvider2).FullName, + typeof(CustomAssemblyProvider3).FullName + } + }; + + [Benchmark(Baseline = true)] + public void WithTypesCache() + { + var found = _typeLoader1.GetTypes().Count(); + } + + [Benchmark] + public void WithoutTypesCache() + { + var found = _typeLoader2.GetTypes(false).Count(); + } + } + + // These are the types we'll find for the benchmark + + public interface ITypeLoaderBenchmarkPlugin + { + } + + public class CustomAssemblyProvider1 : ITypeLoaderBenchmarkPlugin + { + } + + public class CustomAssemblyProvider2 : ITypeLoaderBenchmarkPlugin + { + } + + public class CustomAssemblyProvider3 : ITypeLoaderBenchmarkPlugin + { + } + +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index a4e916a1e1..837bd4e582 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -1,113 +1,36 @@ - - - + - Debug - AnyCPU - {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} + net5.0 Exe - Umbraco.Tests.Benchmarks - Umbraco.Tests.Benchmarks - v4.7.2 - 512 - true - true 8 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 + false - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false 7.3 - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Core - - - {3ae7bf57-966b-45a5-910a-954d7c554441} - Umbraco.Infrastructure - - - {33085570-9bf2-4065-a9b0-a29d920d13ba} - Umbraco.Persistence.SqlCe - - - {5d3b8245-ada6-453f-a008-50ed04bfe770} - Umbraco.Tests - - - {4c4c194c-b5e4-4991-8f87-4373e24cc19f} - Umbraco.Web.UI - - - {651e1350-91b6-44b7-bd60-7207006d7003} - Umbraco.Web - + + + - 0.12.1 - - - 5.0.0 + 0.13.0 5.0.0 - - 0.2.1 + + 4.16.1 + + + all + + - \ No newline at end of file diff --git a/src/Umbraco.Tests.Common/Builders/DataEditorBuilder.cs b/src/Umbraco.Tests.Common/Builders/DataEditorBuilder.cs index 6a5cb84048..5e3d2212ea 100644 --- a/src/Umbraco.Tests.Common/Builders/DataEditorBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/DataEditorBuilder.cs @@ -1,19 +1,23 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Collections.Generic; -using Microsoft.Extensions.Logging.Abstractions; using Moq; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Serialization; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.Common.Builders.Interfaces; namespace Umbraco.Cms.Tests.Common.Builders { - public class DataEditorBuilder : ChildBuilderBase + public class DataEditorBuilder + : ChildBuilderBase, + IWithAliasBuilder, + IWithNameBuilder { + private string _alias; + private string _name; private readonly ConfigurationEditorBuilder> _explicitConfigurationEditorBuilder; private readonly DataValueEditorBuilder> _explicitValueEditorBuilder; private IDictionary _defaultConfiguration; @@ -39,22 +43,34 @@ namespace Umbraco.Cms.Tests.Common.Builders public override IDataEditor Build() { + var name = _name ?? Guid.NewGuid().ToString(); + var alias = _alias ?? name.ToCamelCase(); + IDictionary defaultConfiguration = _defaultConfiguration ?? new Dictionary(); IConfigurationEditor explicitConfigurationEditor = _explicitConfigurationEditorBuilder.Build(); IDataValueEditor explicitValueEditor = _explicitValueEditorBuilder.Build(); return new DataEditor( - NullLoggerFactory.Instance, - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of()) + Mock.Of()) { + Alias = alias, + Name = name, DefaultConfiguration = defaultConfiguration, ExplicitConfigurationEditor = explicitConfigurationEditor, ExplicitValueEditor = explicitValueEditor }; } + + string IWithAliasBuilder.Alias + { + get => _alias; + set => _alias = value; + } + + string IWithNameBuilder.Name + { + get => _name; + set => _name = value; + } } } diff --git a/src/Umbraco.Tests.Common/Builders/DataValueEditorBuilder.cs b/src/Umbraco.Tests.Common/Builders/DataValueEditorBuilder.cs index 7560ac9b2b..633cbb4ca0 100644 --- a/src/Umbraco.Tests.Common/Builders/DataValueEditorBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/DataValueEditorBuilder.cs @@ -55,8 +55,6 @@ namespace Umbraco.Cms.Tests.Common.Builders var valueType = _valueType ?? Guid.NewGuid().ToString(); return new DataValueEditor( - Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()) diff --git a/src/Umbraco.Tests.Common/Builders/MediaTypeBuilder.cs b/src/Umbraco.Tests.Common/Builders/MediaTypeBuilder.cs index 6c11f99b08..3ae17bec2f 100644 --- a/src/Umbraco.Tests.Common/Builders/MediaTypeBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/MediaTypeBuilder.cs @@ -3,10 +3,10 @@ using System.Collections.Generic; using System.Linq; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Tests.Common.Builders.Extensions; using Umbraco.Cms.Tests.Common.Builders.Interfaces; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Common.Builders { @@ -253,5 +253,33 @@ namespace Umbraco.Cms.Tests.Common.Builders get => _propertyTypeIdsIncrementingFrom; set => _propertyTypeIdsIncrementingFrom = value; } + + public static MediaType CreateNewMediaType() + { + var builder = new MediaTypeBuilder(); + IMediaType mediaType = builder + .WithAlias("newMediaType") + .WithName("New Media Type") + .AddPropertyGroup() + .WithName("Media") + .WithSortOrder(1) + .AddPropertyType() + .WithAlias("title") + .WithName("Title") + .WithSortOrder(1) + .Done() + .AddPropertyType() + .WithAlias("videoFile") + .WithName("Video file") + .WithSortOrder(1) + .Done() + .Done() + .Build(); + + // Ensure that nothing is marked as dirty + mediaType.ResetDirtyProperties(false); + + return (MediaType)mediaType; + } } } diff --git a/src/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs b/src/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs index a3e8ee410a..16eb4adda0 100644 --- a/src/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs +++ b/src/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Tests.Common.Published public class TestElementModel1 : PublishedElementModel { public TestElementModel1(IPublishedElement content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Tests.Common.Published public class TestElementModel2 : PublishedElementModel { public TestElementModel2(IPublishedElement content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Tests.Common.Published public class TestContentModel1 : PublishedContentModel { public TestContentModel1(IPublishedContent content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } @@ -47,7 +47,7 @@ namespace Umbraco.Cms.Tests.Common.Published public class TestContentModel2 : PublishedContentModel { public TestContentModel2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } diff --git a/src/Umbraco.Core/TaskHelper.cs b/src/Umbraco.Tests.Common/TaskHelper.cs similarity index 95% rename from src/Umbraco.Core/TaskHelper.cs rename to src/Umbraco.Tests.Common/TaskHelper.cs index ba9f865eba..8b22f7b47d 100644 --- a/src/Umbraco.Core/TaskHelper.cs +++ b/src/Umbraco.Tests.Common/TaskHelper.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -namespace Umbraco.Cms.Core +namespace Umbraco.Cms.Tests.Common { /// /// Helper class to not repeat common patterns with Task. @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Core public void RunBackgroundTask(Func fn) => ExecuteBackgroundTask(fn); // for tests, returning the Task as a public API indicates it can be awaited that is not what we want to do - internal Task ExecuteBackgroundTask(Func fn) + public Task ExecuteBackgroundTask(Func fn) { // it is also possible to use UnsafeQueueUserWorkItem which does not flow the execution context, // however that seems more difficult to use for async operations. @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Core public void RunLongRunningBackgroundTask(Func fn) => ExecuteLongRunningBackgroundTask(fn); // for tests, returning the Task as a public API indicates it can be awaited that is not what we want to do - internal Task ExecuteLongRunningBackgroundTask(Func fn) + public Task ExecuteLongRunningBackgroundTask(Func fn) { // it is also possible to use UnsafeQueueUserWorkItem which does not flow the execution context, // however that seems more difficult to use for async operations. diff --git a/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs b/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs new file mode 100644 index 0000000000..d9d5de1ebd --- /dev/null +++ b/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Tests.Common.TestHelpers +{ + public static class FileSystemsCreator + { + /// + /// Create an instance FileSystems where you can set the individual filesystems. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static FileSystems CreateTestFileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment, + IFileSystem macroPartialFileSystem, + IFileSystem partialViewsFileSystem, + IFileSystem stylesheetFileSystem, + IFileSystem scriptsFileSystem, + IFileSystem mvcViewFileSystem) => + new FileSystems(loggerFactory, ioHelper, globalSettings, hostingEnvironment, macroPartialFileSystem, + partialViewsFileSystem, stylesheetFileSystem, scriptsFileSystem, mvcViewFileSystem); + } +} diff --git a/src/Umbraco.Tests.Common/TestHelpers/MockedValueEditors.cs b/src/Umbraco.Tests.Common/TestHelpers/MockedValueEditors.cs index d246c7c651..2468f54671 100644 --- a/src/Umbraco.Tests.Common/TestHelpers/MockedValueEditors.cs +++ b/src/Umbraco.Tests.Common/TestHelpers/MockedValueEditors.cs @@ -2,6 +2,8 @@ // See LICENSE for more details. using Moq; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -16,11 +18,10 @@ namespace Umbraco.Cms.Tests.Common.TestHelpers var valueType = ValueTypes.IsValue(name) ? name : ValueTypes.String; return new DataValueEditor( - Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), new JsonNetSerializer(), + Mock.Of(), new DataEditorAttribute(name, name, name) { ValueType = valueType diff --git a/src/Umbraco.Tests.Common/TestHelpers/SolidPublishedSnapshot.cs b/src/Umbraco.Tests.Common/TestHelpers/SolidPublishedSnapshot.cs index 40e47dcd1e..17d167bf0d 100644 --- a/src/Umbraco.Tests.Common/TestHelpers/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests.Common/TestHelpers/SolidPublishedSnapshot.cs @@ -300,7 +300,7 @@ namespace Umbraco.Cms.Tests.Common.TestHelpers public class ContentType2 : PublishedContentModel { public ContentType2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } @@ -319,7 +319,7 @@ namespace Umbraco.Cms.Tests.Common.TestHelpers public class PublishedContentStrong1 : PublishedContentModel { public PublishedContentStrong1(IPublishedContent content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } @@ -339,7 +339,7 @@ namespace Umbraco.Cms.Tests.Common.TestHelpers public class PublishedContentStrong2 : PublishedContentModel { public PublishedContentStrong2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content) + : base(content, fallback) { } @@ -358,12 +358,7 @@ namespace Umbraco.Cms.Tests.Common.TestHelpers var dataType = new DataType( new VoidEditor( - Mock.Of(), - dataTypeServiceMock.Object, - Mock.Of(), - Mock.Of(), - Mock.Of(), - jsonSerializer), + Mock.Of()), configurationEditorJsonSerializer) { Id = 666 diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs b/src/Umbraco.Tests.Common/Testing/TestHostingEnvironment.cs similarity index 62% rename from src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs rename to src/Umbraco.Tests.Common/Testing/TestHostingEnvironment.cs index 8980a91cff..e34161a3c2 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs +++ b/src/Umbraco.Tests.Common/Testing/TestHostingEnvironment.cs @@ -7,15 +7,22 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Web.Common.AspNetCore; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; -namespace Umbraco.Cms.Tests.Integration.Implementations +namespace Umbraco.Cms.Tests.Common.Testing { - public class TestHostingEnvironment : AspNetCoreHostingEnvironment, Cms.Core.Hosting.IHostingEnvironment + public class TestHostingEnvironment : AspNetCoreHostingEnvironment, IHostingEnvironment { - public TestHostingEnvironment(IOptionsMonitor hostingSettings,IOptionsMonitor webRoutingSettings, IWebHostEnvironment webHostEnvironment) - : base(hostingSettings,webRoutingSettings, webHostEnvironment) + public TestHostingEnvironment( + IOptionsMonitor hostingSettings, + IOptionsMonitor webRoutingSettings, + IWebHostEnvironment webHostEnvironment) + : base(null, hostingSettings, webRoutingSettings, webHostEnvironment) { } + // override + string IHostingEnvironment.ApplicationId { get; } = "TestApplication"; + + /// /// Gets a value indicating whether we are hosted. /// diff --git a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index a13d6b763b..c0eae36505 100644 --- a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + net5.0 Umbraco.Cms.Tests.Common Umbraco.Cms.Tests Umbraco CMS Test Tools @@ -10,9 +10,13 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + @@ -20,5 +24,6 @@ + diff --git a/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs b/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs index ddac52872f..baa194b17e 100644 --- a/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs +++ b/src/Umbraco.Tests.Integration/ComponentRuntimeTests.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; +using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration { @@ -18,7 +19,11 @@ namespace Umbraco.Cms.Tests.Integration public class ComponentRuntimeTests : UmbracoIntegrationTest { // ensure composers are added - protected override void CustomTestSetup(IUmbracoBuilder builder) => builder.AddComposers(); + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.AddNuCache(); + builder.AddComposers(); + } /// /// This will boot up umbraco with components enabled to show they initialize and shutdown diff --git a/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs index 5cc94ce4c9..bb0da4d08a 100644 --- a/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Tests.Integration/DependencyInjection/UmbracoBuilderExtensions.cs @@ -2,8 +2,11 @@ // See LICENSE for more details. using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using Examine; +using Examine.Lucene.Directories; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -21,7 +24,6 @@ using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.HostedServices; using Umbraco.Cms.Infrastructure.PublishedCache; -using Umbraco.Cms.Infrastructure.Search; using Umbraco.Cms.Tests.Common.TestHelpers.Stubs; using Umbraco.Cms.Tests.Integration.Implementations; using Umbraco.Extensions; @@ -43,7 +45,7 @@ namespace Umbraco.Cms.Tests.Integration.DependencyInjection builder.Services.AddUnique(Mock.Of()); builder.Services.AddUnique(testHelper.MainDom); - builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(factory => Mock.Of()); // we don't want persisted nucache files in tests @@ -51,7 +53,7 @@ namespace Umbraco.Cms.Tests.Integration.DependencyInjection #if IS_WINDOWS // ensure all lucene indexes are using RAM directory (no file system) - builder.Services.AddUnique(); + builder.Services.AddUnique(); #endif // replace this service so that it can lookup the correct file locations @@ -97,18 +99,18 @@ namespace Umbraco.Cms.Tests.Integration.DependencyInjection } // replace the default so there is no background index rebuilder - private class TestBackgroundIndexRebuilder : BackgroundIndexRebuilder + private class TestBackgroundIndexRebuilder : ExamineIndexRebuilder { - public TestBackgroundIndexRebuilder( - IMainDom mainDom, - ILogger logger, - IndexRebuilder indexRebuilder, - IBackgroundTaskQueue backgroundTaskQueue) - : base(mainDom, logger, indexRebuilder, backgroundTaskQueue) + public TestBackgroundIndexRebuilder(IMainDom mainDom, IRuntimeState runtimeState, ILogger logger, IExamineManager examineManager, IEnumerable populators, IBackgroundTaskQueue backgroundTaskQueue) : base(mainDom, runtimeState, logger, examineManager, populators, backgroundTaskQueue) { } - public override void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null) + public override void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true) + { + // noop + } + + public override void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true) { // noop } diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 8e897011d2..90c5e0eb02 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -12,8 +12,10 @@ using System.Reflection; using System.Threading; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; @@ -31,6 +33,7 @@ using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Extensions; using File = System.IO.File; diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index 635a17a2b1..b91a034420 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -150,6 +150,7 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest .AddConfiguration() .AddUmbracoCore() .AddWebComponents() + .AddNuCache() .AddRuntimeMinifier() .AddBackOfficeCore() .AddBackOfficeAuthentication() diff --git a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs index 277510fc9e..c0b490e0e5 100644 --- a/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs +++ b/src/Umbraco.Tests.Integration/Testing/IntegrationTestComponent.cs @@ -2,7 +2,7 @@ // See LICENSE for more details. using Examine; -using Examine.LuceneEngine.Providers; +using Examine.Lucene.Providers; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Tests.Integration.Testing @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing { if (index is LuceneIndex luceneIndex) { - luceneIndex.ProcessNonAsync(); + luceneIndex.WithThreadingMode(IndexThreadingMode.Synchronous); } } } diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 37ed0e36de..9629375553 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -142,12 +142,13 @@ namespace Umbraco.Cms.Tests.Integration.Testing Log.Logger = new LoggerConfiguration() .WriteTo.File(path, rollingInterval: RollingInterval.Day) + .MinimumLevel.Debug() .CreateLogger(); builder.AddSerilog(Log.Logger); }); case UmbracoTestOptions.Logger.Console: - return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => builder.AddConsole()); + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); } } catch diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs index 358a5fb350..ce361e59b6 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs @@ -19,52 +19,35 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO public class FileSystemsTests : UmbracoIntegrationTest { [Test] - public void Can_Get_MediaFileSystem() + public void Can_Get_MediaFileManager() { - IMediaFileSystem fileSystem = GetRequiredService(); + MediaFileManager fileSystem = GetRequiredService(); Assert.NotNull(fileSystem); } [Test] - public void Can_Get_IMediaFileSystem() + public void MediaFileManager_Is_Singleton() { - IMediaFileSystem fileSystem = GetRequiredService(); - Assert.NotNull(fileSystem); - } - - [Test] - public void IMediaFileSystem_Is_Singleton() - { - IMediaFileSystem fileSystem1 = GetRequiredService(); - IMediaFileSystem fileSystem2 = GetRequiredService(); - Assert.AreSame(fileSystem1, fileSystem2); - } - - [Test] - public void Can_Unwrap_MediaFileSystem() - { - IMediaFileSystem fileSystem = GetRequiredService(); - IFileSystem unwrapped = fileSystem.Unwrap(); - Assert.IsNotNull(unwrapped); - var physical = unwrapped as PhysicalFileSystem; - Assert.IsNotNull(physical); + MediaFileManager fileManager1 = GetRequiredService(); + MediaFileManager fileManager2 = GetRequiredService(); + Assert.AreSame(fileManager1, fileManager2); } [Test] public void Can_Delete_MediaFiles() { - IMediaFileSystem fs = GetRequiredService(); - var ms = new MemoryStream(Encoding.UTF8.GetBytes("test")); - string virtPath = fs.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); - fs.AddFile(virtPath, ms); + MediaFileManager mediaFileManager = GetRequiredService(); + var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("test")); + string virtualPath = mediaFileManager.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); + mediaFileManager.FileSystem.AddFile(virtualPath, memoryStream); // ~/media/1234/file.txt exists IHostingEnvironment hostingEnvironment = GetRequiredService(); - string physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtPath)); + string physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtualPath)); Assert.IsTrue(File.Exists(physPath)); // ~/media/1234/file.txt is gone - fs.DeleteMediaFiles(new[] { virtPath }); + mediaFileManager.DeleteMediaFiles(new[] { virtualPath }); Assert.IsFalse(File.Exists(physPath)); IMediaPathScheme scheme = GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs index 276d7a267e..751430a824 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs @@ -35,14 +35,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO public void SetUp() { ClearFiles(HostingEnvironment); - FileSystems.ResetShadowId(); } [TearDown] public void TearDown() { ClearFiles(HostingEnvironment); - FileSystems.ResetShadowId(); } private void ClearFiles(IHostingEnvironment hostingEnvironment) @@ -383,13 +381,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO Assert.IsFalse(File.Exists(path + "/ShadowTests/sub/sub/f2.txt")); } - class FS : FileSystemWrapper - { - public FS(IFileSystem innerFileSystem) - : base(innerFileSystem) - { } - } - [Test] public void ShadowScopeComplete() { @@ -403,11 +394,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), loggerFactory, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem(phy); - var sw = (ShadowWrapper) fs.InnerFileSystem; + var fileSystems = new FileSystems(loggerFactory, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = (ShadowWrapper)fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); @@ -439,8 +429,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var typedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/x")); Assert.IsNotNull(typedDir); dirs = Directory.GetDirectories(typedDir); - var suid = fileSystems.Paths[typeof(FS)]; - var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/" + suid)); // this is where files go + var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/" + shadowPath)); // this is where files go Assert.IsNotNull(scopedDir); scope.Dispose(); scopedFileSystems = false; @@ -496,11 +485,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem( phy); - var sw = (ShadowWrapper) fs.InnerFileSystem; + var fileSystems = new FileSystems(NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); @@ -548,11 +536,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem( phy); - var sw = (ShadowWrapper)fs.InnerFileSystem; + var fileSystems = new FileSystems(NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs index 82dca80285..405f2fd838 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/PackageDataInstallationTests.cs @@ -41,8 +41,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging [DataEditor("7e062c13-7c41-4ad9-b389-41d88aeef87c", "Editor1", "editor1")] public class Editor1 : DataEditor { - public Editor1(ILoggerFactory loggerFactory) - : base(loggerFactory, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), new JsonNetSerializer()) + public Editor1(IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } } @@ -51,8 +51,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging [DataEditor("d15e1281-e456-4b24-aa86-1dda3e4299d5", "Editor2", "editor2")] public class Editor2 : DataEditor { - public Editor2(ILoggerFactory loggerFactory) - : base(loggerFactory, Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), new JsonNetSerializer()) + public Editor2(IDataValueEditorFactory dataValueEditorFactory) + : base(dataValueEditorFactory) { } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs new file mode 100644 index 0000000000..a2a82d57fb --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineBaseTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Data; +using Examine.Lucene.Providers; +using Examine.Search; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NPoco; +using NUnit.Framework; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Querying; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Examine; +using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; +using Umbraco.Cms.Tests.Integration.Testing; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine +{ + [TestFixture] + public abstract class ExamineBaseTest : UmbracoIntegrationTest + { + protected IndexInitializer IndexInitializer => Services.GetRequiredService(); + + protected IHostingEnvironment HostingEnvironment => Services.GetRequiredService(); + + protected IRuntimeState RunningRuntimeState { get; } = Mock.Of(x => x.Level == RuntimeLevel.Run); + + public override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + services.AddSingleton(); + } + + /// + /// Used to create and manage a testable index + /// + /// + /// + /// + /// + /// + /// + protected IDisposable GetSynchronousContentIndex( + bool publishedValuesOnly, + out UmbracoContentIndex index, + out ContentIndexPopulator contentRebuilder, + out ContentValueSetBuilder contentValueSetBuilder, + int? parentId = null, + IContentService contentService = null) + { + contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(publishedValuesOnly); + + ISqlContext sqlContext = Mock.Of(x => x.Query() == Mock.Of>()); + IUmbracoDatabaseFactory dbFactory = Mock.Of(x => x.SqlContext == sqlContext); + + if (contentService == null) + { + contentService = IndexInitializer.GetMockContentService(); + } + + contentRebuilder = IndexInitializer.GetContentIndexRebuilder(contentService, publishedValuesOnly, dbFactory); + + var luceneDir = new RandomIdRAMDirectory(); + + ContentValueSetValidator validator; + + // if only published values then we'll change the validator for tests to + // ensure we don't support protected nodes and that we + // mock the public access service for the special protected node. + if (publishedValuesOnly) + { + var publicAccessServiceMock = new Mock(); + publicAccessServiceMock.Setup(x => x.IsProtected(It.IsAny())) + .Returns((string path) => + { + if (path.EndsWith("," + ExamineDemoDataContentService.ProtectedNode)) + { + return Attempt.Succeed(); + } + return Attempt.Fail(); + }); + + var scopeProviderMock = new Mock(); + scopeProviderMock.Setup(x => x.CreateScope( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Mock.Of); + + validator = new ContentValueSetValidator( + publishedValuesOnly, + false, + publicAccessServiceMock.Object, + scopeProviderMock.Object, + parentId); + } + else + { + validator = new ContentValueSetValidator(publishedValuesOnly, parentId); + } + + index = IndexInitializer.GetUmbracoIndexer( + HostingEnvironment, + RunningRuntimeState, + luceneDir, + validator: validator); + + IDisposable syncMode = index.WithThreadingMode(IndexThreadingMode.Synchronous); + + return new DisposableWrapper(syncMode, index, luceneDir); + } + + private class DisposableWrapper : IDisposable + { + private readonly IDisposable[] _disposables; + + public DisposableWrapper(params IDisposable[] disposables) => _disposables = disposables; + + public void Dispose() + { + foreach (IDisposable d in _disposables) + { + d.Dispose(); + } + } + } + } +} diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataContentService.cs similarity index 94% rename from src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataContentService.cs index ca11680f68..8323acf9bf 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataContentService.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataContentService.cs @@ -1,7 +1,7 @@ -using System.Xml.Linq; +using System.Xml.Linq; using System.Xml.XPath; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { // TODO: This is ultra hack and still left over from legacy but still works for testing atm public class ExamineDemoDataContentService diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataMediaService.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataMediaService.cs similarity index 88% rename from src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataMediaService.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataMediaService.cs index 035a31b240..a7172248db 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineDemoDataMediaService.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineDemoDataMediaService.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; using System.Xml.XPath; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { // TODO: This is ultra hack and still left over from legacy but still works for testing atm internal class ExamineDemoDataMediaService diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineExtensions.cs similarity index 98% rename from src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineExtensions.cs index 9cca58719e..ee8aea385f 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineExtensions.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/ExamineExtensions.cs @@ -1,11 +1,11 @@ -using Examine; +using Examine; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Xml.Linq; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { /// /// LEGACY!! Static methods to help query umbraco xml diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs similarity index 53% rename from src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs index 6dfc0a39ce..b7aa9fafe1 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs @@ -1,11 +1,14 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; +using Examine.Lucene; +using Examine.Lucene.Directories; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; using Lucene.Net.Store; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Moq; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; @@ -14,48 +17,74 @@ using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Tests.TestHelpers; using IContentService = Umbraco.Cms.Core.Services.IContentService; using IMediaService = Umbraco.Cms.Core.Services.IMediaService; -using Version = Lucene.Net.Util.Version; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { /// /// Used internally by test classes to initialize a new index from the template /// - internal static class IndexInitializer + public class IndexInitializer { - public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, IScopeProvider scopeProvider, bool publishedValuesOnly) + private readonly IShortStringHelper _shortStringHelper; + private readonly IJsonSerializer _jsonSerializer; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IScopeProvider _scopeProvider; + private readonly ILoggerFactory _loggerFactory; + + public IndexInitializer( + IShortStringHelper shortStringHelper, + IJsonSerializer jsonSerializer, + PropertyEditorCollection propertyEditors, + IScopeProvider scopeProvider, + ILoggerFactory loggerFactory) + { + _shortStringHelper = shortStringHelper; + _jsonSerializer = jsonSerializer; + _propertyEditors = propertyEditors; + _scopeProvider = scopeProvider; + _loggerFactory = loggerFactory; + } + + public ContentValueSetBuilder GetContentValueSetBuilder(bool publishedValuesOnly) { var contentValueSetBuilder = new ContentValueSetBuilder( - propertyEditors, - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), + _propertyEditors, + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), GetMockUserService(), - TestHelper.ShortStringHelper, - scopeProvider, + _shortStringHelper, + _scopeProvider, publishedValuesOnly); return contentValueSetBuilder; } - public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, IScopeProvider scopeProvider, IUmbracoDatabaseFactory umbracoDatabaseFactory, bool publishedValuesOnly) + public ContentIndexPopulator GetContentIndexRebuilder(IContentService contentService, bool publishedValuesOnly, IUmbracoDatabaseFactory umbracoDatabaseFactory) { - var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors, scopeProvider, publishedValuesOnly); - var contentIndexDataSource = new ContentIndexPopulator(publishedValuesOnly, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder); + var contentValueSetBuilder = GetContentValueSetBuilder(publishedValuesOnly); + var contentIndexDataSource = new ContentIndexPopulator( + _loggerFactory.CreateLogger(), + publishedValuesOnly, + null, + contentService, + umbracoDatabaseFactory, + contentValueSetBuilder); return contentIndexDataSource; } - public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) + public MediaIndexPopulator GetMediaIndexRebuilder(IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), GetMockUserService(), Mock.Of>(), TestHelper.ShortStringHelper, TestHelper.JsonSerializer); + var mediaValueSetBuilder = new MediaValueSetBuilder(_propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), GetMockUserService(), Mock.Of>(), _shortStringHelper, _jsonSerializer); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } + public static IContentService GetMockContentService() { long longTotalRecs; @@ -64,23 +93,25 @@ namespace Umbraco.Tests.UmbracoExamine var allRecs = demoData.GetLatestContentByXPath("//*[@isDoc]") .Root .Elements() - .Select(x => Mock.Of( + .Select((xmlElement, index) => Mock.Of( m => - m.Id == (int)x.Attribute("id") && - m.ParentId == (int)x.Attribute("parentID") && - m.Level == (int)x.Attribute("level") && + m.Id == (int)xmlElement.Attribute("id") && + // have every second one published and include the special one + m.Published == ((ExamineDemoDataContentService.ProtectedNode == (int)xmlElement.Attribute("id")) || (index % 2 == 0 ? true : false)) && + m.ParentId == (int)xmlElement.Attribute("parentID") && + m.Level == (int)xmlElement.Attribute("level") && m.CreatorId == 0 && - m.SortOrder == (int)x.Attribute("sortOrder") && - m.CreateDate == (DateTime)x.Attribute("createDate") && - m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && - m.GetCultureName(It.IsAny()) == (string)x.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && - m.Path == (string)x.Attribute("path") && + m.SortOrder == (int)xmlElement.Attribute("sortOrder") && + m.CreateDate == (DateTime)xmlElement.Attribute("createDate") && + m.UpdateDate == (DateTime)xmlElement.Attribute("updateDate") && + m.Name == (string)xmlElement.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.GetCultureName(It.IsAny()) == (string)xmlElement.Attribute(UmbracoExamineFieldNames.NodeNameFieldName) && + m.Path == (string)xmlElement.Attribute("path") && m.Properties == new PropertyCollection() && m.ContentType == Mock.Of(mt => mt.Icon == "test" && - mt.Alias == x.Name.LocalName && - mt.Id == (int)x.Attribute("nodeType")))) + mt.Alias == xmlElement.Name.LocalName && + mt.Id == (int)xmlElement.Attribute("nodeType")))) .ToArray(); @@ -90,10 +121,7 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); } - public static IUserService GetMockUserService() - { - return Mock.Of(x => x.GetProfileById(It.IsAny()) == Mock.Of(p => p.Id == 0 && p.Name == "admin")); - } + public IUserService GetMockUserService() => Mock.Of(x => x.GetProfileById(It.IsAny()) == Mock.Of(p => p.Id == 0 && p.Name == "admin")); public static IMediaService GetMockMediaService() { @@ -136,30 +164,23 @@ namespace Umbraco.Tests.UmbracoExamine return mediaServiceMock.Object; } - public static ILocalizationService GetMockLocalizationService() - { - return Mock.Of(x => x.GetAllLanguages() == Array.Empty()); - } + public ILocalizationService GetMockLocalizationService() => Mock.Of(x => x.GetAllLanguages() == Array.Empty()); - public static IMediaTypeService GetMockMediaTypeService() + public static IMediaTypeService GetMockMediaTypeService(IShortStringHelper shortStringHelper) { var mediaTypeServiceMock = new Mock(); mediaTypeServiceMock.Setup(x => x.GetAll()) .Returns(new List { - new MediaType(TestHelper.ShortStringHelper, -1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, - new MediaType(TestHelper.ShortStringHelper, -1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} + new MediaType(shortStringHelper, -1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, + new MediaType(shortStringHelper, -1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} }); return mediaTypeServiceMock.Object; } - public static IProfilingLogger GetMockProfilingLogger() - { - return new ProfilingLogger(Mock.Of>(), Mock.Of()); - } + public IProfilingLogger GetMockProfilingLogger() => new ProfilingLogger(Mock.Of>(), Mock.Of()); - public static UmbracoContentIndex GetUmbracoIndexer( - IProfilingLogger profilingLogger, + public UmbracoContentIndex GetUmbracoIndexer( IHostingEnvironment hostingEnvironment, IRuntimeState runtimeState, Directory luceneDir, @@ -171,40 +192,50 @@ namespace Umbraco.Tests.UmbracoExamine languageService = GetMockLocalizationService(); if (analyzer == null) - analyzer = new StandardAnalyzer(Version.LUCENE_30); + analyzer = new StandardAnalyzer(LuceneInfo.CurrentVersion); if (validator == null) validator = new ContentValueSetValidator(true); - var i = new UmbracoContentIndex( + var options = GetOptions( "testIndexer", - luceneDir, - new UmbracoFieldDefinitionCollection(), - analyzer, - profilingLogger, - Mock.Of>(), - Mock.Of(), + new LuceneDirectoryIndexOptions + { + Analyzer = analyzer, + Validator = validator, + DirectoryFactory = new GenericDirectoryFactory(s => luceneDir), + FieldDefinitions = new UmbracoFieldDefinitionCollection() + }); + + var i = new UmbracoContentIndex( + _loggerFactory, + "testIndexer", + options, hostingEnvironment, runtimeState, - languageService, - validator); + languageService); i.IndexingError += IndexingError; + i.IndexOperationComplete += I_IndexOperationComplete; return i; } + private void I_IndexOperationComplete(object sender, IndexOperationEventArgs e) + { + + } + //public static MultiIndexSearcher GetMultiSearcher(Directory pdfDir, Directory simpleDir, Directory conventionDir, Directory cwsDir) //{ // var i = new MultiIndexSearcher("testSearcher", new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Version.LUCENE_29)); // return i; //} + public static IOptionsSnapshot GetOptions(string indexName, LuceneDirectoryIndexOptions options) + => Mock.Of>(x => x.Get(indexName) == options); - internal static void IndexingError(object sender, IndexingErrorEventArgs e) - { - throw new ApplicationException(e.Message, e.InnerException); - } + internal void IndexingError(object sender, IndexingErrorEventArgs e) => throw new ApplicationException(e.Message, e.Exception); } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexTest.cs similarity index 54% rename from src/Umbraco.Tests/UmbracoExamine/IndexTest.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexTest.cs index 3daf185cd4..f6362a8156 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexTest.cs @@ -1,52 +1,67 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Examine; -using Examine.LuceneEngine.Providers; -using Lucene.Net.Index; -using Lucene.Net.Search; -using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Infrastructure.Examine; +using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { + /// /// Tests the standard indexing capabilities /// [TestFixture] - [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + [UmbracoTest(Database = UmbracoTestOptions.Database.None)] public class IndexTest : ExamineBaseTest { [Test] - public void Index_Property_Data_With_Value_Indexer() + public void GivenValidationParentNode_WhenContentIndexedUnderDifferentParent_DocumentIsNotIndexed() { - var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Factory.GetRequiredService(), ScopeProvider, false); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out _, out _, 999)) { - indexer.CreateIndex(); + var searcher = index.Searcher; - var contentType = MockedContentTypes.CreateBasicContentType(); + var contentService = new ExamineDemoDataContentService(); + //get a node from the data repo + var node = contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]") + .Root + .Elements() + .First(); + + ValueSet valueSet = node.ConvertToValueSet(IndexTypes.Content); + + // Ignored since the path isn't under 999 + index.IndexItems(new[] { valueSet }); + Assert.AreEqual(0, searcher.CreateQuery().Id(valueSet.Id).Execute().TotalItemCount); + + // Change so that it's under 999 and verify + valueSet.Values["path"] = new List { "-1,999," + valueSet.Id }; + index.IndexItems(new[] { valueSet }); + Assert.AreEqual(1, searcher.CreateQuery().Id(valueSet.Id).Execute().TotalItemCount); + } + } + + [Test] + public void GivenIndexingDocument_WhenGridPropertyData_ThenDataIndexedInSegregatedFields() + { + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out _, out ContentValueSetBuilder contentValueSetBuilder, null)) + { + index.CreateIndex(); + + ContentType contentType = ContentTypeBuilder.CreateBasicContentType(); contentType.AddPropertyType(new PropertyType(TestHelper.ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "grid", Name = "Grid", PropertyEditorAlias = Cms.Core.Constants.PropertyEditors.Aliases.Grid }); - var content = MockedContent.CreateBasicContent(contentType); + Content content = ContentBuilder.CreateBasicContent(contentType); content.Id = 555; content.Path = "-1,555"; var gridVal = new GridValue @@ -100,15 +115,13 @@ namespace Umbraco.Tests.UmbracoExamine var json = JsonConvert.SerializeObject(gridVal); content.Properties["grid"].SetValue(json); - var valueSet = contentValueSetBuilder.GetValueSets(content); - indexer.IndexItems(valueSet); + IEnumerable valueSet = contentValueSetBuilder.GetValueSets(content); + index.IndexItems(valueSet); - var searcher = indexer.GetSearcher(); - - var results = searcher.CreateQuery().Id(555).Execute(); + ISearchResults results = index.Searcher.CreateQuery().Id(555).Execute(); Assert.AreEqual(1, results.TotalItemCount); - var result = results.First(); + ISearchResult result = results.First(); Assert.IsTrue(result.Values.ContainsKey("grid.row1")); Assert.AreEqual("value1", result.AllValues["grid.row1"][0]); Assert.AreEqual("value2", result.AllValues["grid.row1"][1]); @@ -120,95 +133,64 @@ namespace Umbraco.Tests.UmbracoExamine } [Test] - public void Rebuild_Index() - { - var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); + public void GivenEmptyIndex_WhenUsingWithContentAndMediaPopulators_ThenIndexPopulated() + { + var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(IndexInitializer.GetMockMediaService()); - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { - - var searcher = indexer.GetSearcher(); - //create the whole thing - contentRebuilder.Populate(indexer); - mediaRebuilder.Populate(indexer); + contentRebuilder.Populate(index); + mediaRebuilder.Populate(index); - var result = searcher.CreateQuery().All().Execute(); + var result = index.Searcher.CreateQuery().All().Execute(); Assert.AreEqual(29, result.TotalItemCount); } } - ///// /// /// Check that the node signalled as protected in the content service is not present in the index. /// [Test] - public void Index_Protected_Content_Not_Indexed() + public void GivenPublishedContentIndex_WhenProtectedContentIndexed_ThenItIsIgnored() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir)) - using (indexer.ProcessNonAsync()) - using (var searcher = ((LuceneSearcher)indexer.GetSearcher()).GetLuceneSearcher()) + using (GetSynchronousContentIndex(true, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { //create the whole thing - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); + Assert.Greater( + index.Searcher.CreateQuery().All().Execute().TotalItemCount, + 0); - var protectedQuery = new BooleanQuery(); - protectedQuery.Add( - new BooleanClause( - new TermQuery(new Term(ExamineFieldNames.CategoryFieldName, IndexTypes.Content)), - Occur.MUST)); - - protectedQuery.Add( - new BooleanClause( - new TermQuery(new Term(ExamineFieldNames.ItemIdFieldName, ExamineDemoDataContentService.ProtectedNode.ToString())), - Occur.MUST)); - - var collector = TopScoreDocCollector.Create(100, true); - - searcher.Search(protectedQuery, collector); - - Assert.AreEqual(0, collector.TotalHits, "Protected node should not be indexed"); + Assert.AreEqual( + 0, + index.Searcher.CreateQuery().Id(ExamineDemoDataContentService.ProtectedNode.ToString()).Execute().TotalItemCount); } - } [Test] - public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() + public void GivenMediaUnderNonIndexableParent_WhenMediaMovedUnderIndexableParent_ThenItIsIncludedInTheIndex() { // create a validator with // publishedValuesOnly false - // parentId 1116 (only content under that parent will be indexed) - var validator = new ContentValueSetValidator(false, 1116); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, validator: validator)) - using (indexer.ProcessNonAsync()) + // parentId 1116 (only content under that parent will be indexed) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, 1116)) { - var searcher = indexer.GetSearcher(); - //get a node from the data repo (this one exists underneath 2222) var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") .Root.Elements() - .First(x => (int) x.Attribute("id") == 2112); + .First(x => (int)x.Attribute("id") == 2112); var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 Assert.AreEqual("-1,1111,2222,2112", currPath); //ensure it's indexed - indexer.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); + index.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will not exist because it exists under 2222 - var results = searcher.CreateQuery().Id(2112).Execute(); + var results = index.Searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(0, results.Count()); //now mimic moving 2112 to 1116 @@ -217,38 +199,34 @@ namespace Umbraco.Tests.UmbracoExamine node.SetAttributeValue("parentID", "1116"); //now reindex the node, this should first delete it and then WILL add it because of the parent id constraint - indexer.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); + index.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); //now ensure it exists - results = searcher.CreateQuery().Id(2112).Execute(); + results = index.Searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(1, results.Count()); } } [Test] - public void Index_Move_Media_To_Non_Indexable_ParentID() + public void GivenMediaUnderIndexableParent_WhenMediaMovedUnderNonIndexableParent_ThenItIsRemovedFromTheIndex() { // create a validator with // publishedValuesOnly false // parentId 2222 (only content under that parent will be indexed) - var validator = new ContentValueSetValidator(false, 2222); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, validator: validator)) - using (indexer1.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, 2222)) { - var searcher = indexer1.GetSearcher(); + var searcher = index.Searcher; //get a node from the data repo (this one exists underneath 2222) var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") .Root.Elements() - .First(x => (int) x.Attribute("id") == 2112); + .First(x => (int)x.Attribute("id") == 2112); var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 Assert.AreEqual("-1,1111,2222,2112", currPath); //ensure it's indexed - indexer1.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); + index.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will exist because it exists under 2222 var results = searcher.CreateQuery().Id(2112).Execute(); @@ -259,7 +237,7 @@ namespace Umbraco.Tests.UmbracoExamine node.SetAttributeValue("parentID", "1116"); //now reindex the node, this should first delete it and then NOT add it because of the parent id constraint - indexer1.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); + index.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); //now ensure it's deleted results = searcher.CreateQuery().Id(2112).Execute(); @@ -273,38 +251,34 @@ namespace Umbraco.Tests.UmbracoExamine /// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly. /// [Test] - public void Index_Reindex_Content() + public void GivenEmptyIndex_WhenIndexedWithContentPopulator_ThenTheIndexIsPopulated() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { - - var searcher = indexer.GetSearcher(); - //create the whole thing - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); - var result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); + var result = index.Searcher + .CreateQuery() + .Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content) + .Execute(); Assert.AreEqual(21, result.TotalItemCount); //delete all content - foreach (var r in result) - { - indexer.DeleteFromIndex(r.Id); - } - + index.DeleteFromIndex(result.Select(x => x.Id)); //ensure it's all gone - result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); + result = index.Searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(0, result.TotalItemCount); //call our indexing methods - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); + + result = index.Searcher + .CreateQuery() + .Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content) + .Execute(); - result = searcher.CreateQuery().Field(ExamineFieldNames.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(21, result.TotalItemCount); } } @@ -313,26 +287,24 @@ namespace Umbraco.Tests.UmbracoExamine /// This will delete an item from the index and ensure that all children of the node are deleted too! /// [Test] - public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() + public void GivenPopulatedIndex_WhenDocumentDeleted_ThenItsHierarchyIsAlsoDeleted() { - - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockContentService(), ScopeProvider, UmbracoDatabaseFactory,false); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir)) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null)) { - var searcher = indexer.GetSearcher(); + var searcher = index.Searcher; //create the whole thing - rebuilder.Populate(indexer); + contentRebuilder.Populate(index); + + var results = searcher.CreateQuery().Id(1141).Execute(); + Assert.AreEqual(1, results.Count()); //now delete a node that has children - indexer.DeleteFromIndex(1140.ToString()); + index.DeleteFromIndex(1140.ToString()); //this node had children: 1141 & 1142, let's ensure they are also removed - var results = searcher.CreateQuery().Id(1141).Execute(); + results = searcher.CreateQuery().Id(1141).Execute(); Assert.AreEqual(0, results.Count()); results = searcher.CreateQuery().Id(1142).Execute(); diff --git a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/PublishedContentQueryTests.cs similarity index 62% rename from src/Umbraco.Tests/Web/PublishedContentQueryTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/PublishedContentQueryTests.cs index cc34cd4aba..f2269916a4 100644 --- a/src/Umbraco.Tests/Web/PublishedContentQueryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/PublishedContentQueryTests.cs @@ -1,33 +1,43 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; -using Examine.LuceneEngine.Providers; +using Examine.Lucene; +using Examine.Lucene.Directories; +using Examine.Lucene.Providers; using Lucene.Net.Store; +using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure; using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; +using Umbraco.Cms.Tests.Common.Testing; -namespace Umbraco.Tests.Web +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { [TestFixture] - public class PublishedContentQueryTests + [UmbracoTest(Database = UmbracoTestOptions.Database.None)] + public class PublishedContentQueryTests : ExamineBaseTest { private class TestIndex : LuceneIndex, IUmbracoIndex { private readonly string[] _fieldNames; - public TestIndex(string name, Directory luceneDirectory, string[] fieldNames) - : base(name, luceneDirectory, null, null, null, null) + public TestIndex(ILoggerFactory loggerFactory, string name, Directory luceneDirectory, string[] fieldNames) + : base( + loggerFactory, + name, + IndexInitializer.GetOptions(name, new LuceneDirectoryIndexOptions + { + DirectoryFactory = new GenericDirectoryFactory(s => luceneDirectory) + })) { _fieldNames = fieldNames; } + public bool EnableDefaultEventHandler => throw new NotImplementedException(); public bool PublishedValuesOnly => throw new NotImplementedException(); public IEnumerable GetFields() => _fieldNames; @@ -35,29 +45,29 @@ namespace Umbraco.Tests.Web private TestIndex CreateTestIndex(Directory luceneDirectory, string[] fieldNames) { - var indexer = new TestIndex("TestIndex", luceneDirectory, fieldNames); + var index = new TestIndex(LoggerFactory, "TestIndex", luceneDirectory, fieldNames); - using (indexer.ProcessNonAsync()) + using (index.WithThreadingMode(IndexThreadingMode.Synchronous)) { //populate with some test data - indexer.IndexItem(new ValueSet("1", "content", new Dictionary + index.IndexItem(new ValueSet("1", "content", new Dictionary { [fieldNames[0]] = "Hello world, there are products here", [UmbracoExamineFieldNames.VariesByCultureFieldName] = "n" })); - indexer.IndexItem(new ValueSet("2", "content", new Dictionary + index.IndexItem(new ValueSet("2", "content", new Dictionary { [fieldNames[1]] = "Hello world, there are products here", [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y" })); - indexer.IndexItem(new ValueSet("3", "content", new Dictionary + index.IndexItem(new ValueSet("3", "content", new Dictionary { [fieldNames[2]] = "Hello world, there are products here", [UmbracoExamineFieldNames.VariesByCultureFieldName] = "y" })); } - return indexer; + return index; } private PublishedContentQuery CreatePublishedContentQuery(IIndex indexer) @@ -75,10 +85,10 @@ namespace Umbraco.Tests.Web return new PublishedContentQuery(snapshot, variationContextAccessor, examineManager.Object); } - [TestCase("fr-fr", ExpectedResult = "1, 3", TestName = "Search Culture: fr-fr. Must return both fr-fr and invariant results")] - [TestCase("en-us", ExpectedResult = "1, 2", TestName = "Search Culture: en-us. Must return both en-us and invariant results")] - [TestCase("*", ExpectedResult = "1, 2, 3", TestName = "Search Culture: *. Must return all cultures and all invariant results")] - [TestCase(null, ExpectedResult = "1", TestName = "Search Culture: null. Must return only invariant results")] + [TestCase("fr-fr", ExpectedResult = "1, 3", Description = "Search Culture: fr-fr. Must return both fr-fr and invariant results")] + [TestCase("en-us", ExpectedResult = "1, 2", Description = "Search Culture: en-us. Must return both en-us and invariant results")] + [TestCase("*", ExpectedResult = "1, 2, 3", Description = "Search Culture: *. Must return all cultures and all invariant results")] + [TestCase(null, ExpectedResult = "1", Description = "Search Culture: null. Must return only invariant results")] public string Search(string culture) { using (var luceneDir = new RandomIdRAMDirectory()) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs new file mode 100644 index 0000000000..3d8fc1f192 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/RandomIdRAMDirectory.cs @@ -0,0 +1,11 @@ +using System; +using Lucene.Net.Store; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine +{ + public class RandomIdRAMDirectory : RAMDirectory + { + private readonly string _lockId = Guid.NewGuid().ToString(); + public override string GetLockID() => _lockId; + } +} diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/SearchTests.cs similarity index 69% rename from src/Umbraco.Tests/UmbracoExamine/SearchTests.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/SearchTests.cs index c4698fcdf2..2aefc593db 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/SearchTests.cs @@ -1,23 +1,22 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; +using Examine.Lucene.Providers; using Examine.Search; -using Microsoft.Extensions.DependencyInjection; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Querying; -using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Extensions; -namespace Umbraco.Tests.UmbracoExamine +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] public class SearchTests : ExamineBaseTest { @@ -54,17 +53,14 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); - var propertyEditors = Factory.GetRequiredService(); - var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider, UmbracoDatabaseFactory,true); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir)) - using (indexer.ProcessNonAsync()) + using (GetSynchronousContentIndex(false, out UmbracoContentIndex index, out ContentIndexPopulator contentRebuilder, out _, null, contentService)) { - indexer.CreateIndex(); - rebuilder.Populate(indexer); + index.CreateIndex(); + contentRebuilder.Populate(index); - var searcher = indexer.GetSearcher(); + var searcher = index.Searcher; + + Assert.Greater(searcher.CreateQuery().All().Execute().TotalItemCount, 0); var numberSortedCriteria = searcher.CreateQuery() .ParentId(1148) @@ -99,23 +95,5 @@ namespace Umbraco.Tests.UmbracoExamine return true; } - //[Test] - //public void Test_Index_Type_With_German_Analyzer() - //{ - // using (var luceneDir = new RandomIdRamDirectory()) - // { - // var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, - // new GermanAnalyzer()); - // indexer.RebuildIndex(); - // var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); - // } - //} - - //private readonly TestContentService _contentService = new TestContentService(); - //private readonly TestMediaService _mediaService = new TestMediaService(); - //private static UmbracoExamineSearcher _searcher; - //private static UmbracoContentIndexer _indexer; - //private Lucene.Net.Store.Directory _luceneDir; - } } diff --git a/src/Umbraco.Tests/UmbracoExamine/TestFiles.Designer.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.Designer.cs similarity index 93% rename from src/Umbraco.Tests/UmbracoExamine/TestFiles.Designer.cs rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.Designer.cs index b60dc487de..166d329208 100644 --- a/src/Umbraco.Tests/UmbracoExamine/TestFiles.Designer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Umbraco.Tests.UmbracoExamine { +namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { using System; @@ -19,7 +19,7 @@ namespace Umbraco.Tests.UmbracoExamine { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class TestFiles { @@ -39,7 +39,7 @@ namespace Umbraco.Tests.UmbracoExamine { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Tests.UmbracoExamine.TestFiles", typeof(TestFiles).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine.TestFiles", typeof(TestFiles).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Umbraco.Tests/UmbracoExamine/TestFiles.resx b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.resx similarity index 96% rename from src/Umbraco.Tests/UmbracoExamine/TestFiles.resx rename to src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.resx index e23540252a..b5ed853136 100644 --- a/src/Umbraco.Tests/UmbracoExamine/TestFiles.resx +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/TestFiles.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less new file mode 100644 index 0000000000..1de962f7e1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.less @@ -0,0 +1,122 @@ +.umb-media-entry-editor { + + .umb-cropper-imageholder { + position: relative; + width: 100%; + height: 100%; + } + .umb-cropper-gravity { + height: 100%; + } + .umb-cropper__container { + width: 100%; + height: 100%; + } + .umb-cropper { + height: 100%; + } + .umb-cropper .crop-container { + padding-bottom: 0; + height: calc(100% - 50px) + } + .umb-cropper .crop-controls-wrapper { + justify-content: center; + } + .umb-cropper .crop-slider-wrapper { + max-width: 500px; + } +} + +.umb-media-entry-editor__pane { + display: flex; + flex-flow: row-reverse; + height: 100%; + width: 100%; +} + +.umb-media-entry-editor__crops { + background-color: white; + overflow: auto; + + > button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + text-align: center; + padding: 4px 10px 0 10px; + border-bottom: 1px solid @gray-9; + box-sizing: border-box; + height: 120px; + width: 120px; + color: @ui-active-type; + + &:hover { + color: @ui-active-type-hover; + text-decoration: none; + } + + &:active { + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + &::before { + content: ""; + position: absolute; + width: 0px; + max-height: 50px; + height: (100% - 16px); + top: auto; + bottom: auto; + background-color: @ui-light-active-border; + left: 0; + border-radius: 0 3px 3px 0; + opacity: 0; + transition: all .2s linear; + } + + &.--is-active { + color: @ui-light-active-type; + + &::before { + opacity: 1; + width: 4px; + } + } + &.--is-defined { + + } + + > .__icon { + font-size: 24px; + display: block; + text-align: center; + margin-bottom: 7px; + } + + > .__text { + font-size: 12px; + line-height: 1em; + margin-top: 4px; + } + } +} + +.umb-media-entry-editor__imagecropper { + flex: auto; + height: 100%; +} + +.umb-media-entry-editor__imageholder { + display: block; + position: relative; + height: calc(100% - 50px); +} +.umb-media-entry-editor__imageholder-actions { + background-color: white; + height: 50px; + display: flex; + justify-content: center; +} + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index fec2e632c5..029dedf214 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService, umbSessionStorage, notificationsService) { + function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService, umbSessionStorage, notificationsService, clipboardService) { var vm = this; @@ -19,6 +19,8 @@ angular.module("umbraco") vm.enterSubmitFolder = enterSubmitFolder; vm.focalPointChanged = focalPointChanged; vm.changePagination = changePagination; + vm.onNavigationChanged = onNavigationChanged; + vm.clickClearClipboard = clickClearClipboard; vm.clickHandler = clickHandler; vm.clickItemName = clickItemName; @@ -27,7 +29,10 @@ angular.module("umbraco") vm.selectLayout = selectLayout; vm.showMediaList = false; + vm.navigation = []; + var dialogOptions = $scope.model; + vm.clipboardItems = dialogOptions.clipboardItems; $scope.disableFolderSelect = (dialogOptions.disableFolderSelect && dialogOptions.disableFolderSelect !== "0") ? true : false; $scope.disableFocalPoint = (dialogOptions.disableFocalPoint && dialogOptions.disableFocalPoint !== "0") ? true : false; @@ -100,10 +105,32 @@ angular.module("umbraco") function setTitle() { if (!$scope.model.title) { - localizationService.localize("defaultdialogs_selectMedia") + localizationService.localizeMany(["defaultdialogs_selectMedia", "mediaPicker_tabClipboard"]) .then(function (data) { - $scope.model.title = data; + $scope.model.title = data[0]; + + + vm.navigation = [{ + "alias": "empty", + "name": data[0], + "icon": "icon-umb-media", + "active": true, + "view": "" + }]; + + if(vm.clipboardItems) { + vm.navigation.push({ + "alias": "clipboard", + "name": data[1], + "icon": "icon-paste-in", + "view": "", + "disabled": vm.clipboardItems.length === 0 + }); + } + + vm.activeTab = vm.navigation[0]; }); + } } @@ -149,7 +176,7 @@ angular.module("umbraco") .then(function (node) { $scope.target = node; // Moving directly to existing node's folder - gotoFolder({ id: node.parentId }).then(function() { + gotoFolder({ id: node.parentId }).then(function () { selectMedia(node); $scope.target.url = mediaHelper.resolveFileFromEntity(node); $scope.target.thumbnail = mediaHelper.resolveFileFromEntity(node, true); @@ -169,10 +196,10 @@ angular.module("umbraco") function upload(v) { var fileSelect = $(".umb-file-dropzone .file-select"); - if (fileSelect.length === 0){ + if (fileSelect.length === 0) { localizationService.localize('media_uploadNotAllowed').then(function (message) { notificationsService.warning(message); }); } - else{ + else { fileSelect.trigger("click"); } } @@ -395,6 +422,19 @@ angular.module("umbraco") }); }; + function onNavigationChanged(tab) { + vm.activeTab.active = false; + vm.activeTab = tab; + vm.activeTab.active = true; + }; + + function clickClearClipboard() { + vm.onNavigationChanged(vm.navigation[0]); + vm.navigation[1].disabled = true; + vm.clipboardItems = []; + dialogOptions.clickClearClipboard(); + }; + var debounceSearchMedia = _.debounce(function () { $scope.$apply(function () { if (vm.searchOptions.filter) { @@ -504,13 +544,7 @@ angular.module("umbraco") var allowedTypes = dialogOptions.filter ? dialogOptions.filter.split(",") : null; for (var i = 0; i < data.length; i++) { - if (data[i].metaData.MediaPath !== null) { - data[i].thumbnail = mediaHelper.resolveFileFromEntity(data[i], true); - data[i].image = mediaHelper.resolveFileFromEntity(data[i], false); - } - if (data[i].metaData.UpdateDate !== null){ - data[i].updateDate = data[i].metaData.UpdateDate; - } + setDefaultData(data[i]); data[i].filtered = allowedTypes && allowedTypes.indexOf(data[i].metaData.ContentTypeAlias) < 0; } @@ -523,6 +557,16 @@ angular.module("umbraco") }); } + function setDefaultData(item) { + if (item.metaData.MediaPath !== null) { + item.thumbnail = mediaHelper.resolveFileFromEntity(item, true); + item.image = mediaHelper.resolveFileFromEntity(item, false); + } + if (item.metaData.UpdateDate !== null) { + item.updateDate = item.metaData.UpdateDate; + } + } + function preSelectMedia() { for (var folderIndex = 0; folderIndex < $scope.images.length; folderIndex++) { var folderImage = $scope.images[folderIndex]; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index df0c8e3cef..d1f0699b13 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -3,13 +3,15 @@ - +
@@ -19,21 +21,20 @@
@@ -49,20 +50,20 @@
-
+
+ layouts="vm.layout.layouts" + active-layout="vm.layout.activeLayout" + on-layout-select="vm.selectLayout(layout)">
+ layouts="vm.layout.layouts" + active-layout="vm.layout.activeLayout" + on-layout-select="vm.selectLayout(layout)">
@@ -86,31 +87,29 @@ + class="umb-breadcrumbs__add-ancestor" + ng-show="model.showFolderInput" + ng-model="model.newFolderName" + ng-keydown="enterSubmitFolder($event)" + ng-blur="vm.submitFolder()" + focus-when="{{model.showFolderInput}}" />
- + - - +
- @@ -145,11 +142,30 @@
- - - + + + +
+ + +
+ +
+ + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.html b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.html index c1656c33ff..276cc2491a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card.html @@ -2,7 +2,13 @@
- +
+ + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html index 0fc84c7fb8..9dd90b6b57 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html @@ -8,7 +8,7 @@ class="umb-sub-views-nav-item__action umb-outline umb-outline--thin" aria-haspopup="{{ vm.item.anchors && vm.item.anchors.length > 1 }}" aria-expanded="{{ vm.expanded }}"> - + {{ vm.item.name }}
{{vm.item.badge.count}}
!
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html index 08858b058b..be196fb31f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation.html @@ -23,8 +23,7 @@ text="{{moreButton.name}}" show-text="true" mode="tab" - css-class="umb-sub-views-nav-item__action umb-outline umb-outline--thin {{moreButton.active ? 'is-active' : ''}}" - > + css-class="umb-sub-views-nav-item__action umb-outline umb-outline--thin {{moreButton.active ? 'is-active' : ''}}"> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editors.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editors.html index 9bb5a6161d..4d6ceb2ffc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editors.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editors.html @@ -1,7 +1,9 @@
-
-
- -
-
+
+ +
+
{{width}}px x {{height}}px
+
+
+
-
- +
+
+ -
- - +
+ + +
+ +
- - +
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html index edd840a47f..10aa6a774a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html @@ -1,12 +1,17 @@
- +
- + -
+
+
+ +
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less new file mode 100644 index 0000000000..f7e5764335 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card-grid.less @@ -0,0 +1,137 @@ +.umb-media-card-grid { + /* Grid Setup */ + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + grid-auto-rows: minmax(100px, auto); + grid-gap: 10px; + + justify-items: center; + align-items: center; +} +.umb-media-card-grid__cell { + position: relative; + display: flex; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; +} + +.umb-media-card-grid--inline-create-button { + position: absolute; + height: 100%; + z-index: 1; + opacity: 0; + outline: none; + left: 0; + width: 12px; + margin-left: -7px; + padding-left: 6px; + margin-right: -6px; + transition: opacity 240ms; + + &::before { + content: ''; + position: absolute; + background: @blueMid; + background: linear-gradient(0deg, rgba(@blueMid,0) 0%, rgba(@blueMid,1) 50%, rgba(@blueMid,0) 100%); + border-left: 1px solid white; + border-right: 1px solid white; + border-radius: 2px; + left: 0; + top: 0; + bottom: 0; + width: 2px; + animation: umb-media-card-grid--inline-create-button_before 400ms ease-in-out alternate infinite; + transform: scaleX(.99); + transition: transform 240ms ease-out; + + @keyframes umb-media-card-grid--inline-create-button_before { + 0% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + + > .__plus { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; // lets stop avoiding the mouse values in JS move event. + box-sizing: border-box; + width: 28px; + height: 28px; + margin-left: -18px; + margin-top: -18px - 8px; + border-radius: 3em; + font-size: 14px; + border: 2px solid @blueMid; + color: @blueMid; + background-color: rgba(255, 255, 255, .96); + box-shadow: 0 0 0 2px rgba(255, 255, 255, .96); + transform: scale(0); + transition: transform 240ms ease-in; + + animation: umb-media-card-grid--inline-create-button__plus 400ms ease-in-out alternate infinite; + + @keyframes umb-media-card-grid--inline-create-button__plus { + 0% { color: rgba(@blueMid, 1); } + 100% { color: rgba(@blueMid, 0.8); } + } + + } + + &:focus { + > .__plus { + border-color: @ui-outline; + } + } + + &:hover, &:focus { + opacity: 1; + + &::before { + transform: scaleX(1); + } + > .__plus { + transform: scale(1); + transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275); + + } + } +} + +.umb-media-card-grid__create-button { + position: relative; + width: 100%; + padding-bottom: 100%; + + border: 1px dashed @ui-action-discreet-border; + color: @ui-action-discreet-type; + font-weight: bold; + box-sizing: border-box; + border-radius: @baseBorderRadius; + + > div { + position: absolute; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + } +} + +.umb-media-card-grid__create-button:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + text-decoration: none; +} + +.umb-media-card-grid__create-button.--disabled, +.umb-media-card-grid__create-button.--disabled:hover { + color: @gray-7; + border-color: @gray-7; + cursor: default; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html new file mode 100644 index 0000000000..01ce31415e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.html @@ -0,0 +1,47 @@ + +
+ +
+ +

+ + +

+ +

+ + +

+ + + {{vm.media.name}} + + + + + + + + +
+ + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less new file mode 100644 index 0000000000..de3840b4d7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umb-media-card.less @@ -0,0 +1,186 @@ +.umb-media-card, +umb-media-card { + position: relative; + display: inline-block; + width: 100%; + //background-color: white; + border-radius: @baseBorderRadius; + //box-shadow: 0 1px 2px rgba(0,0,0,.2); + overflow: hidden; + + transition: box-shadow 120ms; + + cursor: pointer; + + .umb-outline(); + + &:hover { + box-shadow: 0 1px 3px rgba(@ui-action-type-hover, .5); + } + + &.--isOpen { + &::after { + content: ""; + position: absolute; + border: 2px solid @ui-active-border; + border-radius: @baseBorderRadius; + top:0; + bottom: 0; + left: 0; + right: 0; + } + } + + &.--hasError { + border: 2px solid @errorBackground; + } + + &.--sortable-placeholder { + &::after { + content: ""; + position: absolute; + background-color:rgba(@ui-drop-area-color, .05); + border: 2px solid rgba(@ui-drop-area-color, .1); + border-radius: @baseBorderRadius; + box-shadow: 0 0 4px rgba(@ui-drop-area-color, 0.05); + top:0; + bottom: 0; + left: 0; + right: 0; + animation: umb-block-card--sortable-placeholder 400ms ease-in-out alternate infinite; + @keyframes umb-block-card--sortable-placeholder { + 0% { opacity: 1; } + 100% { opacity: 0.5; } + } + } + box-shadow: none; + } + + .__status { + + position: absolute; + top: 0; + left: 0; + right: 0; + padding: 2px; + + &.--error { + background-color: @errorBackground; + color: @errorText; + } + } + + .__showcase { + position: relative; + max-width: 100%; + min-height: 120px; + max-height: 240px; + text-align: center; + //padding-bottom: 10/16*100%; + //background-color: @gray-12; + + img { + object-fit: contain; + max-height: 240px; + } + + umb-file-icon { + width: 100%; + padding-bottom: 100%; + display: block; + .umb-file-icon { + position: absolute; + top: 0; + bottom: 0; + left: 10px; + right: 10px; + display: flex; + align-items: center; + justify-content: center; + } + } + } + + .__info { + position: absolute; + text-align: left; + bottom: 0; + width: 100%; + background-color: #fff; + padding-top: 6px; + padding-bottom: 7px;// 7 + 1 to compentiate for the -1 substraction in margin-bottom. + + opacity: 0; + transition: opacity 120ms; + + &.--error { + opacity: 1; + background-color: @errorBackground; + .__name, .__subname { + color: @errorText; + } + } + + .__name { + font-weight: bold; + font-size: 13px; + color: @ui-action-type; + margin-left: 16px; + margin-bottom: -1px; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .__subname { + color: @gray-4; + font-size: 12px; + margin-left: 16px; + margin-top: 1px; + margin-bottom: -1px; + line-height: 1.5em; + } + } + + &:hover, &:focus, &:focus-within { + .__info { + opacity: 1; + } + .__info:not(.--error) { + .__name { + color: @ui-action-type-hover; + } + } + } + + .__actions { + position: absolute; + top: 10px; + right: 10px; + font-size: 0; + background-color: rgba(255, 255, 255, .96); + border-radius: 16px; + padding-left: 5px; + padding-right: 5px; + + opacity: 0; + transition: opacity 120ms; + .__action { + position: relative; + display: inline-block; + padding: 5px; + font-size: 18px; + + color: @ui-action-discreet-type; + &:hover { + color: @ui-action-discreet-type-hover; + } + } + } + &:hover, &:focus, &:focus-within { + .__actions { + opacity: 1; + } + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js new file mode 100644 index 0000000000..24b20367aa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/mediacard/umbMediaCard.component.js @@ -0,0 +1,97 @@ +(function () { + "use strict"; + + angular + .module("umbraco") + .component("umbMediaCard", { + templateUrl: "views/components/mediacard/umb-media-card.html", + controller: MediaCardController, + controllerAs: "vm", + transclude: true, + bindings: { + mediaKey: " { + if(newValue !== oldValue) { + vm.updateThumbnail(); + } + })); + + function checkErrorState() { + + vm.notAllowed = (vm.media &&vm.allowedTypes && vm.allowedTypes.length > 0 && vm.allowedTypes.indexOf(vm.media.metaData.ContentTypeAlias) === -1); + + if ( + vm.hasError === true || vm.notAllowed === true || (vm.media && vm.media.trashed === true) + ) { + $element.addClass("--hasError") + vm.mediaCardForm.$setValidity('error', false) + } else { + $element.removeClass("--hasError") + vm.mediaCardForm.$setValidity('error', true) + } + } + + vm.$onInit = function () { + + unsubscribe.push($scope.$watchGroup(["vm.media.trashed", "vm.hasError"], checkErrorState)); + + vm.updateThumbnail(); + + unsubscribe.push(eventsService.on("editors.media.saved", function(name, args) { + // if this media item uses the updated media type we want to reload the media file + if(args && args.media && args.media.key === vm.mediaKey) { + vm.updateThumbnail(); + } + })); + } + + + vm.$onDestroy = function () { + unsubscribe.forEach(x => x()); + } + + vm.updateThumbnail = function () { + + if(vm.mediaKey && vm.mediaKey !== "") { + vm.loading = true; + + entityResource.getById(vm.mediaKey, "Media").then(function (mediaEntity) { + vm.media = mediaEntity; + checkErrorState(); + vm.thumbnail = mediaHelper.resolveFileFromEntity(mediaEntity, true); + + vm.loading = false; + }, function () { + localizationService.localize("mediaPicker_deletedItem").then(function (localized) { + vm.media = { + name: localized, + icon: "icon-picture", + trashed: true + }; + vm.loading = false; + $element.addClass("--hasError") + vm.mediaCardForm.$setValidity('error', false) + }); + }); + } + + } + + } + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html index 6531114567..59d67ee2e1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree.html @@ -1,7 +1,7 @@