Merge branch 'l10n_develop' of https://github.com/Ombi-app/Ombi into l10n_develop

This commit is contained in:
Dyson Parkes 2021-11-17 10:37:52 +13:00
commit 29994f8e2b
133 changed files with 1442 additions and 459 deletions

View file

@ -3,6 +3,7 @@ name: CI Build
on: on:
push: push:
branches: [ develop, master ] branches: [ develop, master ]
workflow_dispatch:
jobs: jobs:
build-ui: build-ui:
@ -38,7 +39,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '5.0.x' dotnet-version: '6.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v2
@ -67,6 +68,7 @@ jobs:
uses: TriPSs/conventional-changelog-action@v3 uses: TriPSs/conventional-changelog-action@v3
with: with:
version-file: 'version.json' version-file: 'version.json'
release-count: 20
skip-on-empty: 'false' skip-on-empty: 'false'
git-message: 'chore(release): :rocket: {version}' git-message: 'chore(release): :rocket: {version}'
@ -102,6 +104,12 @@ jobs:
format: tar.gz format: tar.gz
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v2

View file

@ -7,6 +7,7 @@ on:
branches: [ develop ] branches: [ develop ]
schedule: schedule:
- cron: '0 0 * * *' - cron: '0 0 * * *'
workflow_dispatch:
jobs: jobs:
automation-tests: automation-tests:
@ -18,7 +19,7 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 5.0.x dotnet-version: 6.0.x
- uses: actions/setup-node@v2 - uses: actions/setup-node@v2
with: with:
node-version: '14' node-version: '14'
@ -38,7 +39,7 @@ jobs:
- name: Start Backend - name: Start Backend
run: | run: |
nohup dotnet run -p ./src/Ombi -- --host http://*:3577 & nohup dotnet run --project ./src/Ombi -- --host http://*:3577 &
- name: Start Frontend - name: Start Frontend
run: | run: |
nohup yarn --cwd ./src/Ombi/ClientApp start & nohup yarn --cwd ./src/Ombi/ClientApp start &

View file

@ -3,6 +3,7 @@ name: PR Build
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
workflow_dispatch:
jobs: jobs:
build-ui: build-ui:
@ -31,7 +32,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
with: with:
dotnet-version: '5.0.x' dotnet-version: '6.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v2
@ -89,6 +90,9 @@ jobs:
format: tar.gz format: tar.gz
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v2

View file

@ -1,3 +1,61 @@
## [4.6.4](https://github.com/Ombi-app/Ombi/compare/v4.6.3...v4.6.4) (2021-11-12)
## [4.6.3](https://github.com/Ombi-app/Ombi/compare/v4.6.2...v4.6.3) (2021-11-11)
### Bug Fixes
* **discover:** :bug: Display TV + movies on actor page in user language ([#4395](https://github.com/Ombi-app/Ombi/issues/4395)) ([fe635c7](https://github.com/Ombi-app/Ombi/commit/fe635c7106bc487ff879bdc8a73bab16cb389b97))
* **permissions:** :bug: Improved the security around the role "Manage Own Requests" ([#4397](https://github.com/Ombi-app/Ombi/issues/4397)) ([334a32b](https://github.com/Ombi-app/Ombi/commit/334a32bca42f90198d9b720d2bdb710a583be47f)), closes [#4391](https://github.com/Ombi-app/Ombi/issues/4391)
* **search:** Fixed some cases where search wouldn't work correctly ([#4398](https://github.com/Ombi-app/Ombi/issues/4398)) ([4410790](https://github.com/Ombi-app/Ombi/commit/4410790bc096826bc11554098f846e3acb59589a))
## [4.6.2](https://github.com/Ombi-app/Ombi/compare/v4.6.1...v4.6.2) (2021-11-10)
### Bug Fixes
* **discover:** TV shows now display on the Actor Pages ([#4388](https://github.com/Ombi-app/Ombi/issues/4388)) ([6b716e7](https://github.com/Ombi-app/Ombi/commit/6b716e722076e3d1e6bf2097c5263645d5ea9edf))
## [4.6.1](https://github.com/Ombi-app/Ombi/compare/v4.6.0...v4.6.1) (2021-11-10)
### Bug Fixes
* :bug: Fixed the MySQL issue after .net 6 upgrade [#4393](https://github.com/Ombi-app/Ombi/issues/4393) ([fea7ff0](https://github.com/Ombi-app/Ombi/commit/fea7ff05139e9ff50c8097fa5389b4ef9ad21a15))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([c6acb45](https://github.com/Ombi-app/Ombi/commit/c6acb45f8d3f371c0b4024c4272849d0d0cc867f))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([18c220a](https://github.com/Ombi-app/Ombi/commit/18c220a0cd0d19e45a07d0c319da2b9512778a8a))
# [4.6.0](https://github.com/Ombi-app/Ombi/compare/v4.4.0...v4.6.0) (2021-11-09)
### Features
* :sparkles: Upgrade Ombi to .NET 6 ([#4390](https://github.com/Ombi-app/Ombi/issues/4390)) ([719eb7d](https://github.com/Ombi-app/Ombi/commit/719eb7dbe37b3a72d264e2f670067518eef70694)), closes [#4392](https://github.com/Ombi-app/Ombi/issues/4392)
# [4.4.0](https://github.com/Ombi-app/Ombi/compare/v4.3.2...v4.4.0) (2021-11-06)
### Bug Fixes
* **request-list:** :bug: Fixed an issue where the request options were not appearing for Music requests ([c0406a2](https://github.com/Ombi-app/Ombi/commit/c0406a2ddebafb03d98ed25cdf7d89dc9a600c7d))
### Features
* **mass-email:** :sparkles: Added the ability to configure the Mass Email, we can now send BCC and we are less likely to be rate limited when not using bcc [#4377](https://github.com/Ombi-app/Ombi/issues/4377) ([ca655ae](https://github.com/Ombi-app/Ombi/commit/ca655ae57042dec44106a2f2ef5ba2e6f1019ee4))
## [4.3.2](https://github.com/Ombi-app/Ombi/compare/v4.3.1...v4.3.2) (2021-11-02) ## [4.3.2](https://github.com/Ombi-app/Ombi/compare/v4.3.1...v4.3.2) (2021-11-02)
@ -67,3 +125,76 @@
## [4.2.11](https://github.com/Ombi-app/Ombi/compare/v4.2.10...v4.2.11) (2021-10-18)
## [4.2.10](https://github.com/Ombi-app/Ombi/compare/v4.2.9...v4.2.10) (2021-10-15)
### Bug Fixes
* :bug: Really really fix it this time? ([543d36e](https://github.com/Ombi-app/Ombi/commit/543d36e5615341bc8378cac377b843a3dbc1ef99))
## [4.2.9](https://github.com/Ombi-app/Ombi/compare/v4.2.8...v4.2.9) (2021-10-15)
### Bug Fixes
* :fire: Really fix the base url issue this time ([9f36923](https://github.com/Ombi-app/Ombi/commit/9f36923c51bfabf9cb026f2da14f9947050af0d9))
## [4.2.8](https://github.com/Ombi-app/Ombi/compare/v4.2.7...v4.2.8) (2021-10-15)
### Bug Fixes
* :adhesive_bandage: See if this fixes the proxy issue ([74d1aca](https://github.com/Ombi-app/Ombi/commit/74d1acae499707a7e21401f53eb2bb90c5bb9cfa))
* :bug: Fixed Ombi not writing the baseUrl correctly ([e9cc8b6](https://github.com/Ombi-app/Ombi/commit/e9cc8b6fe71d3e10c1a901e70227989b3362afe3))
## [4.2.7](https://github.com/Ombi-app/Ombi/compare/v4.2.6...v4.2.7) (2021-10-14)
### Bug Fixes
* :bug: Fixed the issue parsing TheMovieDB dates. They have broken something... ([6e397e0](https://github.com/Ombi-app/Ombi/commit/6e397e02e95f894a92e8bf02428efdcac1275b31))
## [4.2.6](https://github.com/Ombi-app/Ombi/compare/v4.2.5...v4.2.6) (2021-10-14)
### Performance Improvements
* :zap: Use ngxs store for the whole customization section of the app ([97b493d](https://github.com/Ombi-app/Ombi/commit/97b493d869feee59d360b484a6c59388a2aead1f))
## [4.2.5](https://github.com/Ombi-app/Ombi/compare/v4.2.4...v4.2.5) (2021-10-14)
## [4.2.4](https://github.com/Ombi-app/Ombi/compare/v4.2.3...v4.2.4) (2021-10-13)
### Bug Fixes
* **#4344:** :bug: Fixed an issue where we errored on Plex Episode Scan ([cd5532f](https://github.com/Ombi-app/Ombi/commit/cd5532fa8f7ebbfaf942841398672bafb9a405d4))
* **#4345:** :bug: Fixed the issue where denied requests we not appearing correctly ([5a2f652](https://github.com/Ombi-app/Ombi/commit/5a2f652a28f5699dd667afef8dde129817e53392))
## [4.2.3](https://github.com/Ombi-app/Ombi/compare/v4.2.2...v4.2.3) (2021-10-12)
### Bug Fixes
* **user-management:** :bug: Fixed an issue where the Copy users App Link did not generate the correct app link for that user ([8cafcdc](https://github.com/Ombi-app/Ombi/commit/8cafcdcc3baa85c55d75e43835b2289bbea69b0e))

View file

@ -18,7 +18,7 @@ Don't worry, it's grandma friendly, and more importantly; has wife approval cert
| Service | Stable | Develop | Service | Stable | Develop
|----------|:---------------------------:|:----------------------------:| |----------|:---------------------------:|:----------------------------:|
| Build Status | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4) | Build Status | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![CI Build](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/Ombi-app/Ombi/actions/workflows/build.yml) | [![Build Status](https://dev.azure.com/tidusjar/Ombi/_apis/build/status/Ombi%20CI?branchName=feature%2Fv4)](https://dev.azure.com/tidusjar/Ombi/_build/latest?definitionId=18&branchName=feature%2Fv4)
| Download |[![Download](https://img.shields.io/badge/-Download-blue)](https://github.com/Ombi-app/Ombi/releases) | [![Download](https://img.shields.io/badge/-Download-blue)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [![Download](https://img.shields.io/badge/-Download-blue)](https://github.com/ombi-app/ombi/releases) | | Download |[![Download](https://img.shields.io/badge/-Download-blue)](https://github.com/Ombi-app/Ombi/releases) | [![Download](https://img.shields.io/badge/-Download-blue)](https://ci.appveyor.com/project/tidusjar/requestplex/branch/develop/artifacts) | [![Download](https://img.shields.io/badge/-Download-blue)](https://github.com/ombi-app/ombi/releases) |
# Feature Requests # Feature Requests
@ -142,21 +142,28 @@ Here are some of the features Ombi has:
<sub><b>Victor Usoltsev</b></sub> <sub><b>Victor Usoltsev</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/sephrat">
<img src="https://avatars.githubusercontent.com/u/34862846?v=4" width="50;" alt="sephrat"/>
<br />
<sub><b>Sephrat</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/dhruvb14"> <a href="https://github.com/dhruvb14">
<img src="https://avatars.githubusercontent.com/u/4459649?v=4" width="50;" alt="dhruvb14"/> <img src="https://avatars.githubusercontent.com/u/4459649?v=4" width="50;" alt="dhruvb14"/>
<br /> <br />
<sub><b>Dhruv Bhavsar</b></sub> <sub><b>Dhruv Bhavsar</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/joshuaboniface"> <a href="https://github.com/joshuaboniface">
<img src="https://avatars.githubusercontent.com/u/4031396?v=4" width="50;" alt="joshuaboniface"/> <img src="https://avatars.githubusercontent.com/u/4031396?v=4" width="50;" alt="joshuaboniface"/>
<br /> <br />
<sub><b>Joshua M. Boniface</b></sub> <sub><b>Joshua M. Boniface</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/bruvv"> <a href="https://github.com/bruvv">
<img src="https://avatars.githubusercontent.com/u/3063928?v=4" width="50;" alt="bruvv"/> <img src="https://avatars.githubusercontent.com/u/3063928?v=4" width="50;" alt="bruvv"/>
@ -164,13 +171,6 @@ Here are some of the features Ombi has:
<sub><b>Bruvv</b></sub> <sub><b>Bruvv</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/sephrat">
<img src="https://avatars.githubusercontent.com/u/34862846?v=4" width="50;" alt="sephrat"/>
<br />
<sub><b>Sephrat</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/louis-lau"> <a href="https://github.com/louis-lau">
<img src="https://avatars.githubusercontent.com/u/1346804?v=4" width="50;" alt="louis-lau"/> <img src="https://avatars.githubusercontent.com/u/1346804?v=4" width="50;" alt="louis-lau"/>

14
makefile Normal file
View file

@ -0,0 +1,14 @@
backend:
cd src/Ombi && dotnet watch run -- --host http://*:3577
frontend:
cd src/Ombi/ClientApp && yarn start
install-frontend:
cd src/Ombi/ClientApp && yarn
install-tests:
cd tests && yarn
tests:
cd tests && npx cypress open

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,7 +13,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -66,6 +66,8 @@ namespace Ombi.Api
startingTag = builder.Query.Contains("?") ? "&" : "?"; startingTag = builder.Query.Contains("?") ? "&" : "?";
} }
value = Uri.EscapeDataString(value);
builder.Query = hasQuery builder.Query = hasQuery
? $"{builder.Query}{startingTag}{parameter}={value}" ? $"{builder.Query}{startingTag}{parameter}={value}"
: $"{startingTag}{parameter}={value}"; : $"{startingTag}{parameter}={value}";

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,8 +11,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Polly" Version="7.1.0" /> <PackageReference Include="Polly" Version="7.1.0" />
</ItemGroup> </ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoFixture" Version="4.11.0" /> <PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Moq" Version="4.15.1" /> <PackageReference Include="Moq" Version="4.15.1" />
<PackageReference Include="Moq.AutoMock" Version="3.0.0" /> <PackageReference Include="Moq.AutoMock" Version="3.0.0" />
<PackageReference Include="Nunit" Version="3.12.0" /> <PackageReference Include="Nunit" Version="3.12.0" />

View file

@ -0,0 +1,228 @@
using Microsoft.Extensions.Logging;
using MockQueryable.Moq;
using Moq;
using Moq.AutoMock;
using NUnit.Framework;
using Ombi.Core.Authentication;
using Ombi.Core.Models;
using Ombi.Core.Senders;
using Ombi.Notifications;
using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ombi.Core.Tests.Senders
{
[TestFixture]
public class MassEmailSenderTests
{
private MassEmailSender _subject;
private AutoMocker _mocker;
[SetUp]
public void Setup()
{
_mocker = new AutoMocker();
_subject = _mocker.CreateInstance<MassEmailSender>();
}
[Test]
public async Task SendMassEmail_SingleUser()
{
var model = new MassEmailModel
{
Body = "Test",
Subject = "Subject",
Users = new List<OmbiUser>
{
new OmbiUser
{
Id = "a"
}
}
};
_mocker.Setup<OmbiUserManager, IQueryable<OmbiUser>>(x => x.Users).Returns(new List<OmbiUser>
{
new OmbiUser
{
Id = "a",
Email = "Test@test.com"
}
}.AsQueryable().BuildMock().Object);
var result = await _subject.SendMassEmail(model);
_mocker.Verify<IEmailProvider>(x => x.SendAdHoc(It.Is<NotificationMessage>(m => m.Subject == model.Subject
&& m.Message == model.Body
&& m.To == "Test@test.com"), It.IsAny<EmailNotificationSettings>()), Times.Once);
}
[Test]
public async Task SendMassEmail_MultipleUsers()
{
var model = new MassEmailModel
{
Body = "Test",
Subject = "Subject",
Users = new List<OmbiUser>
{
new OmbiUser
{
Id = "a"
},
new OmbiUser
{
Id = "b"
}
}
};
_mocker.Setup<OmbiUserManager, IQueryable<OmbiUser>>(x => x.Users).Returns(new List<OmbiUser>
{
new OmbiUser
{
Id = "a",
Email = "Test@test.com"
},
new OmbiUser
{
Id = "b",
Email = "b@test.com"
}
}.AsQueryable().BuildMock().Object);
var result = await _subject.SendMassEmail(model);
_mocker.Verify<IEmailProvider>(x => x.SendAdHoc(It.Is<NotificationMessage>(m => m.Subject == model.Subject
&& m.Message == model.Body
&& m.To == "Test@test.com"), It.IsAny<EmailNotificationSettings>()), Times.Once);
_mocker.Verify<IEmailProvider>(x => x.SendAdHoc(It.Is<NotificationMessage>(m => m.Subject == model.Subject
&& m.Message == model.Body
&& m.To == "b@test.com"), It.IsAny<EmailNotificationSettings>()), Times.Once);
}
[Test]
public async Task SendMassEmail_UserNoEmail()
{
var model = new MassEmailModel
{
Body = "Test",
Subject = "Subject",
Users = new List<OmbiUser>
{
new OmbiUser
{
Id = "a"
}
}
};
_mocker.Setup<OmbiUserManager, IQueryable<OmbiUser>>(x => x.Users).Returns(new List<OmbiUser>
{
new OmbiUser
{
Id = "a",
}
}.AsQueryable().BuildMock().Object);
var result = await _subject.SendMassEmail(model);
_mocker.Verify<ILogger<MassEmailSender>>(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()),
Times.Once);
_mocker.Verify<IEmailProvider>(x => x.SendAdHoc(It.IsAny<NotificationMessage>(), It.IsAny<EmailNotificationSettings>()), Times.Never);
}
[Test]
public async Task SendMassEmail_Bcc()
{
var model = new MassEmailModel
{
Body = "Test",
Subject = "Subject",
Bcc = true,
Users = new List<OmbiUser>
{
new OmbiUser
{
Id = "a"
},
new OmbiUser
{
Id = "b"
}
}
};
_mocker.Setup<OmbiUserManager, IQueryable<OmbiUser>>(x => x.Users).Returns(new List<OmbiUser>
{
new OmbiUser
{
Id = "a",
Email = "Test@test.com"
},
new OmbiUser
{
Id = "b",
Email = "b@test.com"
}
}.AsQueryable().BuildMock().Object);
var result = await _subject.SendMassEmail(model);
_mocker.Verify<IEmailProvider>(x => x.SendAdHoc(It.Is<NotificationMessage>(m => m.Subject == model.Subject
&& m.Message == model.Body
&& m.Other["bcc"] == "Test@test.com,b@test.com"), It.IsAny<EmailNotificationSettings>()), Times.Once);
}
[Test]
public async Task SendMassEmail_Bcc_NoEmails()
{
var model = new MassEmailModel
{
Body = "Test",
Subject = "Subject",
Bcc = true,
Users = new List<OmbiUser>
{
new OmbiUser
{
Id = "a"
},
new OmbiUser
{
Id = "b"
}
}
};
_mocker.Setup<OmbiUserManager, IQueryable<OmbiUser>>(x => x.Users).Returns(new List<OmbiUser>
{
new OmbiUser
{
Id = "a",
},
new OmbiUser
{
Id = "b",
}
}.AsQueryable().BuildMock().Object);
var result = await _subject.SendMassEmail(model);
_mocker.Verify<IEmailProvider>(x => x.SendAdHoc(It.IsAny<NotificationMessage>(), It.IsAny<EmailNotificationSettings>()), Times.Never);
}
}
}

View file

@ -78,6 +78,32 @@ namespace Ombi.Core.Engine
return _dbTv; return _dbTv;
} }
protected async Task<RequestEngineResult> CheckCanManageRequest(BaseRequest request) {
var errorResult = new RequestEngineResult {
Result = false,
ErrorCode = ErrorCode.NoPermissions
};
var successResult = new RequestEngineResult { Result = true };
// Admins can always manage requests
var isAdmin = await IsInRole(OmbiRoles.PowerUser) || await IsInRole(OmbiRoles.Admin);
if (isAdmin) {
return successResult;
}
// Users with 'ManageOwnRequests' can only manage their own requests
var canManageOwnRequests = await IsInRole(OmbiRoles.ManageOwnRequests);
if (!canManageOwnRequests) {
return errorResult;
}
var isRequestedBySameUser = ( await GetUser() ).Id == request.RequestedUser?.Id;
if (isRequestedBySameUser) {
return successResult;
}
return errorResult;
}
public RequestCountModel RequestCount() public RequestCountModel RequestCount()
{ {
var movieQuery = MovieRepository.GetAll(); var movieQuery = MovieRepository.GetAll();

View file

@ -18,7 +18,7 @@ namespace Ombi.Core.Engine
Task<int> GetTotal(); Task<int> GetTotal();
Task<RequestEngineResult> MarkAvailable(int modelId); Task<RequestEngineResult> MarkAvailable(int modelId);
Task<RequestEngineResult> MarkUnavailable(int modelId); Task<RequestEngineResult> MarkUnavailable(int modelId);
Task RemoveAlbumRequest(int requestId); Task<RequestEngineResult> RemoveAlbumRequest(int requestId);
Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model); Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search); Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
Task<bool> UserHasRequest(string userId); Task<bool> UserHasRequest(string userId);

View file

@ -14,7 +14,7 @@ namespace Ombi.Core.Engine.Interfaces
Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search); Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search);
Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken); Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken);
Task RemoveMovieRequest(int requestId); Task<RequestEngineResult> RemoveMovieRequest(int requestId);
Task RemoveAllMovieRequests(); Task RemoveAllMovieRequests();
Task<MovieRequests> GetRequest(int requestId); Task<MovieRequests> GetRequest(int requestId);
Task<MovieRequests> UpdateMovieRequest(MovieRequests request); Task<MovieRequests> UpdateMovieRequest(MovieRequests request);

View file

@ -20,7 +20,7 @@ namespace Ombi.Core.Engine.Interfaces
Task<TvRequests> UpdateTvRequest(TvRequests request); Task<TvRequests> UpdateTvRequest(TvRequests request);
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId); Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
Task<ChildRequests> UpdateChildRequest(ChildRequests request); Task<ChildRequests> UpdateChildRequest(ChildRequests request);
Task RemoveTvChild(int requestId); Task<RequestEngineResult> RemoveTvChild(int requestId);
Task<RequestEngineResult> ApproveChildRequest(int id); Task<RequestEngineResult> ApproveChildRequest(int id);
Task<IEnumerable<TvRequests>> GetRequestsLite(); Task<IEnumerable<TvRequests>> GetRequestsLite();
Task UpdateQualityProfile(int requestId, int profileId); Task UpdateQualityProfile(int requestId, int profileId);

View file

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Api.TheMovieDb.Models;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Core.Models.Search.V2; using Ombi.Core.Models.Search.V2;
@ -10,6 +11,7 @@ namespace Ombi.Core
{ {
Task<SearchFullInfoTvShowViewModel> GetShowInformation(string tvdbid, CancellationToken token); Task<SearchFullInfoTvShowViewModel> GetShowInformation(string tvdbid, CancellationToken token);
Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId, CancellationToken token); Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId, CancellationToken token);
Task<ActorCredits> GetTvByActor(int actorId, string langCode);
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken); Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
Task<IEnumerable<SearchTvShowViewModel>> Popular(int currentlyLoaded, int amountToLoad, string langCustomCode = null); Task<IEnumerable<SearchTvShowViewModel>> Popular(int currentlyLoaded, int amountToLoad, string langCustomCode = null);
Task<IEnumerable<SearchTvShowViewModel>> Anticipated(int currentlyLoaded, int amountToLoad); Task<IEnumerable<SearchTvShowViewModel>> Anticipated(int currentlyLoaded, int amountToLoad);

View file

@ -654,11 +654,20 @@ namespace Ombi.Core.Engine
/// </summary> /// </summary>
/// <param name="requestId">The request identifier.</param> /// <param name="requestId">The request identifier.</param>
/// <returns></returns> /// <returns></returns>
public async Task RemoveMovieRequest(int requestId) public async Task<RequestEngineResult> RemoveMovieRequest(int requestId)
{ {
var request = await MovieRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); var request = await MovieRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
var result = await CheckCanManageRequest(request);
if (result.IsError)
return result;
await MovieRepository.Delete(request); await MovieRepository.Delete(request);
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
return new RequestEngineResult
{
Result = true,
};
} }
public async Task RemoveAllMovieRequests() public async Task RemoveAllMovieRequests()

View file

@ -404,10 +404,20 @@ namespace Ombi.Core.Engine
/// </summary> /// </summary>
/// <param name="requestId">The request identifier.</param> /// <param name="requestId">The request identifier.</param>
/// <returns></returns> /// <returns></returns>
public async Task RemoveAlbumRequest(int requestId) public async Task<RequestEngineResult> RemoveAlbumRequest(int requestId)
{ {
var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId); var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
var result = await CheckCanManageRequest(request);
if (result.IsError)
return result;
await MusicRepository.Delete(request); await MusicRepository.Delete(request);
return new RequestEngineResult
{
Result = true,
};
} }
public async Task<bool> UserHasRequest(string userId) public async Task<bool> UserHasRequest(string userId)

View file

@ -7,7 +7,7 @@ namespace Ombi.Core.Engine
{ {
public bool Result { get; set; } public bool Result { get; set; }
public string Message { get; set; } public string Message { get; set; }
public bool IsError => !string.IsNullOrEmpty(ErrorMessage); public bool IsError => ( !string.IsNullOrEmpty(ErrorMessage) || ErrorCode != null );
public string ErrorMessage { get; set; } public string ErrorMessage { get; set; }
public ErrorCode? ErrorCode { get; set; } public ErrorCode? ErrorCode { get; set; }
public int RequestId { get; set; } public int RequestId { get; set; }

View file

@ -749,10 +749,14 @@ namespace Ombi.Core.Engine
return request; return request;
} }
public async Task RemoveTvChild(int requestId) public async Task<RequestEngineResult> RemoveTvChild(int requestId)
{ {
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
var result = await CheckCanManageRequest(request);
if (result.IsError)
return result;
TvRepository.Db.ChildRequests.Remove(request); TvRepository.Db.ChildRequests.Remove(request);
var all = TvRepository.Db.TvRequests.Include(x => x.ChildRequests); var all = TvRepository.Db.TvRequests.Include(x => x.ChildRequests);
var parent = all.FirstOrDefault(x => x.Id == request.ParentRequestId); var parent = all.FirstOrDefault(x => x.Id == request.ParentRequestId);
@ -766,6 +770,11 @@ namespace Ombi.Core.Engine
await TvRepository.Db.SaveChangesAsync(); await TvRepository.Db.SaveChangesAsync();
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
return new RequestEngineResult
{
Result = true,
};
} }
public async Task RemoveTvRequest(int requestId) public async Task RemoveTvRequest(int requestId)

View file

@ -323,6 +323,7 @@ namespace Ombi.Core.Engine.V2
public async Task<ActorCredits> GetMoviesByActor(int actorId, string langCode) public async Task<ActorCredits> GetMoviesByActor(int actorId, string langCode)
{ {
langCode = await DefaultLanguageCode(langCode);
var result = await Cache.GetOrAddAsync(nameof(GetMoviesByActor) + actorId + langCode, var result = await Cache.GetOrAddAsync(nameof(GetMoviesByActor) + actorId + langCode,
() => MovieApi.GetActorMovieCredits(actorId, langCode), DateTimeOffset.Now.AddHours(12)); () => MovieApi.GetActorMovieCredits(actorId, langCode), DateTimeOffset.Now.AddHours(12));
// Later we run this through the rules engine // Later we run this through the rules engine

View file

@ -147,6 +147,13 @@ namespace Ombi.Core.Engine.V2
return await processed; return await processed;
} }
public async Task<ActorCredits> GetTvByActor(int actorId, string langCode)
{
langCode = await DefaultLanguageCode(langCode);
var result = await Cache.GetOrAddAsync(nameof(GetTvByActor) + actorId + langCode,
() => _movieApi.GetActorTvCredits(actorId, langCode), DateTimeOffset.Now.AddHours(12));
return result;
}
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken) public async Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken)
{ {

View file

@ -35,6 +35,8 @@ namespace Ombi.Core.Models
public string Subject { get; set; } public string Subject { get; set; }
public string Body { get; set; } public string Body { get; set; }
public bool Bcc { get; set; }
public List<OmbiUser> Users { get; set; } public List<OmbiUser> Users { get; set; }
} }
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,8 +13,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="10.0.0" /> <PackageReference Include="AutoMapper" Version="10.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
<PackageReference Include="MusicBrainzAPI" Version="2.0.1" /> <PackageReference Include="MusicBrainzAPI" Version="2.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />

View file

@ -25,7 +25,9 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -64,6 +66,63 @@ namespace Ombi.Core.Senders
var customization = await _customizationService.GetSettingsAsync(); var customization = await _customizationService.GetSettingsAsync();
var email = await _emailService.GetSettingsAsync(); var email = await _emailService.GetSettingsAsync();
var messagesSent = new List<Task>(); var messagesSent = new List<Task>();
if (model.Bcc)
{
await SendBccMails(model, customization, email, messagesSent);
}
else
{
await SendIndividualEmails(model, customization, email, messagesSent);
}
await Task.WhenAll(messagesSent);
return true;
}
private async Task SendBccMails(MassEmailModel model, CustomizationSettings customization, EmailNotificationSettings email, List<Task> messagesSent)
{
var resolver = new NotificationMessageResolver();
var curlys = new NotificationMessageCurlys();
var validUsers = new List<OmbiUser>();
foreach (var user in model.Users)
{
var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id);
if (!fullUser.Email.HasValue())
{
_log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName);
continue;
}
validUsers.Add(fullUser);
}
if (!validUsers.Any())
{
return;
}
var firstUser = validUsers.FirstOrDefault();
var bccAddress = string.Join(',', validUsers.Select(x => x.Email));
curlys.Setup(firstUser, customization);
var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject };
var content = resolver.ParseMessage(template, curlys);
var msg = new NotificationMessage
{
Message = content.Message,
Subject = content.Subject,
Other = new Dictionary<string, string> { { "bcc", bccAddress } }
};
messagesSent.Add(_email.SendAdHoc(msg, email));
}
private async Task SendIndividualEmails(MassEmailModel model, CustomizationSettings customization, EmailNotificationSettings email, List<Task> messagesSent)
{
var resolver = new NotificationMessageResolver();
var curlys = new NotificationMessageCurlys();
foreach (var user in model.Users) foreach (var user in model.Users)
{ {
var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id); var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id);
@ -72,8 +131,6 @@ namespace Ombi.Core.Senders
_log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName); _log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName);
continue; continue;
} }
var resolver = new NotificationMessageResolver();
var curlys = new NotificationMessageCurlys();
curlys.Setup(fullUser, customization); curlys.Setup(fullUser, customization);
var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject }; var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject };
var content = resolver.ParseMessage(template, curlys); var content = resolver.ParseMessage(template, curlys);
@ -83,13 +140,19 @@ namespace Ombi.Core.Senders
To = fullUser.Email, To = fullUser.Email,
Subject = content.Subject Subject = content.Subject
}; };
messagesSent.Add(_email.SendAdHoc(msg, email)); messagesSent.Add(DelayEmail(msg, email));
_log.LogInformation("Sent mass email to user {0} @ {1}", fullUser.UserName, fullUser.Email); _log.LogInformation("Sent mass email to user {0} @ {1}", fullUser.UserName, fullUser.Email);
} }
}
await Task.WhenAll(messagesSent); /// <summary>
/// This will add a 2 second delay, this is to help with concurrent connection limits
return true; /// <see href="https://github.com/Ombi-app/Ombi/issues/4377"/>
/// </summary>
private async Task DelayEmail(NotificationMessage msg, EmailNotificationSettings email)
{
await Task.Delay(2000);
await _email.SendAdHoc(msg, email);
} }
} }
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,10 +11,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="nunit" Version="3.11.0" /> <PackageReference Include="nunit" Version="3.11.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
</ItemGroup> </ItemGroup>

View file

@ -6,16 +6,6 @@ namespace Ombi.Helpers
{ {
public static class LinqHelpers public static class LinqHelpers
{ {
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> knownKeys = new HashSet<TKey>();
foreach (TSource source1 in source)
{
if (knownKeys.Add(keySelector(source1)))
yield return source1;
}
}
public static HashSet<T> ToHashSet<T>( public static HashSet<T> ToHashSet<T>(
this IEnumerable<T> source, this IEnumerable<T> source,
IEqualityComparer<T> comparer = null) IEqualityComparer<T> comparer = null)

View file

@ -53,17 +53,18 @@ namespace Ombi.Helpers
_memoryCache.Set(CacheKey, mediaServiceCache); _memoryCache.Set(CacheKey, mediaServiceCache);
} }
public async Task Purge() public Task Purge()
{ {
var keys = _memoryCache.Get<List<string>>(CacheKey); var keys = _memoryCache.Get<List<string>>(CacheKey);
if (keys == null) if (keys == null)
{ {
return; return Task.CompletedTask;
} }
foreach (var key in keys) foreach (var key in keys)
{ {
base.Remove(key); base.Remove(key);
} }
return Task.CompletedTask;
} }
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,8 +13,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="EasyCrypto" Version="3.3.2" /> <PackageReference Include="EasyCrypto" Version="3.3.2" />
<PackageReference Include="LazyCache.AspNetCore" Version="2.1.3" /> <PackageReference Include="LazyCache.AspNetCore" Version="2.1.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.0" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.0" />
<PackageReference Include="Quartz" Version="3.1.0" /> <PackageReference Include="Quartz" Version="3.1.0" />

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates
if (string.IsNullOrEmpty(_templateLocation)) if (string.IsNullOrEmpty(_templateLocation))
{ {
#if DEBUG #if DEBUG
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net5.0", "Templates", _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates",
"BasicTemplate.html"); "BasicTemplate.html");
#else #else
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html");

View file

@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates
if (string.IsNullOrEmpty(_templateLocation)) if (string.IsNullOrEmpty(_templateLocation))
{ {
#if DEBUG #if DEBUG
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net5.0", "Templates", "NewsletterTemplate.html"); _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net6.0", "Templates", "NewsletterTemplate.html");
#else #else
_templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "NewsletterTemplate.html"); _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates", "NewsletterTemplate.html");
#endif #endif

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>
@ -10,7 +10,7 @@
<PackageReference Include="Nunit" Version="3.11.0" /> <PackageReference Include="Nunit" Version="3.11.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="16.8.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="16.8.0"></packagereference>
<PackageReference Include="Moq" Version="4.10.0" /> <PackageReference Include="Moq" Version="4.10.0" />
</ItemGroup> </ItemGroup>

View file

@ -64,7 +64,20 @@ namespace Ombi.Notifications
MessageId = messageId MessageId = messageId
}; };
message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress)); message.From.Add(new MailboxAddress(string.IsNullOrEmpty(settings.SenderName) ? settings.SenderAddress : settings.SenderName, settings.SenderAddress));
message.To.Add(new MailboxAddress(model.To, model.To)); if (model.To.HasValue())
{
message.To.Add(new MailboxAddress(model.To, model.To));
}
// Check for BCC
if (model.Other.TryGetValue("bcc", out var bcc))
{
var bccList = bcc.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (var item in bccList)
{
message.Bcc.Add(new MailboxAddress(item, item));
}
}
using (var client = new SmtpClient()) using (var client = new SmtpClient())
{ {

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>
@ -11,7 +11,7 @@
<PackageReference Include="Moq" Version="4.15.1" /> <PackageReference Include="Moq" Version="4.15.1" />
<PackageReference Include="Moq.AutoMock" Version="0.4.0" /> <PackageReference Include="Moq.AutoMock" Version="0.4.0" />
<PackageReference Include="Nunit" Version="3.11.0" /> <PackageReference Include="Nunit" Version="3.11.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"></packagereference>

View file

@ -321,7 +321,9 @@ namespace Ombi.Schedule.Jobs.Ombi
public async Task DownloadAsync(string requestUri, string filename) public async Task DownloadAsync(string requestUri, string filename)
{ {
Logger.LogDebug(LoggingEvents.Updater, "Starting the DownloadAsync"); Logger.LogDebug(LoggingEvents.Updater, "Starting the DownloadAsync");
#pragma warning disable SYSLIB0014 // Type or member is obsolete
using (var client = new WebClient()) using (var client = new WebClient())
#pragma warning restore SYSLIB0014 // Type or member is obsolete
{ {
await client.DownloadFileTaskAsync(requestUri, filename); await client.DownloadFileTaskAsync(requestUri, filename);
} }

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,10 +11,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dapper" Version="1.50.2" />
<PackageReference Include="Quartz" Version="3.1.0" /> <PackageReference Include="Quartz" Version="3.1.0" />
<PackageReference Include="Serilog" Version="2.8.0" /> <PackageReference Include="Serilog" Version="2.8.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.30.0" />
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.6.13" /> <PackageReference Include="HtmlAgilityPack" Version="1.6.13" />
<PackageReference Include="Markdig" Version="0.14.8" /> <PackageReference Include="Markdig" Version="0.14.8" />

View file

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>net5.0</TargetFramework> <PropertyGroup><TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -10,7 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="nunit" Version="3.11.0" /> <PackageReference Include="nunit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
</ItemGroup> </ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -11,7 +11,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Quartz" Version="3.1.0" /> <PackageReference Include="Quartz" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup> </ItemGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -12,14 +12,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.0-alpha.1" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.0" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.0" />
<PackageReference Include="Polly" Version="7.1.0" /> <PackageReference Include="Polly" Version="7.1.0" />
<!--<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />--> <!--<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />-->

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>8.0</LangVersion> <LangVersion>8.0</LangVersion>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -9,12 +9,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="6.0.0" />
<PackageReference Include="Moq" Version="4.10.0" /> <PackageReference Include="Moq" Version="4.10.0" />
<PackageReference Include="Nunit" Version="3.11.0" /> <PackageReference Include="Nunit" Version="3.11.0" />
<PackageReference Include="Hangfire" Version="1.7.1" /> <PackageReference Include="Hangfire" Version="1.7.1" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="16.8.0"></packagereference> <packagereference Include="Microsoft.NET.Test.Sdk" Version="16.8.0"></packagereference>
</ItemGroup> </ItemGroup>

View file

@ -31,6 +31,7 @@ namespace Ombi.Api.TheMovieDb
Task<TvInfo> GetTVInfo(string themoviedbid, string langCode = "en"); Task<TvInfo> GetTVInfo(string themoviedbid, string langCode = "en");
Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode); Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode);
Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode); Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode);
Task<ActorCredits> GetActorTvCredits(int actorId, string langCode);
Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken); Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken);
Task<TheMovieDbContainer<DiscoverMovies>> DiscoverMovies(string langCode, int keywordId); Task<TheMovieDbContainer<DiscoverMovies>> DiscoverMovies(string langCode, int keywordId);
Task<FullMovieInfo> GetFullMovieInfo(int movieId, CancellationToken cancellationToken, string langCode); Task<FullMovieInfo> GetFullMovieInfo(int movieId, CancellationToken cancellationToken, string langCode);

View file

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyName>Ombi.Api.TheMovieDb</AssemblyName> <AssemblyName>Ombi.Api.TheMovieDb</AssemblyName>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>

View file

@ -139,6 +139,16 @@ namespace Ombi.Api.TheMovieDb
var result = await Api.Request<ActorCredits>(request); var result = await Api.Request<ActorCredits>(request);
return result; return result;
} }
public async Task<ActorCredits> GetActorTvCredits(int actorId, string langCode)
{
var request = new Request($"person/{actorId}/tv_credits", BaseUri, HttpMethod.Get);
request.AddQueryString("api_key", ApiToken);
request.AddQueryString("language", langCode);
var result = await Api.Request<ActorCredits>(request);
return result;
}
public async Task<List<TvSearchResult>> SearchTv(string searchTerm, string year = default) public async Task<List<TvSearchResult>> SearchTv(string searchTerm, string year = default)
{ {

View file

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64;</RuntimeIdentifiers>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.0</FileVersion>
<Version></Version> <Version></Version>
@ -13,14 +13,14 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.6.0" /> <PackageReference Include="CommandLineParser" Version="2.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Serilog" Version="2.9.0" /> <PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" /> <PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />

View file

@ -10,13 +10,13 @@
"cSpell.words": [ "cSpell.words": [
"usermanagement" "usermanagement"
], ],
"discord.enabled": true,
"conventionalCommits.scopes": [ "conventionalCommits.scopes": [
"discover", "discover",
"request-limits", "request-limits",
"notifications", "notifications",
"settings", "settings",
"user-management", "user-management",
"newsletter" "newsletter",
"mass-email"
] ]
} }

View file

@ -65,6 +65,9 @@ import { TooltipModule } from "primeng/tooltip";
import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor"; import { UnauthorizedInterceptor } from "./auth/unauthorized.interceptor";
import { environment } from "../environments/environment"; import { environment } from "../environments/environment";
import { MatPaginatorIntl } from "@angular/material/paginator";
import { TranslateService } from "@ngx-translate/core";
import { MatPaginatorI18n } from "./localization/MatPaginatorI18n";
const routes: Routes = [ const routes: Routes = [
{ path: "*", component: PageNotFoundComponent }, { path: "*", component: PageNotFoundComponent },
@ -212,6 +215,10 @@ export function JwtTokenGetter() {
useClass: UnauthorizedInterceptor, useClass: UnauthorizedInterceptor,
multi: true multi: true
}, },
{
provide: MatPaginatorIntl, deps: [TranslateService],
useFactory: (translateService: TranslateService) => new MatPaginatorI18n(translateService).getPaginatorIntl()
},
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
}) })

View file

@ -1,4 +1,4 @@
<div class="small-middle-container" *ngIf="actorCredits"> <div class="small-middle-container">
<div *ngIf="loadingFlag" class="row justify-content-md-center top-space loading-spinner"> <div *ngIf="loadingFlag" class="row justify-content-md-center top-space loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner> <mat-spinner [color]="'accent'"></mat-spinner>

View file

@ -1,10 +1,12 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { SearchV2Service } from "../../../services"; import { SearchV2Service } from "../../../services";
import { IActorCredits } from "../../../interfaces/ISearchTvResultV2"; import { IActorCredits, IActorCast } from "../../../interfaces/ISearchTvResultV2";
import { IDiscoverCardResult } from "../../interfaces"; import { IDiscoverCardResult } from "../../interfaces";
import { RequestType } from "../../../interfaces"; import { RequestType } from "../../../interfaces";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
import { forkJoin } from "rxjs";
import { isEqual } from "lodash";
@Component({ @Component({
templateUrl: "./discover-actor.component.html", templateUrl: "./discover-actor.component.html",
@ -12,7 +14,6 @@ import { AuthService } from "../../../auth/auth.service";
}) })
export class DiscoverActorComponent { export class DiscoverActorComponent {
public actorId: number; public actorId: number;
public actorCredits: IActorCredits;
public loadingFlag: boolean; public loadingFlag: boolean;
public isAdmin: boolean; public isAdmin: boolean;
@ -24,24 +25,32 @@ export class DiscoverActorComponent {
this.route.params.subscribe((params: any) => { this.route.params.subscribe((params: any) => {
this.actorId = params.actorId; this.actorId = params.actorId;
this.isAdmin = this.auth.isAdmin(); this.isAdmin = this.auth.isAdmin();
this.loading(); this.search();
this.searchService.getMoviesByActor(this.actorId).subscribe(res => {
this.actorCredits = res;
this.createModel();
});
}); });
} }
private createModel() { private search() {
this.finishLoading();
this.discoverResults = []; this.discoverResults = [];
this.actorCredits.cast.forEach(m => { this.loading();
forkJoin([
this.searchService.getMoviesByActor(this.actorId),
this.searchService.getTvByActor(this.actorId)
]).subscribe(([movie, tv]) => {
this.pushDiscoverResults(movie.cast, RequestType.movie);
this.pushDiscoverResults(tv.cast, RequestType.tvShow);
this.finishLoading();
});
}
pushDiscoverResults(cast: IActorCast[], type: RequestType) {
cast.forEach(m => {
this.discoverResults.push({ this.discoverResults.push({
available: false, available: false,
posterPath: m.poster_path ? `https://image.tmdb.org/t/p/w300/${m.poster_path}` : "../../../images/default_movie_poster.png", posterPath: m.poster_path ? `https://image.tmdb.org/t/p/w300/${m.poster_path}` : "../../../images/default_movie_poster.png",
requested: false, requested: false,
title: m.title, title: m.title,
type: RequestType.movie, type: type,
id: m.id, id: m.id,
url: null, url: null,
rating: 0, rating: 0,

View file

@ -1,6 +1,6 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
@Component({ @Component({
template: "<h2>Page not found</h2>", template: "<h2>{{ 'ErrorPages.NotFound' | translate }}</h2>",
}) })
export class PageNotFoundComponent { } export class PageNotFoundComponent { }

View file

@ -121,6 +121,7 @@ export interface IMassEmailModel {
subject: string; subject: string;
body: string; body: string;
users: IUser[]; users: IUser[];
bcc: boolean;
} }
export interface INotificationPreferences { export interface INotificationPreferences {

View file

@ -1,6 +1,6 @@
<div class="container top-spacing" *ngIf="details"> <div class="container top-spacing" *ngIf="details">
<h1>Issues for {{details.title}}</h1> <h1>{{ 'Issues.IssuesForTitle' | translate: { title: details.title} }}</h1>
<div> <div>
<span>{{'Issues.Requested' | translate}} <span>{{'Issues.Requested' | translate}}
<i *ngIf="!hasRequest" class="far fa-times-circle"></i> <i *ngIf="!hasRequest" class="far fa-times-circle"></i>

View file

@ -0,0 +1,34 @@
import { MatPaginatorIntl } from '@angular/material/paginator';
import { TranslateService } from '@ngx-translate/core';
export class MatPaginatorI18n {
constructor(private translate: TranslateService) { }
getPaginatorIntl(): MatPaginatorIntl {
const paginatorIntl = new MatPaginatorIntl();
paginatorIntl.itemsPerPageLabel = this.translate.instant('Paginator.itemsPerPageLabel');
paginatorIntl.nextPageLabel = this.translate.instant('Paginator.nextPageLabel');
paginatorIntl.previousPageLabel = this.translate.instant('Paginator.previousPageLabel');
paginatorIntl.firstPageLabel = this.translate.instant('Paginator.firstPageLabel');
paginatorIntl.lastPageLabel = this.translate.instant('Paginator.lastPageLabel');
paginatorIntl.getRangeLabel = this.getRangeLabel.bind(this);
return paginatorIntl;
}
private getRangeLabel(page: number, pageSize: number, length: number): string {
if (length == 0 || pageSize == 0) {
return this.translate.instant('Paginator.rangePageLabel1', { length });
}
length = Math.max(length, 0);
const startIndex = page * pageSize;
// If the start index exceeds the list length, do not try and fix the end index to the end.
const endIndex =
startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize;
return this.translate.instant('Paginator.rangePageLabel2', { startIndex: startIndex + 1, endIndex, length });
}
}

View file

@ -143,7 +143,7 @@
<div class="row" *ngIf="movie.videos?.results?.length > 0"> <div class="row" *ngIf="movie.videos?.results?.length > 0">
<div class="col-12"> <div class="col-12">
<mat-card class="mat-elevation-z8"> <mat-card class="mat-elevation-z8">
<mat-card-header>Trailers</mat-card-header> <mat-card-header>{{'MediaDetails.Trailers' | translate}}</mat-card-header>
<mat-card-content> <mat-card-content>
<p-carousel class="no-indicator" [numVisible]="2" [numScroll]="10" [page]="0" [value]="movie.videos?.results"> <p-carousel class="no-indicator" [numVisible]="2" [numScroll]="10" [page]="0" [value]="movie.videos?.results">
<ng-template let-result pTemplate="item"> <ng-template let-result pTemplate="item">

View file

@ -104,7 +104,7 @@
<hr /> <hr />
<div class="genre-button-container" *ngIf="movie.genres"> <div class="genre-button-container" *ngIf="movie.genres">
<span class="label">{{'MediaDetails.Genres' | translate }}</span> <span class="label">{{'MediaDetails.GenresLabel' | translate }}</span>
<div> <div>
<mat-chip-list> <mat-chip-list>
<mat-chip selected *ngFor="let genre of movie.genres"> <mat-chip selected *ngFor="let genre of movie.genres">

View file

@ -58,7 +58,7 @@
<div class="genre-button-container" *ngIf="tv.genres"> <div class="genre-button-container" *ngIf="tv.genres">
<span class="label">{{'MediaDetails.Genres' | translate }}</span> <span class="label">{{'MediaDetails.GenresLabel' | translate }}</span>
<div> <div>
<mat-chip-list> <mat-chip-list>
<mat-chip selected *ngFor="let genre of tv.genres"> <mat-chip selected *ngFor="let genre of tv.genres">
@ -70,7 +70,7 @@
<hr /> <hr />
<div class="keyword-button-container" *ngIf="tv?.keywords?.keywordsValue?.length > 0"> <div class="keyword-button-container" *ngIf="tv?.keywords?.keywordsValue?.length > 0">
<span class="label">{{'MediaDetails.Keywords' | translate }}:</span> <span class="label">{{'MediaDetails.Keywords' | translate }}</span>
<mat-chip-list> <mat-chip-list>
<mat-chip selected *ngFor="let keyword of tv.keywords.keywordsValue"> <mat-chip selected *ngFor="let keyword of tv.keywords.keywordsValue">
{{keyword.name}} {{keyword.name}}

View file

@ -5,8 +5,9 @@
<div> {{ request.requestStatus | translate }}</div> <div> {{ request.requestStatus | translate }}</div>
</mat-panel-title> </mat-panel-title>
<mat-panel-description> <mat-panel-description>
{{'Requests.RequestedBy' | translate}} '{{request.requestedUser.userAlias}}' on {{'Requests.RequestedByOn' | translate: {
{{request.requestedDate | amLocal | amUserLocale | amDateFormat: 'LL' }} user: request.requestedUser.userAlias,
date: request.requestedDate | amLocal | amUserLocale | amDateFormat: 'LL' } }}
<span *ngIf="request.denied"> - {{request.deniedReason}}</span> <span *ngIf="request.denied"> - {{request.deniedReason}}</span>
</mat-panel-description> </mat-panel-description>
</mat-expansion-panel-header> </mat-expansion-panel-header>

View file

@ -323,8 +323,7 @@
} }
.media-row .mat-raised-button{ .media-row .mat-raised-button{
padding:2px 1.5em;; padding:2px 1.5em;
width:170px;
margin-top:10px; margin-top:10px;
margin-left:10px; margin-left:10px;
} }

View file

@ -60,7 +60,7 @@
<th mat-header-cell *matHeaderCellDef> </th> <th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<button mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</button> <button mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</button>
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin"> {{ 'Requests.Options' | translate}}</button> <button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName )"> {{ 'Requests.Options' | translate}}</button>
</td> </td>
</ng-container> </ng-container>

View file

@ -25,6 +25,8 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit {
public defaultSort: string = "requestedDate"; public defaultSort: string = "requestedDate";
public defaultOrder: string = "desc"; public defaultOrder: string = "desc";
public currentFilter: RequestFilterType = RequestFilterType.All; public currentFilter: RequestFilterType = RequestFilterType.All;
public manageOwnRequests: boolean;
public userName: string;
public RequestFilter = RequestFilterType; public RequestFilter = RequestFilterType;
@ -42,10 +44,12 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit {
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
private auth: AuthService, private storageService: StorageService) { private auth: AuthService, private storageService: StorageService) {
this.userName = auth.claims().name;
} }
public ngOnInit() { public ngOnInit() {
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.manageOwnRequests = this.auth.hasRole("ManageOwnRequests")
const defaultCount = this.storageService.get(this.storageKeyGridCount); const defaultCount = this.storageService.get(this.storageKeyGridCount);
const defaultSort = this.storageService.get(this.storageKey); const defaultSort = this.storageService.get(this.storageKey);
@ -117,16 +121,17 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit {
public openOptions(request: IAlbumRequest) { public openOptions(request: IAlbumRequest) {
const filter = () => { const filter = () => {
this.dataSource = this.dataSource.filter((req) => { this.dataSource = this.dataSource.filter((req) => {
return req.id !== request.id; return req.id !== request.id;
}) });
}; };
const onChange = () => { const onChange = () => {
this.ref.detectChanges(); this.ref.detectChanges();
}; };
this.onOpenOptions.emit({ request: request, filter: filter, onChange: onChange }); const data = { request: request, filter: filter, onChange: onChange, manageOwnRequests: this.manageOwnRequests, isAdmin: this.isAdmin };
this.onOpenOptions.emit(data);
} }
public switchFilter(type: RequestFilterType) { public switchFilter(type: RequestFilterType) {

View file

@ -76,7 +76,7 @@
<th mat-header-cell *matHeaderCellDef> </th> <th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button> <button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || manageOwnRequests"> {{ 'Requests.Options' | translate}}</button> <button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName ) "> {{ 'Requests.Options' | translate}}</button>
</td> </td>
</ng-container> </ng-container>

View file

@ -31,6 +31,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
public defaultOrder: string = "desc"; public defaultOrder: string = "desc";
public currentFilter: RequestFilterType = RequestFilterType.All; public currentFilter: RequestFilterType = RequestFilterType.All;
public selection = new SelectionModel<IMovieRequests>(true, []); public selection = new SelectionModel<IMovieRequests>(true, []);
public userName: string;
public RequestFilter = RequestFilterType; public RequestFilter = RequestFilterType;
@ -46,10 +47,11 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef, constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
private auth: AuthService, private storageService: StorageService, private auth: AuthService, private storageService: StorageService,
private requestServiceV1: RequestService, private notification: NotificationService, private requestServiceV1: RequestService, private notification: NotificationService,
private translateService: TranslateService) { private translateService: TranslateService) {
this.userName = auth.claims().name;
} }
public ngOnInit() { public ngOnInit() {

View file

@ -1,8 +1,10 @@
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef } from '@angular/material/bottom-sheet'; import { MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef } from '@angular/material/bottom-sheet';
import { RequestService } from '../../../services'; import { MessageService, RequestService } from '../../../services';
import { RequestType } from '../../../interfaces'; import { IRequestEngineResult, RequestType } from '../../../interfaces';
import { UpdateType } from '../../models/UpdateType'; import { UpdateType } from '../../models/UpdateType';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'request-options', selector: 'request-options',
@ -13,21 +15,31 @@ export class RequestOptionsComponent {
public RequestType = RequestType; public RequestType = RequestType;
constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: any, constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: any,
private requestService: RequestService, private bottomSheetRef: MatBottomSheetRef<RequestOptionsComponent>) { } private requestService: RequestService,
private messageService: MessageService,
private bottomSheetRef: MatBottomSheetRef<RequestOptionsComponent>,
private translate: TranslateService) { }
public async delete() { public async delete() {
var request: Observable<IRequestEngineResult>;
if (this.data.type === RequestType.movie) { if (this.data.type === RequestType.movie) {
await this.requestService.removeMovieRequestAsync(this.data.id); request = this.requestService.removeMovieRequestAsync(this.data.id);
} }
if (this.data.type === RequestType.tvShow) { if (this.data.type === RequestType.tvShow) {
await this.requestService.deleteChild(this.data.id).toPromise(); request = this.requestService.deleteChild(this.data.id);
} }
if (this.data.type === RequestType.album) { if (this.data.type === RequestType.album) {
await this.requestService.removeAlbumRequest(this.data.id).toPromise(); request = this.requestService.removeAlbumRequest(this.data.id);
} }
request.subscribe(result => {
this.bottomSheetRef.dismiss({type: UpdateType.Delete}); if (result.result) {
return; this.messageService.send(this.translate.instant("Requests.SuccessfullyDeleted"));
this.bottomSheetRef.dismiss({type: UpdateType.Delete});
return;
} else {
this.messageService.sendRequestEngineResultError(result);
}
});
} }
public async approve() { public async approve() {

View file

@ -1,6 +1,6 @@
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" <input type="text" id="search" class="form-control form-control-custom searchwidth" [placeholder]="'Search.Search' | translate"
(keyup)="search($event)"> (keyup)="search($event)">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay"> <button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">

View file

@ -1,6 +1,6 @@
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" <input type="text" id="search" class="form-control form-control-custom searchwidth" [placeholder]="'Search.Search' | translate"
(keyup)="search($event)"> (keyup)="search($event)">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay"> <button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">

View file

@ -1,6 +1,6 @@
<div class="form-group"> <div class="form-group">
<div> <div>
<input type="text" id="search" class="form-control form-control-custom" placeholder="Search" (keyup)="search($event)"> <input type="text" id="search" class="form-control form-control-custom" [placeholder]="'Common.Search' | translate" (keyup)="search($event)">
</div> </div>
</div> </div>
<br /> <br />

View file

@ -73,8 +73,8 @@ export class RequestService extends ServiceHelpers {
this.http.delete(`${this.url}movie/${requestId}`, {headers: this.headers}).subscribe(); this.http.delete(`${this.url}movie/${requestId}`, {headers: this.headers}).subscribe();
} }
public removeMovieRequestAsync(requestId: number) { public removeMovieRequestAsync(requestId: number): Observable<IRequestEngineResult> {
return this.http.delete(`${this.url}movie/${requestId}`, {headers: this.headers}).toPromise(); return this.http.delete<IRequestEngineResult>(`${this.url}movie/${requestId}`, {headers: this.headers});
} }
public updateMovieRequest(request: IMovieRequests): Observable<IMovieRequests> { public updateMovieRequest(request: IMovieRequests): Observable<IMovieRequests> {
@ -128,8 +128,9 @@ export class RequestService extends ServiceHelpers {
public approveChild(child: ITvUpdateModel): Observable<IRequestEngineResult> { public approveChild(child: ITvUpdateModel): Observable<IRequestEngineResult> {
return this.http.post<IRequestEngineResult>(`${this.url}tv/approve`, JSON.stringify(child), {headers: this.headers}); return this.http.post<IRequestEngineResult>(`${this.url}tv/approve`, JSON.stringify(child), {headers: this.headers});
} }
public deleteChild(childId: number): Observable<boolean> {
return this.http.delete<boolean>(`${this.url}tv/child/${childId}`, {headers: this.headers}); public deleteChild(childId: number): Observable<IRequestEngineResult> {
return this.http.delete<IRequestEngineResult>(`${this.url}tv/child/${childId}`, {headers: this.headers});
} }
public subscribeToMovie(requestId: number): Observable<boolean> { public subscribeToMovie(requestId: number): Observable<boolean> {
@ -184,7 +185,7 @@ export class RequestService extends ServiceHelpers {
return this.http.get<IAlbumRequest[]>(`${this.url}music/search/${search}`, {headers: this.headers}); return this.http.get<IAlbumRequest[]>(`${this.url}music/search/${search}`, {headers: this.headers});
} }
public removeAlbumRequest(request: number): any { public removeAlbumRequest(request: number): Observable<IRequestEngineResult> {
return this.http.delete(`${this.url}music/${request}`, {headers: this.headers}); return this.http.delete<IRequestEngineResult>(`${this.url}music/${request}`, {headers: this.headers});
} }
} }

Some files were not shown because too many files have changed in this diff Show more