diff options
Diffstat (limited to 'Timeline')
273 files changed, 0 insertions, 29178 deletions
diff --git a/Timeline/Auth/Attribute.cs b/Timeline/Auth/Attribute.cs deleted file mode 100644 index 86d0109b..00000000 --- a/Timeline/Auth/Attribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.AspNetCore.Authorization;
-using Timeline.Entities;
-
-namespace Timeline.Auth
-{
- public class AdminAuthorizeAttribute : AuthorizeAttribute
- {
- public AdminAuthorizeAttribute()
- {
- Roles = UserRoles.Admin;
- }
- }
-
- public class UserAuthorizeAttribute : AuthorizeAttribute
- {
- public UserAuthorizeAttribute()
- {
- Roles = UserRoles.User;
- }
- }
-}
diff --git a/Timeline/Auth/MyAuthenticationHandler.cs b/Timeline/Auth/MyAuthenticationHandler.cs deleted file mode 100644 index 3c97c329..00000000 --- a/Timeline/Auth/MyAuthenticationHandler.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Timeline.Services;
-using static Timeline.Resources.Authentication.AuthHandler;
-
-namespace Timeline.Auth
-{
- public static class AuthenticationConstants
- {
- public const string Scheme = "Bearer";
- public const string DisplayName = "My Jwt Auth Scheme";
- }
-
- public class MyAuthenticationOptions : AuthenticationSchemeOptions
- {
- /// <summary>
- /// The query param key to search for token. If null then query params are not searched for token. Default to <c>"token"</c>.
- /// </summary>
- public string TokenQueryParamKey { get; set; } = "token";
- }
-
- public class MyAuthenticationHandler : AuthenticationHandler<MyAuthenticationOptions>
- {
- private readonly ILogger<MyAuthenticationHandler> _logger;
- private readonly IUserTokenManager _userTokenManager;
-
- public MyAuthenticationHandler(IOptionsMonitor<MyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserTokenManager userTokenManager)
- : base(options, logger, encoder, clock)
- {
- _logger = logger.CreateLogger<MyAuthenticationHandler>();
- _userTokenManager = userTokenManager;
- }
-
- // return null if no token is found
- private string? ExtractToken()
- {
- // check the authorization header
- string header = Request.Headers[HeaderNames.Authorization];
- if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase))
- {
- var token = header.Substring("Bearer ".Length).Trim();
- _logger.LogInformation(LogTokenFoundInHeader, token);
- return token;
- }
-
- // check the query params
- var paramQueryKey = Options.TokenQueryParamKey;
- if (!string.IsNullOrEmpty(paramQueryKey))
- {
- string token = Request.Query[paramQueryKey];
- if (!string.IsNullOrEmpty(token))
- {
- _logger.LogInformation(LogTokenFoundInQuery, paramQueryKey, token);
- return token;
- }
- }
-
- // not found anywhere then return null
- return null;
- }
-
- protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
- {
- var token = ExtractToken();
- if (string.IsNullOrEmpty(token))
- {
- _logger.LogInformation(LogTokenNotFound);
- return AuthenticateResult.NoResult();
- }
-
- try
- {
- var userInfo = await _userTokenManager.VerifyToken(token);
-
- var identity = new ClaimsIdentity(AuthenticationConstants.Scheme);
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userInfo.Id!.Value.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64));
- identity.AddClaim(new Claim(identity.NameClaimType, userInfo.Username, ClaimValueTypes.String));
- identity.AddClaims(UserRoleConvert.ToArray(userInfo.Administrator!.Value).Select(role => new Claim(identity.RoleClaimType, role, ClaimValueTypes.String)));
-
- var principal = new ClaimsPrincipal();
- principal.AddIdentity(identity);
-
- return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthenticationConstants.Scheme));
- }
- catch (Exception e) when (!(e is ArgumentException))
- {
- _logger.LogInformation(e, LogTokenValidationFail);
- return AuthenticateResult.Fail(e);
- }
- }
- }
-}
diff --git a/Timeline/Auth/PrincipalExtensions.cs b/Timeline/Auth/PrincipalExtensions.cs deleted file mode 100644 index ad7a887f..00000000 --- a/Timeline/Auth/PrincipalExtensions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Security.Principal;
-using Timeline.Entities;
-
-namespace Timeline.Auth
-{
- internal static class PrincipalExtensions
- {
- internal static bool IsAdministrator(this IPrincipal principal)
- {
- return principal.IsInRole(UserRoles.Admin);
- }
- }
-}
diff --git a/Timeline/ClientApp/.babelrc b/Timeline/ClientApp/.babelrc deleted file mode 100644 index 092f2f73..00000000 --- a/Timeline/ClientApp/.babelrc +++ /dev/null @@ -1,27 +0,0 @@ -{
- "presets": [
- "@babel/env",
- "@babel/preset-react"
- ],
- "plugins": [
- "@babel/plugin-syntax-dynamic-import",
- "@babel/plugin-proposal-class-properties",
- "@babel/plugin-proposal-optional-chaining",
- "@babel/plugin-proposal-nullish-coalescing-operator",
- [
- "@babel/plugin-proposal-decorators",
- {
- "decoratorsBeforeExport": true
- }
- ],
- [
- "babel-plugin-transform-builtin-extend",
- {
- "globals": [
- "Error",
- "Array"
- ]
- }
- ]
- ]
-}
\ No newline at end of file diff --git a/Timeline/ClientApp/.editorconfig b/Timeline/ClientApp/.editorconfig deleted file mode 100644 index 779719e0..00000000 --- a/Timeline/ClientApp/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -root = true
-end_of_line = lf
-
-[*.ts]
-tab_width = 2
-
-[*.tsx]
-tab_width = 2
-
-[*.css]
-tab_width = 2
-
-[*.sass]
-tab_width = 2
diff --git a/Timeline/ClientApp/.eslintignore b/Timeline/ClientApp/.eslintignore deleted file mode 100644 index f29f7466..00000000 --- a/Timeline/ClientApp/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -.yarn
-node_modules
-dist
-webpack.*.js
-.eslintrc.js
-postcss.config.js
diff --git a/Timeline/ClientApp/.eslintrc.js b/Timeline/ClientApp/.eslintrc.js deleted file mode 100644 index 900489ed..00000000 --- a/Timeline/ClientApp/.eslintrc.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = {
- env: {
- browser: true,
- es2020: true,
- },
- extends: [
- "eslint:recommended",
- "plugin:react/recommended",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:@typescript-eslint/recommended-requiring-type-checking",
- "plugin:prettier/recommended",
- "prettier/react",
- "prettier/@typescript-eslint",
- "plugin:react-hooks/recommended",
- ],
- globals: {
- Atomics: "readonly",
- SharedArrayBuffer: "readonly",
- },
- parser: "@typescript-eslint/parser",
- parserOptions: {
- project: ["./src/app/tsconfig.json", "./src/sw/tsconfig.json"],
- ecmaFeatures: {
- jsx: true,
- },
- sourceType: "module",
- },
- plugins: ["react", "@typescript-eslint", "react-hooks"],
- settings: {
- react: {
- version: "detect",
- },
- },
- rules: {
- "react/prop-types": "off",
- "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
- "@typescript-eslint/explicit-function-return-type": [
- "warn",
- {
- allowExpressions: true,
- allowTypedFunctionExpressions: true,
- allowHigherOrderFunctions: true,
- },
- ],
- },
-};
diff --git a/Timeline/ClientApp/.gitattributes b/Timeline/ClientApp/.gitattributes deleted file mode 100644 index c1aa21ac..00000000 --- a/Timeline/ClientApp/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -.yarn/** linguist-vendored
\ No newline at end of file diff --git a/Timeline/ClientApp/.gitignore b/Timeline/ClientApp/.gitignore deleted file mode 100644 index 1de0b58f..00000000 --- a/Timeline/ClientApp/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-
-.yarn/*
-!.yarn/cache
-!.yarn/releases
-!.yarn/plugins
-!.yarn/sdks
-!.yarn/versions
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-.vscode/launch.json
-
-/dist
\ No newline at end of file diff --git a/Timeline/ClientApp/.vscode/extensions.json b/Timeline/ClientApp/.vscode/extensions.json deleted file mode 100644 index be640996..00000000 --- a/Timeline/ClientApp/.vscode/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{
- "recommendations": [
- "dbaeumer.vscode-eslint",
- "esbenp.prettier-vscode",
- "arcanis.vscode-zipfs",
- "syler.sass-indented",
- "editorconfig.editorconfig"
- ]
-}
diff --git a/Timeline/ClientApp/.vscode/preview.yml b/Timeline/ClientApp/.vscode/preview.yml deleted file mode 100644 index eb5e452c..00000000 --- a/Timeline/ClientApp/.vscode/preview.yml +++ /dev/null @@ -1,10 +0,0 @@ - -# .vscode/preview.yml -autoOpen: false # 打开工作空间时是否自动开启所有应用的预览 -apps: - - port: 3000 # 应用的端口 - run: yarn start:mock # 应用的启动命令 - root: . # 应用的启动目录 - name: timeline # 应用名称 - description: Timeline App # 应用描述 - autoOpen: false # 打开工作空间时是否自动开启预览(优先级高于根级 autoOpen) diff --git a/Timeline/ClientApp/.vscode/settings.json b/Timeline/ClientApp/.vscode/settings.json deleted file mode 100644 index 3db658ba..00000000 --- a/Timeline/ClientApp/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{
- "eslint.nodePath": ".yarn/sdks",
- "prettier.prettierPath": ".yarn/sdks/prettier/index.js",
- "typescript.tsdk": ".yarn/sdks/typescript/lib",
- "typescript.enablePromptUseWorkspaceTsdk": true
-}
diff --git a/Timeline/ClientApp/.yarnrc.yml b/Timeline/ClientApp/.yarnrc.yml deleted file mode 100644 index 2e4e43a7..00000000 --- a/Timeline/ClientApp/.yarnrc.yml +++ /dev/null @@ -1,5 +0,0 @@ -plugins:
- - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
- spec: "@yarnpkg/plugin-interactive-tools"
-
-yarnPath: .yarn/releases/yarn-2.1.1.cjs
diff --git a/Timeline/ClientApp/LICENSE b/Timeline/ClientApp/LICENSE deleted file mode 100644 index 238cd2d9..00000000 --- a/Timeline/ClientApp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License
-
-Copyright (c) 2020 杨宇千
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/Timeline/ClientApp/package.json b/Timeline/ClientApp/package.json deleted file mode 100644 index 65c5cbe2..00000000 --- a/Timeline/ClientApp/package.json +++ /dev/null @@ -1,111 +0,0 @@ -{
- "name": "timeline",
- "version": "0.1.0",
- "private": true,
- "homepage": "https://crupest.xyz",
- "keywords": [],
- "description": "Timeline app.",
- "dependencies": {
- "axios": "^0.21.0",
- "bootstrap": "^4.5.3",
- "bootstrap-icons": "^1.0.0",
- "classnames": "^2.2.6",
- "clsx": "^1.1.1",
- "core-js": "^3.6.5",
- "i18next": "^19.8.3",
- "i18next-browser-languagedetector": "^6.0.1",
- "localforage": "^1.9.0",
- "lodash": "^4.17.20",
- "pepjs": "^0.5.2",
- "react": "^17.0.1",
- "react-bootstrap": "^1.4.0",
- "react-dom": "^17.0.1",
- "react-hot-loader": "^4.13.0",
- "react-i18next": "^11.7.3",
- "react-inlinesvg": "^2.1.1",
- "react-responsive": "^8.1.0",
- "react-router": "^5.2.0",
- "react-router-bootstrap": "^0.25.0",
- "react-router-dom": "^5.2.0",
- "regenerator-runtime": "^0.13.7",
- "rxjs": "^6.6.3",
- "workbox-precaching": "^5.1.4",
- "workbox-routing": "^5.1.4",
- "workbox-strategies": "^5.1.4",
- "workbox-window": "^5.1.4",
- "xregexp": "^4.3.0"
- },
- "scripts": {
- "start": "webpack-dev-server --config ./webpack.config.dev.js",
- "build": "webpack --config ./webpack.config.prod.js",
- "lint": "eslint src/ --ext .js --ext .jsx --ext .ts --ext .tsx"
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- },
- "devDependencies": {
- "@babel/core": "^7.12.3",
- "@babel/plugin-proposal-class-properties": "^7.12.1",
- "@babel/plugin-proposal-decorators": "^7.12.1",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
- "@babel/plugin-proposal-optional-chaining": "^7.12.1",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/preset-env": "^7.12.1",
- "@babel/preset-react": "^7.12.1",
- "@babel/preset-typescript": "^7.12.1",
- "@hot-loader/react-dom": "^17.0.0",
- "@types/classnames": "^2.2.10",
- "@types/lodash": "^4.14.162",
- "@types/node": "^14.14.5",
- "@types/react": "^16.9.53",
- "@types/react-dom": "^16.9.8",
- "@types/react-responsive": "^8.0.2",
- "@types/react-router": "^5.1.8",
- "@types/react-router-bootstrap": "^0.24.5",
- "@types/react-router-dom": "^5.1.6",
- "@types/webpack-env": "^1.15.3",
- "@types/xregexp": "^4.3.0",
- "@typescript-eslint/eslint-plugin": "^4.6.0",
- "@typescript-eslint/parser": "^4.6.0",
- "@yarnpkg/pnpify": "^2.3.3",
- "babel-loader": "^8.1.0",
- "babel-plugin-transform-builtin-extend": "^1.1.2",
- "clean-webpack-plugin": "^3.0.0",
- "copy-webpack-plugin": "^6.2.1",
- "css-loader": "^5.0.0",
- "eslint": "^7.12.1",
- "eslint-config-prettier": "^6.14.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-prettier": "^3.1.4",
- "eslint-plugin-react": "^7.21.5",
- "eslint-plugin-react-hooks": "^4.2.0",
- "file-loader": "^6.1.1",
- "html-webpack-plugin": "^4.5.0",
- "http-server": "^0.12.3",
- "mini-css-extract-plugin": "^1.2.0",
- "postcss": "^8.1.4",
- "postcss-loader": "^4.0.4",
- "postcss-preset-env": "^6.7.0",
- "prettier": "^2.1.2",
- "sass": "^1.27.0",
- "sass-loader": "^10.0.4",
- "style-loader": "^2.0.0",
- "ts-loader": "^8.0.7",
- "typescript": "^4.0.5",
- "url-loader": "^4.1.1",
- "webpack": "^5.2.0",
- "webpack-chain": "^6.5.1",
- "webpack-cli": "^4.1.0",
- "webpack-dev-server": "^3.11.0",
- "workbox-webpack-plugin": "^5.1.4"
- }
-}
diff --git a/Timeline/ClientApp/postcss.config.js b/Timeline/ClientApp/postcss.config.js deleted file mode 100644 index 74ee8155..00000000 --- a/Timeline/ClientApp/postcss.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = {
- plugins: [
- [
- "postcss-preset-env",
- {
- // Options
- },
- ],
- ],
-};
diff --git a/Timeline/ClientApp/public/android-chrome-192x192.png b/Timeline/ClientApp/public/android-chrome-192x192.png Binary files differdeleted file mode 100644 index da9b6b81..00000000 --- a/Timeline/ClientApp/public/android-chrome-192x192.png +++ /dev/null diff --git a/Timeline/ClientApp/public/android-chrome-512x512.png b/Timeline/ClientApp/public/android-chrome-512x512.png Binary files differdeleted file mode 100644 index fa84e055..00000000 --- a/Timeline/ClientApp/public/android-chrome-512x512.png +++ /dev/null diff --git a/Timeline/ClientApp/public/apple-touch-icon.png b/Timeline/ClientApp/public/apple-touch-icon.png Binary files differdeleted file mode 100644 index d5a3fb45..00000000 --- a/Timeline/ClientApp/public/apple-touch-icon.png +++ /dev/null diff --git a/Timeline/ClientApp/public/browserconfig.xml b/Timeline/ClientApp/public/browserconfig.xml deleted file mode 100644 index f2c89409..00000000 --- a/Timeline/ClientApp/public/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
- <msapplication>
- <tile>
- <square150x150logo src="/mstile-150x150.png"/>
- <TileColor>#2d89ef</TileColor>
- </tile>
- </msapplication>
-</browserconfig>
diff --git a/Timeline/ClientApp/public/favicon-16x16.png b/Timeline/ClientApp/public/favicon-16x16.png Binary files differdeleted file mode 100644 index 6c978995..00000000 --- a/Timeline/ClientApp/public/favicon-16x16.png +++ /dev/null diff --git a/Timeline/ClientApp/public/favicon-32x32.png b/Timeline/ClientApp/public/favicon-32x32.png Binary files differdeleted file mode 100644 index bbde902f..00000000 --- a/Timeline/ClientApp/public/favicon-32x32.png +++ /dev/null diff --git a/Timeline/ClientApp/public/favicon.ico b/Timeline/ClientApp/public/favicon.ico Binary files differdeleted file mode 100644 index d4cd3db6..00000000 --- a/Timeline/ClientApp/public/favicon.ico +++ /dev/null diff --git a/Timeline/ClientApp/public/mstile-144x144.png b/Timeline/ClientApp/public/mstile-144x144.png Binary files differdeleted file mode 100644 index 61eaaf43..00000000 --- a/Timeline/ClientApp/public/mstile-144x144.png +++ /dev/null diff --git a/Timeline/ClientApp/public/mstile-150x150.png b/Timeline/ClientApp/public/mstile-150x150.png Binary files differdeleted file mode 100644 index 85fa83ee..00000000 --- a/Timeline/ClientApp/public/mstile-150x150.png +++ /dev/null diff --git a/Timeline/ClientApp/public/mstile-310x150.png b/Timeline/ClientApp/public/mstile-310x150.png Binary files differdeleted file mode 100644 index 41889953..00000000 --- a/Timeline/ClientApp/public/mstile-310x150.png +++ /dev/null diff --git a/Timeline/ClientApp/public/mstile-310x310.png b/Timeline/ClientApp/public/mstile-310x310.png Binary files differdeleted file mode 100644 index cddce02e..00000000 --- a/Timeline/ClientApp/public/mstile-310x310.png +++ /dev/null diff --git a/Timeline/ClientApp/public/mstile-70x70.png b/Timeline/ClientApp/public/mstile-70x70.png Binary files differdeleted file mode 100644 index 52f59d43..00000000 --- a/Timeline/ClientApp/public/mstile-70x70.png +++ /dev/null diff --git a/Timeline/ClientApp/public/safari-pinned-tab.svg b/Timeline/ClientApp/public/safari-pinned-tab.svg deleted file mode 100644 index e91f046a..00000000 --- a/Timeline/ClientApp/public/safari-pinned-tab.svg +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
- "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
- width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
- preserveAspectRatio="xMidYMid meet">
-<metadata>
-Created by potrace 1.11, written by Peter Selinger 2001-2013
-</metadata>
-<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
-fill="#000000" stroke="none">
-<path d="M3080 6207 l0 -794 -57 -13 c-148 -33 -321 -102 -503 -202 -250 -136
--519 -398 -687 -669 -122 -196 -224 -463 -264 -694 -24 -139 -32 -418 -16
--560 72 -648 485 -1236 1069 -1523 68 -33 139 -67 158 -75 51 -22 219 -74 263
--82 l37 -7 0 -794 0 -794 420 0 420 0 0 794 0 794 38 7 c128 23 423 146 573
-238 503 309 846 851 916 1447 10 83 10 355 1 441 -15 127 -49 283 -89 399 -38
-111 -36 105 -108 252 -151 310 -366 554 -673 765 -62 43 -260 149 -306 164
--20 6 -41 15 -47 19 -15 11 -183 66 -248 81 l-57 14 0 792 0 793 -420 0 -420
-0 0 -793z m600 -1601 c14 -3 39 -9 55 -12 17 -2 48 -10 70 -16 22 -6 42 -12
-45 -13 21 -4 117 -45 170 -74 277 -145 492 -417 565 -716 24 -100 26 -116 31
--235 12 -327 -107 -619 -344 -847 -342 -329 -846 -405 -1272 -192 -293 147
--488 387 -584 719 -24 81 -31 151 -31 300 1 234 65 432 203 630 57 82 177 201
-263 263 137 97 323 173 474 193 28 3 52 8 54 10 6 5 274 -3 301 -10z"/>
-</g>
-</svg>
diff --git a/Timeline/ClientApp/public/site.webmanifest b/Timeline/ClientApp/public/site.webmanifest deleted file mode 100644 index 74f0901a..00000000 --- a/Timeline/ClientApp/public/site.webmanifest +++ /dev/null @@ -1,22 +0,0 @@ -{
- "$schema": "http://json.schemastore.org/web-manifest",
-
- "name": "Timeline",
- "short_name": "Timeline",
- "description": "Record your life in Timeline! Created by crupest.",
- "icons": [
- {
- "src": "/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
diff --git a/Timeline/ClientApp/sandbox.config.json b/Timeline/ClientApp/sandbox.config.json deleted file mode 100644 index 42b540ce..00000000 --- a/Timeline/ClientApp/sandbox.config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "infiniteLoopProtection": true, - "hardReloadOnChange": false, - "view": "browser", - "container": { - "port": 3000, - "startScript": "start:mock" - }, - "port": 3000, - "startScript": "start:mock" -} diff --git a/Timeline/ClientApp/src/app/App.tsx b/Timeline/ClientApp/src/app/App.tsx deleted file mode 100644 index b68eddb6..00000000 --- a/Timeline/ClientApp/src/app/App.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from "react"; -import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; -import { hot } from "react-hot-loader/root"; - -import AppBar from "./views/common/AppBar"; -import LoadingPage from "./views/common/LoadingPage"; -import Home from "./views/home"; -import Login from "./views/login"; -import Settings from "./views/settings"; -import About from "./views/about"; -import User from "./views/user"; -import TimelinePage from "./views/timeline"; -import AlertHost from "./views/common/alert/AlertHost"; - -import { dataStorage } from "./services/common"; -import { userService, useRawUser } from "./services/user"; - -const NoMatch: React.FC = () => { - return ( - <> - <AppBar /> - <div style={{ height: 56 }} /> - <div>Ah-oh, 404!</div> - </> - ); -}; - -const LazyAdmin = React.lazy( - () => import(/* webpackChunkName: "admin" */ "./views/admin/Admin") -); - -const App: React.FC = () => { - const [loading, setLoading] = React.useState<boolean>(true); - - const user = useRawUser(); - - React.useEffect(() => { - void userService.checkLoginState(); - void dataStorage.ready().then(() => setLoading(false)); - }, []); - - if (user === undefined || loading) { - return <LoadingPage />; - } else { - return ( - <React.Suspense fallback={<LoadingPage />}> - <Router> - <AppBar /> - <Switch> - <Route exact path="/"> - <Home /> - </Route> - <Route exact path="/login"> - <Login /> - </Route> - <Route path="/settings"> - <Settings /> - </Route> - <Route path="/about"> - <About /> - </Route> - <Route path="/timelines/:name"> - <TimelinePage /> - </Route> - <Route path="/users/:username"> - <User /> - </Route> - {user && user.administrator && ( - <Route path="/admin"> - <LazyAdmin user={user} /> - </Route> - )} - <Route> - <NoMatch /> - </Route> - </Switch> - <AlertHost /> - </Router> - </React.Suspense> - ); - } -}; - -export default hot(App); diff --git a/Timeline/ClientApp/src/app/common.ts b/Timeline/ClientApp/src/app/common.ts deleted file mode 100644 index 0a2d345f..00000000 --- a/Timeline/ClientApp/src/app/common.ts +++ /dev/null @@ -1,44 +0,0 @@ -import React from "react"; -import { Observable, Subject } from "rxjs"; - -// This error is thrown when ui goes wrong with bad logic. -// Such as a variable should not be null, but it does. -// This error should never occur. If it does, it indicates there is some logic bug in codes. -export class UiLogicError extends Error {} - -export function useEventEmiiter(): [() => Observable<null>, () => void] { - const ref = React.useRef<Subject<null> | null>(null); - - return React.useMemo(() => { - const getter = (): Subject<null> => { - if (ref.current == null) { - ref.current = new Subject<null>(); - } - return ref.current; - }; - const trigger = (): void => { - getter().next(null); - }; - return [getter, trigger]; - }, []); -} - -export function useValueEventEmiiter<T>(): [ - () => Observable<T>, - (value: T) => void -] { - const ref = React.useRef<Subject<T> | null>(null); - - return React.useMemo(() => { - const getter = (): Subject<T> => { - if (ref.current == null) { - ref.current = new Subject<T>(); - } - return ref.current; - }; - const trigger = (value: T): void => { - getter().next(value); - }; - return [getter, trigger]; - }, []); -} diff --git a/Timeline/ClientApp/src/app/http/common.ts b/Timeline/ClientApp/src/app/http/common.ts deleted file mode 100644 index 54203d1a..00000000 --- a/Timeline/ClientApp/src/app/http/common.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { AxiosError, AxiosResponse } from "axios"; - -export const apiBaseUrl = "/api"; - -export function base64(blob: Blob): Promise<string> { - return new Promise<string>((resolve) => { - const reader = new FileReader(); - reader.onload = function () { - resolve((reader.result as string).replace(/^data:.+;base64,/, "")); - }; - reader.readAsDataURL(blob); - }); -} - -export function extractStatusCode(error: AxiosError): number | null { - if (error.isAxiosError) { - const code = error?.response?.status; - if (typeof code === "number") { - return code; - } - } - return null; -} - -export interface CommonErrorResponse { - code: number; - message: string; -} - -export function extractErrorCode( - error: AxiosError<CommonErrorResponse> -): number | null { - if (error.isAxiosError) { - const code = error.response?.data?.code; - if (typeof code === "number") { - return code; - } - } - return null; -} - -export class HttpNetworkError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export class HttpForbiddenError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export class NotModified {} - -export interface BlobWithEtag { - data: Blob; - etag: string; -} - -export function extractResponseData<T>(res: AxiosResponse<T>): T { - return res.data; -} - -export function catchIfStatusCodeIs< - TResult, - TErrorHandlerResult extends TResult | PromiseLike<TResult> | null | undefined ->( - statusCode: number, - errorHandler: (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult -): (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult { - return (error: AxiosError<CommonErrorResponse>) => { - if (extractStatusCode(error) == statusCode) { - return errorHandler(error); - } else { - throw error; - } - }; -} - -export function convertToIfStatusCodeIs<NewError>( - statusCode: number, - newErrorType: { - new (innerError: AxiosError): NewError; - } -): (error: AxiosError<CommonErrorResponse>) => never { - return catchIfStatusCodeIs(statusCode, (error) => { - throw new newErrorType(error); - }); -} - -export function catchIfErrorCodeIs< - TResult, - TErrorHandlerResult extends TResult | PromiseLike<TResult> | null | undefined ->( - errorCode: number, - errorHandler: (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult -): (error: AxiosError<CommonErrorResponse>) => TErrorHandlerResult { - return (error: AxiosError<CommonErrorResponse>) => { - if (extractErrorCode(error) == errorCode) { - return errorHandler(error); - } else { - throw error; - } - }; -} -export function convertToIfErrorCodeIs<NewError>( - errorCode: number, - newErrorType: { - new (innerError: AxiosError): NewError; - } -): (error: AxiosError<CommonErrorResponse>) => never { - return catchIfErrorCodeIs(errorCode, (error) => { - throw new newErrorType(error); - }); -} - -export function convertToNetworkError( - error: AxiosError<CommonErrorResponse> -): never { - if (error.isAxiosError && error.response == null) { - throw new HttpNetworkError(error); - } else { - throw error; - } -} - -export function convertToForbiddenError( - error: AxiosError<CommonErrorResponse> -): never { - if ( - error.isAxiosError && - error.response != null && - (error.response.status == 401 || error.response.status == 403) - ) { - throw new HttpForbiddenError(error); - } else { - throw error; - } -} - -export function convertToNotModified( - error: AxiosError<CommonErrorResponse> -): NotModified { - if ( - error.isAxiosError && - error.response != null && - error.response.status == 304 - ) { - return new NotModified(); - } else { - throw error; - } -} - -export function convertToBlobWithEtag(res: AxiosResponse<Blob>): BlobWithEtag { - return { - data: res.data, - etag: (res.headers as Record<"etag", string>)["etag"], - }; -} diff --git a/Timeline/ClientApp/src/app/http/timeline.ts b/Timeline/ClientApp/src/app/http/timeline.ts deleted file mode 100644 index eb7d5065..00000000 --- a/Timeline/ClientApp/src/app/http/timeline.ts +++ /dev/null @@ -1,544 +0,0 @@ -import axios, { AxiosError } from "axios"; - -import { updateQueryString, applyQueryParameters } from "../utilities/url"; - -import { - apiBaseUrl, - extractResponseData, - convertToNetworkError, - base64, - convertToIfStatusCodeIs, - convertToIfErrorCodeIs, - BlobWithEtag, - NotModified, - convertToNotModified, - convertToForbiddenError, - convertToBlobWithEtag, -} from "./common"; -import { HttpUser } from "./user"; - -export const kTimelineVisibilities = ["Public", "Register", "Private"] as const; - -export type TimelineVisibility = typeof kTimelineVisibilities[number]; - -export interface HttpTimelineInfo { - uniqueId: string; - name: string; - description: string; - owner: HttpUser; - visibility: TimelineVisibility; - lastModified: Date; - members: HttpUser[]; -} - -export interface HttpTimelineListQuery { - visibility?: TimelineVisibility; - relate?: string; - relateType?: "own" | "join"; -} - -export interface HttpTimelinePostRequest { - name: string; -} - -export interface HttpTimelinePostTextContent { - type: "text"; - text: string; -} - -export interface HttpTimelinePostImageContent { - type: "image"; -} - -export type HttpTimelinePostContent = - | HttpTimelinePostTextContent - | HttpTimelinePostImageContent; - -export interface HttpTimelinePostInfo { - id: number; - content: HttpTimelinePostContent; - time: Date; - lastUpdated: Date; - author: HttpUser; - deleted: false; -} - -export interface HttpTimelineDeletedPostInfo { - id: number; - time: Date; - lastUpdated: Date; - author?: HttpUser; - deleted: true; -} - -export type HttpTimelineGenericPostInfo = - | HttpTimelinePostInfo - | HttpTimelineDeletedPostInfo; - -export interface HttpTimelinePostPostRequestTextContent { - type: "text"; - text: string; -} - -export interface HttpTimelinePostPostRequestImageContent { - type: "image"; - data: Blob; -} - -export type HttpTimelinePostPostRequestContent = - | HttpTimelinePostPostRequestTextContent - | HttpTimelinePostPostRequestImageContent; - -export interface HttpTimelinePostPostRequest { - content: HttpTimelinePostPostRequestContent; - time?: Date; -} - -export interface HttpTimelinePatchRequest { - visibility?: TimelineVisibility; - description?: string; -} - -export class HttpTimelineNotExistError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export class HttpTimelinePostNotExistError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export class HttpTimelineNameConflictError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -//-------------------- begin: internal model -------------------- - -interface RawTimelineInfo { - uniqueId: string; - name: string; - description: string; - owner: HttpUser; - visibility: TimelineVisibility; - lastModified: string; - members: HttpUser[]; -} - -interface RawTimelinePostTextContent { - type: "text"; - text: string; -} - -interface RawTimelinePostImageContent { - type: "image"; - url: string; -} - -type RawTimelinePostContent = - | RawTimelinePostTextContent - | RawTimelinePostImageContent; - -interface RawTimelinePostInfo { - id: number; - content: RawTimelinePostContent; - time: string; - lastUpdated: string; - author: HttpUser; - deleted: false; -} - -interface RawTimelineDeletedPostInfo { - id: number; - time: string; - lastUpdated: string; - author: HttpUser; - deleted: true; -} - -type RawTimelineGenericPostInfo = - | RawTimelinePostInfo - | RawTimelineDeletedPostInfo; - -interface RawTimelinePostPostRequestTextContent { - type: "text"; - text: string; -} - -interface RawTimelinePostPostRequestImageContent { - type: "image"; - data: string; -} - -type RawTimelinePostPostRequestContent = - | RawTimelinePostPostRequestTextContent - | RawTimelinePostPostRequestImageContent; - -interface RawTimelinePostPostRequest { - content: RawTimelinePostPostRequestContent; - time?: string; -} - -//-------------------- end: internal model -------------------- - -function processRawTimelineInfo(raw: RawTimelineInfo): HttpTimelineInfo { - return { - ...raw, - lastModified: new Date(raw.lastModified), - }; -} - -function processRawTimelinePostInfo( - raw: RawTimelinePostInfo -): HttpTimelinePostInfo; -function processRawTimelinePostInfo( - raw: RawTimelineGenericPostInfo -): HttpTimelineGenericPostInfo; -function processRawTimelinePostInfo( - raw: RawTimelineGenericPostInfo -): HttpTimelineGenericPostInfo { - return { - ...raw, - time: new Date(raw.time), - lastUpdated: new Date(raw.lastUpdated), - }; -} - -export interface IHttpTimelineClient { - listTimeline(query: HttpTimelineListQuery): Promise<HttpTimelineInfo[]>; - getTimeline(timelineName: string): Promise<HttpTimelineInfo>; - getTimeline( - timelineName: string, - query: { - checkUniqueId?: string; - } - ): Promise<HttpTimelineInfo>; - getTimeline( - timelineName: string, - query: { - checkUniqueId?: string; - ifModifiedSince: Date; - } - ): Promise<HttpTimelineInfo | NotModified>; - postTimeline( - req: HttpTimelinePostRequest, - token: string - ): Promise<HttpTimelineInfo>; - patchTimeline( - timelineName: string, - req: HttpTimelinePatchRequest, - token: string - ): Promise<HttpTimelineInfo>; - deleteTimeline(timelineName: string, token: string): Promise<void>; - memberPut( - timelineName: string, - username: string, - token: string - ): Promise<void>; - memberDelete( - timelineName: string, - username: string, - token: string - ): Promise<void>; - listPost( - timelineName: string, - token?: string - ): Promise<HttpTimelinePostInfo[]>; - listPost( - timelineName: string, - token: string | undefined, - query: { - modifiedSince?: Date; - includeDeleted?: false; - } - ): Promise<HttpTimelinePostInfo[]>; - listPost( - timelineName: string, - token: string | undefined, - query: { - modifiedSince?: Date; - includeDeleted: true; - } - ): Promise<HttpTimelineGenericPostInfo[]>; - getPostData( - timelineName: string, - postId: number, - token?: string - ): Promise<BlobWithEtag>; - getPostData( - timelineName: string, - postId: number, - token: string | undefined, - etag: string - ): Promise<BlobWithEtag | NotModified>; - postPost( - timelineName: string, - req: HttpTimelinePostPostRequest, - token: string - ): Promise<HttpTimelinePostInfo>; - deletePost( - timelineName: string, - postId: number, - token: string - ): Promise<void>; -} - -export class HttpTimelineClient implements IHttpTimelineClient { - listTimeline(query: HttpTimelineListQuery): Promise<HttpTimelineInfo[]> { - return axios - .get<RawTimelineInfo[]>( - applyQueryParameters(`${apiBaseUrl}/timelines`, query) - ) - .then(extractResponseData) - .then((list) => list.map(processRawTimelineInfo)) - .catch(convertToNetworkError); - } - - getTimeline(timelineName: string): Promise<HttpTimelineInfo>; - getTimeline( - timelineName: string, - query: { - checkUniqueId?: string; - } - ): Promise<HttpTimelineInfo>; - getTimeline( - timelineName: string, - query: { - checkUniqueId?: string; - ifModifiedSince: Date; - } - ): Promise<HttpTimelineInfo | NotModified>; - getTimeline( - timelineName: string, - query?: { - checkUniqueId?: string; - ifModifiedSince?: Date; - } - ): Promise<HttpTimelineInfo | NotModified> { - return axios - .get<RawTimelineInfo>( - applyQueryParameters(`${apiBaseUrl}/timelines/${timelineName}`, query) - ) - .then((res) => { - if (res.status === 304) { - return new NotModified(); - } else { - return processRawTimelineInfo(res.data); - } - }) - .catch(convertToIfStatusCodeIs(404, HttpTimelineNotExistError)) - .catch(convertToNetworkError); - } - - postTimeline( - req: HttpTimelinePostRequest, - token: string - ): Promise<HttpTimelineInfo> { - return axios - .post<RawTimelineInfo>(`${apiBaseUrl}/timelines?token=${token}`, req) - .then(extractResponseData) - .then(processRawTimelineInfo) - .catch(convertToIfErrorCodeIs(11040101, HttpTimelineNameConflictError)) - .catch(convertToNetworkError); - } - - patchTimeline( - timelineName: string, - req: HttpTimelinePatchRequest, - token: string - ): Promise<HttpTimelineInfo> { - return axios - .patch<RawTimelineInfo>( - `${apiBaseUrl}/timelines/${timelineName}?token=${token}`, - req - ) - .then(extractResponseData) - .then(processRawTimelineInfo) - .catch(convertToNetworkError); - } - - deleteTimeline(timelineName: string, token: string): Promise<void> { - return axios - .delete(`${apiBaseUrl}/timelines/${timelineName}?token=${token}`) - .catch(convertToNetworkError) - .then(); - } - - memberPut( - timelineName: string, - username: string, - token: string - ): Promise<void> { - return axios - .put( - `${apiBaseUrl}/timelines/${timelineName}/members/${username}?token=${token}` - ) - .catch(convertToNetworkError) - .then(); - } - - memberDelete( - timelineName: string, - username: string, - token: string - ): Promise<void> { - return axios - .delete( - `${apiBaseUrl}/timelines/${timelineName}/members/${username}?token=${token}` - ) - .catch(convertToNetworkError) - .then(); - } - - listPost( - timelineName: string, - token?: string - ): Promise<HttpTimelinePostInfo[]>; - listPost( - timelineName: string, - token: string | undefined, - query: { - modifiedSince?: Date; - includeDeleted?: false; - } - ): Promise<HttpTimelinePostInfo[]>; - listPost( - timelineName: string, - token: string | undefined, - query: { - modifiedSince?: Date; - includeDeleted: true; - } - ): Promise<HttpTimelineGenericPostInfo[]>; - listPost( - timelineName: string, - token?: string, - query?: { - modifiedSince?: Date; - includeDeleted?: boolean; - } - ): Promise<HttpTimelineGenericPostInfo[]> { - let url = `${apiBaseUrl}/timelines/${timelineName}/posts`; - url = updateQueryString("token", token, url); - if (query != null) { - if (query.modifiedSince != null) { - url = updateQueryString( - "modifiedSince", - query.modifiedSince.toISOString(), - url - ); - } - if (query.includeDeleted != null) { - url = updateQueryString( - "includeDeleted", - query.includeDeleted ? "true" : "false", - url - ); - } - } - - return axios - .get<RawTimelineGenericPostInfo[]>(url) - .then(extractResponseData) - .catch(convertToIfStatusCodeIs(404, HttpTimelineNotExistError)) - .catch(convertToForbiddenError) - .catch(convertToNetworkError) - .then((rawPosts) => - rawPosts.map((raw) => processRawTimelinePostInfo(raw)) - ); - } - - getPostData( - timelineName: string, - postId: number, - token: string - ): Promise<BlobWithEtag>; - getPostData( - timelineName: string, - postId: number, - token?: string, - etag?: string - ): Promise<BlobWithEtag | NotModified> { - const headers = - etag != null - ? { - "If-None-Match": etag, - } - : undefined; - - let url = `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}/data`; - url = updateQueryString("token", token, url); - - return axios - .get(url, { - responseType: "blob", - headers, - }) - .then(convertToBlobWithEtag) - .catch(convertToNotModified) - .catch(convertToIfStatusCodeIs(404, HttpTimelinePostNotExistError)) - .catch(convertToNetworkError); - } - - async postPost( - timelineName: string, - req: HttpTimelinePostPostRequest, - token: string - ): Promise<HttpTimelinePostInfo> { - let content: RawTimelinePostPostRequestContent; - if (req.content.type === "image") { - const base64Data = await base64(req.content.data); - content = { - ...req.content, - data: base64Data, - } as RawTimelinePostPostRequestImageContent; - } else { - content = req.content; - } - const rawReq: RawTimelinePostPostRequest = { - content, - }; - if (req.time != null) { - rawReq.time = req.time.toISOString(); - } - return await axios - .post<RawTimelinePostInfo>( - `${apiBaseUrl}/timelines/${timelineName}/posts?token=${token}`, - rawReq - ) - .then(extractResponseData) - .catch(convertToNetworkError) - .then((rawPost) => processRawTimelinePostInfo(rawPost)); - } - - deletePost( - timelineName: string, - postId: number, - token: string - ): Promise<void> { - return axios - .delete( - `${apiBaseUrl}/timelines/${timelineName}/posts/${postId}?token=${token}` - ) - .catch(convertToNetworkError) - .then(); - } -} - -let client: IHttpTimelineClient = new HttpTimelineClient(); - -export function getHttpTimelineClient(): IHttpTimelineClient { - return client; -} - -export function setHttpTimelineClient( - newClient: IHttpTimelineClient -): IHttpTimelineClient { - const old = client; - client = newClient; - return old; -} diff --git a/Timeline/ClientApp/src/app/http/token.ts b/Timeline/ClientApp/src/app/http/token.ts deleted file mode 100644 index ae0cf3f6..00000000 --- a/Timeline/ClientApp/src/app/http/token.ts +++ /dev/null @@ -1,72 +0,0 @@ -import axios, { AxiosError } from "axios"; - -import { - apiBaseUrl, - convertToNetworkError, - convertToIfErrorCodeIs, - extractResponseData, -} from "./common"; -import { HttpUser } from "./user"; - -export interface HttpCreateTokenRequest { - username: string; - password: string; - expire: number; -} - -export interface HttpCreateTokenResponse { - token: string; - user: HttpUser; -} - -export interface HttpVerifyTokenRequest { - token: string; -} - -export interface HttpVerifyTokenResponse { - user: HttpUser; -} - -export class HttpCreateTokenBadCredentialError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export interface IHttpTokenClient { - create(req: HttpCreateTokenRequest): Promise<HttpCreateTokenResponse>; - verify(req: HttpVerifyTokenRequest): Promise<HttpVerifyTokenResponse>; -} - -export class HttpTokenClient implements IHttpTokenClient { - create(req: HttpCreateTokenRequest): Promise<HttpCreateTokenResponse> { - return axios - .post<HttpCreateTokenResponse>(`${apiBaseUrl}/token/create`, req) - .then(extractResponseData) - .catch( - convertToIfErrorCodeIs(11010101, HttpCreateTokenBadCredentialError) - ) - .catch(convertToNetworkError); - } - - verify(req: HttpVerifyTokenRequest): Promise<HttpVerifyTokenResponse> { - return axios - .post<HttpVerifyTokenResponse>(`${apiBaseUrl}/token/verify`, req) - .then(extractResponseData) - .catch(convertToNetworkError); - } -} - -let client: IHttpTokenClient = new HttpTokenClient(); - -export function getHttpTokenClient(): IHttpTokenClient { - return client; -} - -export function setHttpTokenClient( - newClient: IHttpTokenClient -): IHttpTokenClient { - const old = client; - client = newClient; - return old; -} diff --git a/Timeline/ClientApp/src/app/http/user.ts b/Timeline/ClientApp/src/app/http/user.ts deleted file mode 100644 index a0a02cce..00000000 --- a/Timeline/ClientApp/src/app/http/user.ts +++ /dev/null @@ -1,134 +0,0 @@ -import axios, { AxiosError } from "axios"; - -import { - apiBaseUrl, - convertToNetworkError, - extractResponseData, - convertToIfStatusCodeIs, - convertToIfErrorCodeIs, - NotModified, - BlobWithEtag, - convertToBlobWithEtag, - convertToNotModified, -} from "./common"; - -export interface HttpUser { - uniqueId: string; - username: string; - administrator: boolean; - nickname: string; -} - -export interface HttpUserPatchRequest { - nickname?: string; -} - -export interface HttpChangePasswordRequest { - oldPassword: string; - newPassword: string; -} - -export class HttpUserNotExistError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export class HttpChangePasswordBadCredentialError extends Error { - constructor(public innerError?: AxiosError) { - super(); - } -} - -export interface IHttpUserClient { - get(username: string): Promise<HttpUser>; - patch( - username: string, - req: HttpUserPatchRequest, - token: string - ): Promise<HttpUser>; - getAvatar(username: string): Promise<BlobWithEtag>; - getAvatar( - username: string, - etag: string - ): Promise<BlobWithEtag | NotModified>; - putAvatar(username: string, data: Blob, token: string): Promise<void>; - changePassword(req: HttpChangePasswordRequest, token: string): Promise<void>; -} - -export class HttpUserClient implements IHttpUserClient { - get(username: string): Promise<HttpUser> { - return axios - .get<HttpUser>(`${apiBaseUrl}/users/${username}`) - .then(extractResponseData) - .catch(convertToIfStatusCodeIs(404, HttpUserNotExistError)) - .catch(convertToNetworkError); - } - - patch( - username: string, - req: HttpUserPatchRequest, - token: string - ): Promise<HttpUser> { - return axios - .patch<HttpUser>(`${apiBaseUrl}/users/${username}?token=${token}`, req) - .then(extractResponseData) - .catch(convertToNetworkError); - } - - getAvatar(username: string): Promise<BlobWithEtag>; - getAvatar( - username: string, - etag?: string - ): Promise<BlobWithEtag | NotModified> { - const headers = - etag != null - ? { - "If-None-Match": etag, - } - : undefined; - - return axios - .get(`${apiBaseUrl}/users/${username}/avatar`, { - responseType: "blob", - headers, - }) - .then(convertToBlobWithEtag) - .catch(convertToNotModified) - .catch(convertToIfStatusCodeIs(404, HttpUserNotExistError)) - .catch(convertToNetworkError); - } - - putAvatar(username: string, data: Blob, token: string): Promise<void> { - return axios - .put(`${apiBaseUrl}/users/${username}/avatar?token=${token}`, data, { - headers: { - "Content-Type": data.type, - }, - }) - .catch(convertToNetworkError) - .then(); - } - - changePassword(req: HttpChangePasswordRequest, token: string): Promise<void> { - return axios - .post(`${apiBaseUrl}/userop/changepassword?token=${token}`, req) - .catch( - convertToIfErrorCodeIs(11020201, HttpChangePasswordBadCredentialError) - ) - .catch(convertToNetworkError) - .then(); - } -} - -let client: IHttpUserClient = new HttpUserClient(); - -export function getHttpUserClient(): IHttpUserClient { - return client; -} - -export function setHttpUserClient(newClient: IHttpUserClient): IHttpUserClient { - const old = client; - client = newClient; - return old; -} diff --git a/Timeline/ClientApp/src/app/i18n.ts b/Timeline/ClientApp/src/app/i18n.ts deleted file mode 100644 index cdced7bf..00000000 --- a/Timeline/ClientApp/src/app/i18n.ts +++ /dev/null @@ -1,79 +0,0 @@ -import i18n, { BackendModule, ResourceKey } from "i18next"; -import LanguageDetector from "i18next-browser-languagedetector"; -import { initReactI18next } from "react-i18next"; - -const backend: BackendModule = { - type: "backend", - async read(language, namespace, callback) { - function error(message: string): void { - callback(new Error(message), false); - } - - function success(result: ResourceKey): void { - callback(null, result); - } - - if (namespace !== "translation") { - error("Namespace must be 'translation'."); - } - - if (language === "en") { - const res = ( - await import( - /* webpackChunkName: "locales-en" */ "./locales/en/translation" - ) - ).default; - success(res); - } else if (language === "zh-cn" || language === "zh") { - const res = ( - await import( - /* webpackChunkName: "locales-zh" */ "./locales/zh/translation" - ) - ).default; - success(res); - } else { - error(`Language ${language} is not supported.`); - } - }, - init() {}, // eslint-disable-line @typescript-eslint/no-empty-function - create() {}, // eslint-disable-line @typescript-eslint/no-empty-function -}; - -export const i18nPromise = i18n - .use(LanguageDetector) - .use(backend) - .use(initReactI18next) // bind react-i18next to the instance - .init({ - fallbackLng: false, - lowerCaseLng: true, - - debug: process.env.NODE_ENV === "development", - - interpolation: { - escapeValue: false, // not needed for react!! - }, - - // react i18next special options (optional) - // override if needed - omit if ok with defaults - /* - react: { - bindI18n: 'languageChanged', - bindI18nStore: '', - transEmptyNodeValue: '', - transSupportBasicHtmlNodes: true, - transKeepBasicHtmlNodesFor: ['br', 'strong', 'i'], - useSuspense: true, - } - */ - }); - -if (module.hot) { - module.hot.accept( - ["./locales/en/translation", "./locales/zh/translation"], - () => { - void i18n.reloadResources(); - } - ); -} - -export default i18n; diff --git a/Timeline/ClientApp/src/app/index.ejs b/Timeline/ClientApp/src/app/index.ejs deleted file mode 100644 index 49306786..00000000 --- a/Timeline/ClientApp/src/app/index.ejs +++ /dev/null @@ -1,29 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8" /> - <meta http-equiv="X-UA-Compatible" content="IE=edge" /> - <meta name="viewport" content="width=device-width,initial-scale=1.0" /> - - <link rel="icon" href="/favicon.ico" /> - <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> - <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" /> - <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" /> - <link rel="manifest" href="/site.webmanifest" /> - <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" /> - <meta name="msapplication-TileColor" content="#2d89ef" /> - <meta name="theme-color" content="#ffffff" /> - - <title><%= htmlWebpackPlugin.options.title %></title> - </head> - <body> - <noscript> - <strong> - We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work - properly without JavaScript enabled. Please enable it to continue. - </strong> - </noscript> - <div id="app"></div> - <!-- built files will be auto injected --> - </body> -</html> diff --git a/Timeline/ClientApp/src/app/index.sass b/Timeline/ClientApp/src/app/index.sass deleted file mode 100644 index 08e03bac..00000000 --- a/Timeline/ClientApp/src/app/index.sass +++ /dev/null @@ -1,66 +0,0 @@ -@import '~bootstrap/scss/bootstrap' - -@import './views/common/common' -@import './views/common/alert/alert' -@import './views/home/home' -@import './views/about/about' -@import './views/login/login' -@import './views/timeline-common/timeline-common' -@import './views/timeline/timeline' -@import './views/user/user' - -body - margin: 0 - -small - line-height: 1.2 - -.flex-fix-length - flex-grow: 0 - flex-shrink: 0 - -.position-lt - left: 0 - top: 0 - -.avatar - width: 60px - &.large - width: 100px - &.small - width: 40px - -.mt-appbar - margin-top: 56px - -.icon-button - font-size: 1.4em - &.large - font-size: 1.6em - -.cursor-pointer - cursor: pointer - -textarea - resize: none - -.white-space-no-wrap - white-space: nowrap - -.cru-card - @extend .shadow - @extend .border - @extend .border-primary - @extend .rounded - @extend .bg-light - -.full-viewport-center-child - position: fixed - width: 100vw - height: 100vh - display: flex - justify-content: center - align-items: center - -.text-orange - color: $orange diff --git a/Timeline/ClientApp/src/app/index.tsx b/Timeline/ClientApp/src/app/index.tsx deleted file mode 100644 index 00a75a4a..00000000 --- a/Timeline/ClientApp/src/app/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import "regenerator-runtime"; -import "core-js/modules/es.promise"; -import "core-js/modules/es.array.iterator"; -import "pepjs"; - -import React from "react"; -import ReactDOM from "react-dom"; - -import "./index.sass"; - -import "./i18n"; - -import App from "./App"; - -ReactDOM.render(<App />, document.getElementById("app")); diff --git a/Timeline/ClientApp/src/app/locales/en/translation.ts b/Timeline/ClientApp/src/app/locales/en/translation.ts deleted file mode 100644 index c7f33d1e..00000000 --- a/Timeline/ClientApp/src/app/locales/en/translation.ts +++ /dev/null @@ -1,202 +0,0 @@ -import TranslationResource from "../scheme"; - -const translation: TranslationResource = { - welcome: "Welcome!", - search: "Search", - loadFailReload: "Load failed, click <1>here</1> to reload.", - serviceWorker: { - availableOffline: - "Timeline is now cached in your computer and you can use it offline. 🎉🎉🎉", - upgradePrompt: "App is getting a new version!", - upgradeNow: "Update Now", - upgradeSuccess: - "Congratulations! App update succeeded! Still you can use it offline. 🎉🎉🎉", - externalActivatedPrompt: - "A new version of app is activated. Please refresh the page. Or it may be broken.", - reloadNow: "Refresh Now", - }, - nav: { - settings: "Settings", - login: "Login", - about: "About", - }, - chooseImage: "Choose a image", - loadImageError: "Failed to load image.", - home: { - go: "Go!", - allTimeline: "All Timelines", - joinTimeline: "Joined Timelines", - ownTimeline: "Owned Timelines", - offlinePrompt: - "Oh oh, it seems you are offline. Here list some timelines cached locally. You can view them or click <1>here</1> to refresh.", - createButton: "Create Timeline", - createDialog: { - title: "Create Timeline!", - name: "Name", - nameFormat: - "Name must consist of only letter including non-English letter, digit, hyphen(-) and underline(_) and be no longer than 26.", - badFormat: "Bad format.", - noEmpty: "Empty is not allowed.", - tooLong: "Too long.", - }, - }, - operationDialog: { - retry: "Retry", - nextStep: "Next", - previousStep: "Previous", - confirm: "Confirm", - cancel: "Cancel", - ok: "OK!", - processing: "Processing...", - success: "Success!", - error: "An error occured.", - }, - timeline: { - messageCantSee: "Sorry, you are not allowed to see this timeline.😅", - userNotExist: "The user does not exist!", - timelineNotExist: "The timeline does not exist!", - manage: "Manage", - memberButton: "Member", - send: "Send", - deletePostFailed: "Failed to delete post.", - sendPostFailed: "Failed to send post.", - visibility: { - public: "public to everyone", - register: "only registed people can see", - private: "only members can see", - }, - visibilityTooltip: { - public: - "Everyone including those without accounts can see content of the timeline.", - register: - "Only those who have an account and logined can see content of the timeline.", - private: "Only members of this timeline can see content of the timeline.", - }, - dialogChangeProperty: { - title: "Change Timeline Properties", - visibility: "Visibility", - description: "Description", - }, - member: { - alreadyMember: "The user is already a member.", - add: "Add", - remove: "Remove", - }, - manageItem: { - nickname: "Nickname", - avatar: "Avatar", - property: "Timeline Property", - member: "Timeline Member", - delete: "Delete Timeline", - }, - deleteDialog: { - title: "Delete Timeline", - inputPrompt: - "This is a dangerous action. If you are sure to delete timeline<1>{{name}}</1>, please input its name below and click confirm button.", - notMatch: "Name does not match.", - }, - postSyncState: { - syncing: "Syncing", - synced: "Synced", - offline: "Offline", - }, - post: { - deleteDialog: { - title: "Confirm Delete", - prompt: - "Are you sure to delete the post? This operation is not recoverable.", - }, - }, - }, - user: { - username: "username", - password: "password", - login: "login", - rememberMe: "Remember Me", - welcomeBack: "Welcome back!", - verifyTokenFailed: "User login info is expired. Please login again!", - verifyTokenFailedNetwork: - "Verifying user login info failed. Please check your network and refresh page!", - }, - login: { - emptyUsername: "Username can't be empty.", - emptyPassword: "Password can't be empty.", - badCredential: "Username or password is invalid.", - alreadyLogin: "Already login! Redirect to home page in 3s!", - }, - userPage: { - dialogChangeNickname: { - title: "Change Nickname", - inputLabel: "New nickname", - }, - dialogChangeAvatar: { - title: "Change Avatar", - previewImgAlt: "preview", - prompt: { - select: "Please select a picture.", - crop: "Please crop the picture.", - processingCrop: "Cropping picture...", - uploading: "Uploading...", - preview: "Please preview avatar", - }, - upload: "upload", - }, - }, - settings: { - subheaders: { - account: "Account", - customization: "Customization", - }, - languagePrimary: "Choose display language.", - languageSecondary: - "You language preference will be saved locally. Next time you visit this page, last language option will be used.", - changePassword: "Change account's password.", - logout: "Log out this account.", - gotoSelf: - "Click here to go to timeline of myself to change nickname and avatar.", - dialogChangePassword: { - title: "Change Password", - prompt: - "You are changing your password. You need to input the correct old password. After change, you need to login again and all old login will be invalid.", - inputOldPassword: "Old password", - inputNewPassword: "New password", - inputRetypeNewPassword: "Retype new password", - errorEmptyOldPassword: "Old password can't be empty.", - errorEmptyNewPassword: "New password can't be empty.", - errorRetypeNotMatch: "Password retyped does not match.", - }, - dialogConfirmLogout: { - title: "Confirm Logout", - prompt: - "Are you sure to log out? All cached data in the browser will be deleted.", - }, - }, - about: { - author: { - title: "Site Developer", - fullname: "Fullname: ", - nickname: "Nickname: ", - introduction: "Introduction: ", - introductionContent: "A programmer coding based on coincidence", - links: "Links: ", - }, - site: { - title: "Site Information", - content: - "The name of this site is <1>Timeline</1>, which is a Web App with <3>timeline</3> as its core concept. Its frontend and backend are both developed by <5>me</5>, and open source on GitHub. It is relatively easy to deploy it on your own server, which is also one of my goals. Welcome to comment anything in GitHub repository.", - repo: "GitHub Repo", - }, - credits: { - title: "Credits", - content: - "Timeline is works standing on shoulders of gaints. Special appreciation for many open source projects listed below or not. Related licenses could be found in GitHub repository.", - frontend: "Frontend: ", - backend: "Backend: ", - }, - }, - admin: { - title: "admin", - }, -}; - -export default translation; diff --git a/Timeline/ClientApp/src/app/locales/scheme.ts b/Timeline/ClientApp/src/app/locales/scheme.ts deleted file mode 100644 index 9e3534ac..00000000 --- a/Timeline/ClientApp/src/app/locales/scheme.ts +++ /dev/null @@ -1,182 +0,0 @@ -export default interface TranslationResource { - welcome: string; - search: string; - chooseImage: string; - loadImageError: string; - loadFailReload: string; - serviceWorker: { - availableOffline: string; - upgradePrompt: string; - upgradeNow: string; - upgradeSuccess: string; - externalActivatedPrompt: string; - reloadNow: string; - }; - nav: { - settings: string; - login: string; - about: string; - }; - home: { - go: string; - allTimeline: string; - joinTimeline: string; - ownTimeline: string; - offlinePrompt: string; - createButton: string; - createDialog: { - title: string; - name: string; - nameFormat: string; - badFormat: string; - noEmpty: string; - tooLong: string; - }; - }; - operationDialog: { - retry: string; - nextStep: string; - previousStep: string; - confirm: string; - cancel: string; - ok: string; - processing: string; - success: string; - error: string; - }; - timeline: { - messageCantSee: string; - userNotExist: string; - timelineNotExist: string; - manage: string; - memberButton: string; - send: string; - deletePostFailed: string; - sendPostFailed: string; - visibility: { - public: string; - register: string; - private: string; - }; - visibilityTooltip: { - public: string; - register: string; - private: string; - }; - dialogChangeProperty: { - title: string; - visibility: string; - description: string; - }; - member: { - alreadyMember: string; - add: string; - remove: string; - }; - manageItem: { - nickname: string; - avatar: string; - property: string; - member: string; - delete: string; - }; - deleteDialog: { - title: string; - inputPrompt: string; - notMatch: string; - }; - postSyncState: { - syncing: string; - synced: string; - offline: string; - }; - post: { - deleteDialog: { - title: string; - prompt: string; - }; - }; - }; - user: { - username: string; - password: string; - login: string; - rememberMe: string; - welcomeBack: string; - verifyTokenFailed: string; - verifyTokenFailedNetwork: string; - }; - login: { - emptyUsername: string; - emptyPassword: string; - badCredential: string; - alreadyLogin: string; - }; - userPage: { - dialogChangeNickname: { - title: string; - inputLabel: string; - }; - dialogChangeAvatar: { - title: string; - previewImgAlt: string; - prompt: { - select: string; - crop: string; - processingCrop: string; - preview: string; - uploading: string; - }; - upload: string; - }; - }; - settings: { - subheaders: { - account: string; - customization: string; - }; - languagePrimary: string; - languageSecondary: string; - changePassword: string; - logout: string; - gotoSelf: string; - dialogChangePassword: { - title: string; - prompt: string; - inputOldPassword: string; - inputNewPassword: string; - inputRetypeNewPassword: string; - errorEmptyOldPassword: string; - errorEmptyNewPassword: string; - errorRetypeNotMatch: string; - }; - dialogConfirmLogout: { - title: string; - prompt: string; - }; - }; - about: { - author: { - title: string; - fullname: string; - nickname: string; - introduction: string; - introductionContent: string; - links: string; - }; - site: { - title: string; - content: string; - repo: string; - }; - credits: { - title: string; - content: string; - frontend: string; - backend: string; - }; - }; - admin: { - title: string; - }; -} diff --git a/Timeline/ClientApp/src/app/locales/zh/translation.ts b/Timeline/ClientApp/src/app/locales/zh/translation.ts deleted file mode 100644 index df316366..00000000 --- a/Timeline/ClientApp/src/app/locales/zh/translation.ts +++ /dev/null @@ -1,195 +0,0 @@ -import TranslationResource from "../scheme"; - -const translation: TranslationResource = { - welcome: "欢迎!", - search: "搜索", - loadFailReload: "加载失败,<1>点击</1>重试。", - serviceWorker: { - availableOffline: "Timeline 已经缓存在本地,你可以离线使用它。🎉🎉🎉", - upgradePrompt: "App 有新版本!", - upgradeNow: "现在升级", - upgradeSuccess: "App 升级成功,当然,你仍可以离线使用它。 🎉🎉🎉", - externalActivatedPrompt: - "一个新的 App 版本已经激活,请刷新页面使用,否则页面可能会出现故障。", - reloadNow: "立刻刷新", - }, - nav: { - settings: "设置", - login: "登陆", - about: "关于", - }, - chooseImage: "选择一个图片", - loadImageError: "加载图片失败", - home: { - go: "冲!", - allTimeline: "所有的时间线", - joinTimeline: "加入的时间线", - ownTimeline: "拥有的时间线", - offlinePrompt: - "你好像处于离线状态。以下是一些缓存在本地的时间线。你可以查看它们或者<1>点击</1>重新获取在线信息。", - createButton: "创建时间线", - createDialog: { - title: "创建时间线!", - name: "名字", - nameFormat: - "名字只能由字母、汉字、数字、下划线(_)和连字符(-)构成,且长度不能超过26.", - badFormat: "格式错误", - noEmpty: "不能为空", - tooLong: "太长了", - }, - }, - operationDialog: { - retry: "重试", - nextStep: "下一步", - previousStep: "上一步", - confirm: "确定", - cancel: "取消", - ok: "好的!", - processing: "处理中...", - success: "成功!", - error: "出错啦!", - }, - timeline: { - messageCantSee: "不好意思,你没有权限查看这个时间线。😅", - userNotExist: "该用户不存在!", - timelineNotExist: "该时间线不存在!", - manage: "管理", - memberButton: "成员", - send: "发送", - deletePostFailed: "删除消息失败。", - sendPostFailed: "发送消息失败。", - visibility: { - public: "对所有人公开", - register: "仅注册可见", - private: "仅成员可见", - }, - visibilityTooltip: { - public: "所有人都可以看到这个时间线的内容,包括没有注册的人。", - register: "只有拥有本网站的账号且登陆了的人才能看到这个时间线的内容。", - private: "只有这个时间线的成员可以看到这个时间线的内容。", - }, - dialogChangeProperty: { - title: "修改时间线属性", - visibility: "可见性", - description: "描述", - }, - member: { - alreadyMember: "该用户已经是一个成员。", - add: "添加", - remove: "移除", - }, - manageItem: { - nickname: "昵称", - avatar: "头像", - property: "时间线属性", - member: "时间线成员", - delete: "删除时间线", - }, - deleteDialog: { - title: "删除时间线", - inputPrompt: - "这是一个危险的操作。如果您确认要删除时间线<1>{{name}}</1>,请在下面输入它的名字并点击确认。", - notMatch: "名字不匹配", - }, - postSyncState: { - syncing: "同步中", - synced: "同步成功", - offline: "离线", - }, - post: { - deleteDialog: { - title: "确认删除", - prompt: "确定删除这个消息?这个操作不可撤销。", - }, - }, - }, - user: { - username: "用户名", - password: "密码", - login: "登录", - rememberMe: "记住我", - welcomeBack: "欢迎回来!", - verifyTokenFailed: "用户登录信息已过期,请重新登陆!", - verifyTokenFailedNetwork: - "验证用户登录信息失败,请检查网络连接并刷新页面!", - }, - login: { - emptyUsername: "用户名不能为空。", - emptyPassword: "密码不能为空。", - badCredential: "用户名或密码错误。", - alreadyLogin: "已经登陆,三秒后导航到首页!", - }, - userPage: { - dialogChangeNickname: { - title: "更改昵称", - inputLabel: "新昵称", - }, - dialogChangeAvatar: { - title: "修改头像", - previewImgAlt: "预览", - prompt: { - select: "请选择一个图片", - crop: "请裁剪图片", - processingCrop: "正在裁剪图片", - uploading: "正在上传", - preview: "请预览图片", - }, - upload: "上传", - }, - }, - settings: { - subheaders: { - account: "账户", - customization: "个性化", - }, - languagePrimary: "选择显示的语言。", - languageSecondary: - "您的语言偏好将会存储在本地,下次浏览时将自动使用上次保存的语言选项。", - changePassword: "更改账号的密码。", - logout: "注销此账号。", - gotoSelf: "点击前往个人时间线修改昵称和头像!", - dialogChangePassword: { - title: "修改密码", - prompt: - "您正在修改密码,您需要输入正确的旧密码。成功修改后您需要重新登陆,而且以前所有的登录都会失效。", - inputOldPassword: "旧密码", - inputNewPassword: "新密码", - inputRetypeNewPassword: "再次输入新密码", - errorEmptyOldPassword: "旧密码不能为空。", - errorEmptyNewPassword: "新密码不能为空", - errorRetypeNotMatch: "两次输入的密码不一致", - }, - dialogConfirmLogout: { - title: "确定注销", - prompt: "您确定注销此账号?这将删除所有已经缓存在浏览器的数据。", - }, - }, - about: { - author: { - title: "网站作者", - fullname: "姓名:", - nickname: "昵称:", - introduction: "简介:", - introductionContent: "一个基于巧合编程的代码爱好者。", - links: "链接:", - }, - site: { - title: "网站信息", - content: - "这个网站的名字叫 <1>Timeline</1>,是一个以<3>时间线</3>为核心概念的 Web App . 它的前端和后端都是由<5>我</5>开发,并且在 GitHub 上开源。大家可以相对轻松的把它们部署在自己的服务器上,这也是我的目标之一。欢迎大家前往 GitHub 仓库提出任何意见。", - repo: "GitHub 仓库", - }, - credits: { - title: "鸣谢", - content: - "Timeline 是站在巨人肩膀上的作品,感谢以下列出的和其他未列出的许多开源项目,相关 License 请在 GitHub 仓库中查看。", - frontend: "前端:", - backend: "后端:", - }, - }, - admin: { - title: "管理", - }, -}; - -export default translation; diff --git a/Timeline/ClientApp/src/app/service-worker.tsx b/Timeline/ClientApp/src/app/service-worker.tsx deleted file mode 100644 index 3be54bc1..00000000 --- a/Timeline/ClientApp/src/app/service-worker.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { Button } from "react-bootstrap"; - -import { pushAlert } from "./services/alert"; - -if ("serviceWorker" in navigator) { - let isThisTriggerUpgrade = false; - - const upgradeSuccessLocalStorageKey = "TIMELINE_UPGRADE_SUCCESS"; - - if (window.localStorage.getItem(upgradeSuccessLocalStorageKey)) { - pushAlert({ - message: { - type: "i18n", - key: "serviceWorker.upgradeSuccess", - }, - type: "success", - }); - window.localStorage.removeItem(upgradeSuccessLocalStorageKey); - } - - void import("workbox-window").then(({ Workbox, messageSW }) => { - const wb = new Workbox("/sw.js"); - let registration: ServiceWorkerRegistration | undefined; - - // externalactivated is not usable but I still use its name. - wb.addEventListener("controlling", () => { - const upgradeReload = (): void => { - window.localStorage.setItem(upgradeSuccessLocalStorageKey, "true"); - window.location.reload(); - }; - - if (isThisTriggerUpgrade) { - upgradeReload(); - } else { - const Message: React.FC = () => { - const { t } = useTranslation(); - return ( - <> - {t("serviceWorker.externalActivatedPrompt")} - <Button - variant="outline-success" - size="sm" - onClick={upgradeReload} - > - {t("serviceWorker.reloadNow")} - </Button> - </> - ); - }; - - pushAlert({ - message: Message, - dismissTime: "never", - type: "warning", - }); - } - }); - - wb.addEventListener("activated", (event) => { - if (!event.isUpdate) { - pushAlert({ - message: { - type: "i18n", - key: "serviceWorker.availableOffline", - }, - type: "success", - }); - } - }); - - const showSkipWaitingPrompt = (): void => { - const upgrade = (): void => { - isThisTriggerUpgrade = true; - if (registration && registration.waiting) { - // Send a message to the waiting service worker, - // instructing it to activate. - // Note: for this to work, you have to add a message - // listener in your service worker. See below. - void messageSW(registration.waiting, { type: "SKIP_WAITING" }); - } - }; - - const UpgradeMessage: React.FC = () => { - const { t } = useTranslation(); - return ( - <> - {t("serviceWorker.upgradePrompt")} - <Button variant="outline-success" size="sm" onClick={upgrade}> - {t("serviceWorker.upgradeNow")} - </Button> - </> - ); - }; - - pushAlert({ - message: UpgradeMessage, - dismissTime: "never", - type: "success", - }); - }; - - // Add an event listener to detect when the registered - // service worker has installed but is waiting to activate. - wb.addEventListener("waiting", showSkipWaitingPrompt); - wb.addEventListener("externalwaiting", showSkipWaitingPrompt); - - void wb.register().then((reg) => { - registration = reg; - }); - }); -} diff --git a/Timeline/ClientApp/src/app/services/DataHub.ts b/Timeline/ClientApp/src/app/services/DataHub.ts deleted file mode 100644 index 93a9b41f..00000000 --- a/Timeline/ClientApp/src/app/services/DataHub.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { pull } from "lodash"; -import { Observable, BehaviorSubject, combineLatest } from "rxjs"; -import { map } from "rxjs/operators"; - -export type Subscriber<TData> = (data: TData) => void; - -export type WithSyncStatus<T> = T & { syncing: boolean }; - -export class DataLine<TData> { - private _current: TData | undefined = undefined; - - private _syncPromise: Promise<void> | null = null; - private _syncingSubject = new BehaviorSubject<boolean>(false); - - private _observers: Subscriber<TData>[] = []; - - constructor( - private config: { - sync: () => Promise<void>; - destroyable?: (value: TData | undefined) => boolean; - disableInitSync?: boolean; - } - ) { - if (config.disableInitSync !== true) { - setImmediate(() => void this.sync()); - } - } - - private subscribe(subscriber: Subscriber<TData>): void { - this._observers.push(subscriber); - if (this._current !== undefined) { - subscriber(this._current); - } - } - - private unsubscribe(subscriber: Subscriber<TData>): void { - if (!this._observers.includes(subscriber)) return; - pull(this._observers, subscriber); - } - - getObservable(): Observable<TData> { - return new Observable<TData>((observer) => { - const f = (data: TData): void => { - observer.next(data); - }; - this.subscribe(f); - - return () => { - this.unsubscribe(f); - }; - }); - } - - getSyncStatusObservable(): Observable<boolean> { - return this._syncingSubject.asObservable(); - } - - getDataWithSyncStatusObservable(): Observable<WithSyncStatus<TData>> { - return combineLatest([ - this.getObservable(), - this.getSyncStatusObservable(), - ]).pipe( - map(([data, syncing]) => ({ - ...data, - syncing, - })) - ); - } - - get value(): TData | undefined { - return this._current; - } - - next(value: TData): void { - this._current = value; - this._observers.forEach((observer) => observer(value)); - } - - get isSyncing(): boolean { - return this._syncPromise != null; - } - - sync(): Promise<void> { - if (this._syncPromise == null) { - this._syncingSubject.next(true); - this._syncPromise = this.config.sync().then(() => { - this._syncingSubject.next(false); - this._syncPromise = null; - }); - } - - return this._syncPromise; - } - - syncWithAction( - syncAction: (line: DataLine<TData>) => Promise<void> - ): Promise<void> { - if (this._syncPromise == null) { - this._syncingSubject.next(true); - this._syncPromise = syncAction(this).then(() => { - this._syncingSubject.next(false); - this._syncPromise = null; - }); - } - - return this._syncPromise; - } - - get destroyable(): boolean { - const customDestroyable = this.config?.destroyable; - - return ( - this._observers.length === 0 && - !this.isSyncing && - (customDestroyable != null ? customDestroyable(this._current) : true) - ); - } -} - -export class DataHub<TKey, TData> { - private sync: (key: TKey, line: DataLine<TData>) => Promise<void>; - private keyToString: (key: TKey) => string; - private destroyable?: (key: TKey, value: TData | undefined) => boolean; - - private readonly subscriptionLineMap = new Map<string, DataLine<TData>>(); - - private cleanTimerId = 0; - - // setup is called after creating line and if it returns a function as destroyer, then when the line is destroyed the destroyer will be called. - constructor(config: { - sync: (key: TKey, line: DataLine<TData>) => Promise<void>; - keyToString?: (key: TKey) => string; - destroyable?: (key: TKey, value: TData | undefined) => boolean; - }) { - this.sync = config.sync; - this.keyToString = - config.keyToString ?? - ((value): string => { - if (typeof value === "string") return value; - else - throw new Error( - "Default keyToString function only pass string value." - ); - }); - - this.destroyable = config.destroyable; - } - - private cleanLines(): void { - const toDelete: string[] = []; - for (const [key, line] of this.subscriptionLineMap.entries()) { - if (line.destroyable) { - toDelete.push(key); - } - } - - if (toDelete.length === 0) return; - - for (const key of toDelete) { - this.subscriptionLineMap.delete(key); - } - - if (this.subscriptionLineMap.size === 0) { - window.clearInterval(this.cleanTimerId); - this.cleanTimerId = 0; - } - } - - private createLine(key: TKey, disableInitSync = false): DataLine<TData> { - const keyString = this.keyToString(key); - const { destroyable } = this; - const newLine: DataLine<TData> = new DataLine<TData>({ - sync: () => this.sync(key, newLine), - destroyable: - destroyable != null ? (value) => destroyable(key, value) : undefined, - disableInitSync: disableInitSync, - }); - this.subscriptionLineMap.set(keyString, newLine); - if (this.subscriptionLineMap.size === 1) { - this.cleanTimerId = window.setInterval(this.cleanLines.bind(this), 20000); - } - return newLine; - } - - getObservable(key: TKey): Observable<TData> { - return this.getLineOrCreate(key).getObservable(); - } - - getSyncStatusObservable(key: TKey): Observable<boolean> { - return this.getLineOrCreate(key).getSyncStatusObservable(); - } - - getDataWithSyncStatusObservable( - key: TKey - ): Observable<WithSyncStatus<TData>> { - return this.getLineOrCreate(key).getDataWithSyncStatusObservable(); - } - - getLine(key: TKey): DataLine<TData> | null { - const keyString = this.keyToString(key); - return this.subscriptionLineMap.get(keyString) ?? null; - } - - getLineOrCreate(key: TKey): DataLine<TData> { - const keyString = this.keyToString(key); - return this.subscriptionLineMap.get(keyString) ?? this.createLine(key); - } - - getLineOrCreateWithoutInitSync(key: TKey): DataLine<TData> { - const keyString = this.keyToString(key); - return ( - this.subscriptionLineMap.get(keyString) ?? this.createLine(key, true) - ); - } - - optionalInitLineWithSyncAction( - key: TKey, - syncAction: (line: DataLine<TData>) => Promise<void> - ): Promise<void> { - const optionalLine = this.getLine(key); - if (optionalLine != null) return Promise.resolve(); - const line = this.createLine(key, true); - return line.syncWithAction(syncAction); - } -} diff --git a/Timeline/ClientApp/src/app/services/alert.ts b/Timeline/ClientApp/src/app/services/alert.ts deleted file mode 100644 index e4c0e653..00000000 --- a/Timeline/ClientApp/src/app/services/alert.ts +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import pull from "lodash/pull"; - -export interface AlertInfo { - type?: "primary" | "secondary" | "success" | "danger" | "warning" | "info"; - message: string | React.FC<unknown> | { type: "i18n"; key: string }; - dismissTime?: number | "never"; -} - -export interface AlertInfoEx extends AlertInfo { - id: number; -} - -export type AlertConsumer = (alerts: AlertInfoEx) => void; - -export class AlertService { - private consumers: AlertConsumer[] = []; - private savedAlerts: AlertInfoEx[] = []; - private currentId = 1; - - private produce(alert: AlertInfoEx): void { - for (const consumer of this.consumers) { - consumer(alert); - } - } - - registerConsumer(consumer: AlertConsumer): void { - this.consumers.push(consumer); - if (this.savedAlerts.length !== 0) { - for (const alert of this.savedAlerts) { - this.produce(alert); - } - this.savedAlerts = []; - } - } - - unregisterConsumer(consumer: AlertConsumer): void { - pull(this.consumers, consumer); - } - - push(alert: AlertInfo): void { - const newAlert: AlertInfoEx = { ...alert, id: this.currentId++ }; - if (this.consumers.length === 0) { - this.savedAlerts.push(newAlert); - } else { - this.produce(newAlert); - } - } -} - -export const alertService = new AlertService(); - -export function pushAlert(alert: AlertInfo): void { - alertService.push(alert); -} - -export const kAlertHostId = "alert-host"; - -export function getAlertHost(): HTMLElement | null { - return document.getElementById(kAlertHostId); -} diff --git a/Timeline/ClientApp/src/app/services/common.ts b/Timeline/ClientApp/src/app/services/common.ts deleted file mode 100644 index 3bb6b9d7..00000000 --- a/Timeline/ClientApp/src/app/services/common.ts +++ /dev/null @@ -1,23 +0,0 @@ -import localforage from "localforage"; - -import { HttpNetworkError } from "@/http/common"; - -export const dataStorage = localforage.createInstance({ - name: "data", - description: "Database for offline data.", - driver: localforage.INDEXEDDB, -}); - -export class ForbiddenError extends Error { - constructor(message?: string) { - super(message); - } -} - -export function throwIfNotNetworkError(e: unknown): void { - if (!(e instanceof HttpNetworkError)) { - throw e; - } -} - -export type BlobOrStatus = Blob | "loading" | "error"; diff --git a/Timeline/ClientApp/src/app/services/timeline.ts b/Timeline/ClientApp/src/app/services/timeline.ts deleted file mode 100644 index 9db76281..00000000 --- a/Timeline/ClientApp/src/app/services/timeline.ts +++ /dev/null @@ -1,702 +0,0 @@ -import React from "react"; -import XRegExp from "xregexp"; -import { Observable, from, combineLatest, of } from "rxjs"; -import { map, switchMap, startWith } from "rxjs/operators"; -import { uniqBy } from "lodash"; - -import { convertError } from "@/utilities/rxjs"; -import { - TimelineVisibility, - HttpTimelineInfo, - HttpTimelinePatchRequest, - HttpTimelinePostPostRequest, - HttpTimelinePostPostRequestContent, - HttpTimelinePostPostRequestTextContent, - HttpTimelinePostPostRequestImageContent, - HttpTimelinePostInfo, - HttpTimelinePostTextContent, - getHttpTimelineClient, - HttpTimelineNotExistError, - HttpTimelineNameConflictError, -} from "@/http/timeline"; -import { BlobWithEtag, NotModified, HttpForbiddenError } from "@/http/common"; -import { HttpUser } from "@/http/user"; - -export { kTimelineVisibilities } from "@/http/timeline"; - -export type { TimelineVisibility } from "@/http/timeline"; - -import { dataStorage, throwIfNotNetworkError, BlobOrStatus } from "./common"; -import { DataHub, WithSyncStatus } from "./DataHub"; -import { UserAuthInfo, checkLogin, userService, userInfoService } from "./user"; - -export type TimelineInfo = HttpTimelineInfo; -export type TimelineChangePropertyRequest = HttpTimelinePatchRequest; -export type TimelineCreatePostRequest = HttpTimelinePostPostRequest; -export type TimelineCreatePostContent = HttpTimelinePostPostRequestContent; -export type TimelineCreatePostTextContent = HttpTimelinePostPostRequestTextContent; -export type TimelineCreatePostImageContent = HttpTimelinePostPostRequestImageContent; - -export type TimelinePostTextContent = HttpTimelinePostTextContent; - -export interface TimelinePostImageContent { - type: "image"; - data: BlobOrStatus; -} - -export type TimelinePostContent = - | TimelinePostTextContent - | TimelinePostImageContent; - -export interface TimelinePostInfo { - id: number; - content: TimelinePostContent; - time: Date; - lastUpdated: Date; - author: HttpUser; -} - -export const timelineVisibilityTooltipTranslationMap: Record< - TimelineVisibility, - string -> = { - Public: "timeline.visibilityTooltip.public", - Register: "timeline.visibilityTooltip.register", - Private: "timeline.visibilityTooltip.private", -}; - -export class TimelineNotExistError extends Error {} -export class TimelineNameConflictError extends Error {} - -export type TimelineWithSyncStatus = WithSyncStatus< - | { - type: "cache"; - timeline: TimelineInfo; - } - | { - type: "offline" | "synced"; - timeline: TimelineInfo | null; - } ->; - -export type TimelinePostsWithSyncState = WithSyncStatus<{ - type: - | "cache" - | "offline" // Sync failed and use cache. - | "synced" // Sync succeeded. - | "forbid" // The list is forbidden to see. - | "notexist"; // The timeline does not exist. - posts: TimelinePostInfo[]; -}>; - -type TimelineData = Omit<HttpTimelineInfo, "owner" | "members"> & { - owner: string; - members: string[]; -}; - -type TimelinePostData = Omit<HttpTimelinePostInfo, "author"> & { - author: string; -}; - -export class TimelineService { - private getCachedTimeline( - timelineName: string - ): Promise<TimelineData | null> { - return dataStorage.getItem<TimelineData | null>(`timeline.${timelineName}`); - } - - private saveTimeline( - timelineName: string, - data: TimelineData - ): Promise<void> { - return dataStorage - .setItem<TimelineData>(`timeline.${timelineName}`, data) - .then(); - } - - private async clearTimelineData(timelineName: string): Promise<void> { - const keys = (await dataStorage.keys()).filter((k) => - k.startsWith(`timeline.${timelineName}`) - ); - await Promise.all(keys.map((k) => dataStorage.removeItem(k))); - } - - private convertHttpTimelineToData(timeline: HttpTimelineInfo): TimelineData { - return { - ...timeline, - owner: timeline.owner.username, - members: timeline.members.map((m) => m.username), - }; - } - - private _timelineHub = new DataHub< - string, - | { - type: "cache"; - timeline: TimelineData; - } - | { - type: "offline" | "synced"; - timeline: TimelineData | null; - } - >({ - sync: async (key, line) => { - const cache = await this.getCachedTimeline(key); - - if (line.value == undefined) { - if (cache != null) { - line.next({ type: "cache", timeline: cache }); - } - } - - try { - const httpTimeline = await getHttpTimelineClient().getTimeline(key); - - userInfoService.saveUsers([ - httpTimeline.owner, - ...httpTimeline.members, - ]); - - const timeline = this.convertHttpTimelineToData(httpTimeline); - - if (cache != null && timeline.uniqueId !== cache.uniqueId) { - console.log( - `Timeline with name ${key} has changed to a new one. Clear old data.` - ); - await this.clearTimelineData(key); // If timeline has changed, clear all old data. - } - - await this.saveTimeline(key, timeline); - - line.next({ type: "synced", timeline }); - } catch (e) { - if (e instanceof HttpTimelineNotExistError) { - line.next({ type: "synced", timeline: null }); - } else { - if (cache == null) { - line.next({ type: "offline", timeline: null }); - } else { - line.next({ type: "offline", timeline: cache }); - } - throwIfNotNetworkError(e); - } - } - }, - }); - - syncTimeline(timelineName: string): Promise<void> { - return this._timelineHub.getLineOrCreate(timelineName).sync(); - } - - getTimeline$(timelineName: string): Observable<TimelineWithSyncStatus> { - return this._timelineHub.getDataWithSyncStatusObservable(timelineName).pipe( - switchMap((state) => { - const { timeline } = state; - if (timeline != null) { - return combineLatest( - [timeline.owner, ...timeline.members].map((u) => - userInfoService.getUser$(u) - ) - ).pipe( - map((users) => { - return { - ...state, - timeline: { - ...timeline, - owner: users[0], - members: users.slice(1), - }, - }; - }) - ); - } else { - return of(state as TimelineWithSyncStatus); - } - }) - ); - } - - createTimeline(timelineName: string): Observable<TimelineInfo> { - const user = checkLogin(); - return from( - getHttpTimelineClient().postTimeline( - { - name: timelineName, - }, - user.token - ) - ).pipe( - convertError(HttpTimelineNameConflictError, TimelineNameConflictError) - ); - } - - changeTimelineProperty( - timelineName: string, - req: TimelineChangePropertyRequest - ): Observable<TimelineInfo> { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .patchTimeline(timelineName, req, user.token) - .then((timeline) => { - void this.syncTimeline(timelineName); - return timeline; - }) - ); - } - - deleteTimeline(timelineName: string): Observable<unknown> { - const user = checkLogin(); - return from( - getHttpTimelineClient().deleteTimeline(timelineName, user.token) - ); - } - - addMember(timelineName: string, username: string): Observable<unknown> { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .memberPut(timelineName, username, user.token) - .then(() => { - void this.syncTimeline(timelineName); - }) - ); - } - - removeMember(timelineName: string, username: string): Observable<unknown> { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .memberDelete(timelineName, username, user.token) - .then(() => { - void this.syncTimeline(timelineName); - }) - ); - } - - private convertHttpPostToData(post: HttpTimelinePostInfo): TimelinePostData { - return { - ...post, - author: post.author.username, - }; - } - - private convertHttpPostToDataList( - posts: HttpTimelinePostInfo[] - ): TimelinePostData[] { - return posts.map((post) => this.convertHttpPostToData(post)); - } - - private getCachedPosts( - timelineName: string - ): Promise<TimelinePostData[] | null> { - return dataStorage.getItem<TimelinePostData[] | null>( - `timeline.${timelineName}.posts` - ); - } - - private savePosts( - timelineName: string, - data: TimelinePostData[] - ): Promise<void> { - return dataStorage - .setItem<TimelinePostData[]>(`timeline.${timelineName}.posts`, data) - .then(); - } - - private syncPosts(timelineName: string): Promise<void> { - return this._postsHub.getLineOrCreate(timelineName).sync(); - } - - private _postsHub = new DataHub< - string, - { - type: "cache" | "offline" | "synced" | "forbid" | "notexist"; - posts: TimelinePostData[]; - } - >({ - sync: async (key, line) => { - // Wait for timeline synced. In case the timeline has changed to another and old data has been cleaned. - await this.syncTimeline(key); - - if (line.value == null) { - const cache = await this.getCachedPosts(key); - if (cache != null) { - line.next({ type: "cache", posts: cache }); - } - } - - const now = new Date(); - - const lastUpdatedTime = await dataStorage.getItem<Date | null>( - `timeline.${key}.lastUpdated` - ); - - try { - if (lastUpdatedTime == null) { - const httpPosts = await getHttpTimelineClient().listPost( - key, - userService.currentUser?.token - ); - - userInfoService.saveUsers( - uniqBy( - httpPosts.map((post) => post.author), - "username" - ) - ); - - const posts = this.convertHttpPostToDataList(httpPosts); - await this.savePosts(key, posts); - await dataStorage.setItem<Date>(`timeline.${key}.lastUpdated`, now); - - line.next({ type: "synced", posts }); - } else { - const httpPosts = await getHttpTimelineClient().listPost( - key, - userService.currentUser?.token, - { - modifiedSince: lastUpdatedTime, - includeDeleted: true, - } - ); - - const deletedIds = httpPosts - .filter((p) => p.deleted) - .map((p) => p.id); - const changed = httpPosts.filter( - (p): p is HttpTimelinePostInfo => !p.deleted - ); - - userInfoService.saveUsers( - uniqBy( - httpPosts - .map((post) => post.author) - .filter((u): u is HttpUser => u != null), - "username" - ) - ); - - const cache = (await this.getCachedPosts(key)) ?? []; - - const posts = cache.filter((p) => !deletedIds.includes(p.id)); - - for (const changedPost of changed) { - const savedChangedPostIndex = posts.findIndex( - (p) => p.id === changedPost.id - ); - if (savedChangedPostIndex === -1) { - posts.push(this.convertHttpPostToData(changedPost)); - } else { - posts[savedChangedPostIndex] = this.convertHttpPostToData( - changedPost - ); - } - } - - await this.savePosts(key, posts); - await dataStorage.setItem<Date>(`timeline.${key}.lastUpdated`, now); - line.next({ type: "synced", posts }); - } - } catch (e) { - if (e instanceof HttpTimelineNotExistError) { - line.next({ type: "notexist", posts: [] }); - } else if (e instanceof HttpForbiddenError) { - line.next({ type: "forbid", posts: [] }); - } else { - const cache = await this.getCachedPosts(key); - if (cache == null) { - line.next({ type: "offline", posts: [] }); - } else { - line.next({ type: "offline", posts: cache }); - } - throwIfNotNetworkError(e); - } - } - }, - }); - - getPosts$(timelineName: string): Observable<TimelinePostsWithSyncState> { - return this._postsHub.getDataWithSyncStatusObservable(timelineName).pipe( - switchMap((state) => { - if (state.posts.length === 0) { - return of({ - ...state, - posts: [], - }); - } - - return combineLatest([ - combineLatest( - state.posts.map((post) => userInfoService.getUser$(post.author)) - ), - combineLatest( - state.posts.map((post) => { - if (post.content.type === "image") { - return this.getPostData$(timelineName, post.id); - } else { - return of(null); - } - }) - ), - ]).pipe( - map(([authors, datas]) => { - return { - ...state, - posts: state.posts.map((post, i) => { - const { content } = post; - - return { - ...post, - author: authors[i], - content: (() => { - if (content.type === "text") return content; - else - return { - type: "image", - data: datas[i], - } as TimelinePostImageContent; - })(), - }; - }), - }; - }) - ); - }) - ); - } - - private getCachedPostData(key: { - timelineName: string; - postId: number; - }): Promise<BlobWithEtag | null> { - return dataStorage.getItem<BlobWithEtag | null>( - `timeline.${key.timelineName}.post.${key.postId}.data` - ); - } - - private savePostData( - key: { - timelineName: string; - postId: number; - }, - data: BlobWithEtag - ): Promise<void> { - return dataStorage - .setItem<BlobWithEtag>( - `timeline.${key.timelineName}.post.${key.postId}.data`, - data - ) - .then(); - } - - private syncPostData(key: { - timelineName: string; - postId: number; - }): Promise<void> { - return this._postDataHub.getLineOrCreate(key).sync(); - } - - private _postDataHub = new DataHub< - { timelineName: string; postId: number }, - | { data: Blob; type: "cache" | "synced" | "offline" } - | { data?: undefined; type: "notexist" | "offline" } - >({ - keyToString: (key) => `${key.timelineName}.${key.postId}`, - sync: async (key, line) => { - const cache = await this.getCachedPostData(key); - if (line.value == null) { - if (cache != null) { - line.next({ type: "cache", data: cache.data }); - } - } - - if (cache == null) { - try { - const res = await getHttpTimelineClient().getPostData( - key.timelineName, - key.postId - ); - await this.savePostData(key, res); - line.next({ data: res.data, type: "synced" }); - } catch (e) { - line.next({ type: "offline" }); - throwIfNotNetworkError(e); - } - } else { - try { - const res = await getHttpTimelineClient().getPostData( - key.timelineName, - key.postId, - cache.etag - ); - if (res instanceof NotModified) { - line.next({ data: cache.data, type: "synced" }); - } else { - await this.savePostData(key, res); - line.next({ data: res.data, type: "synced" }); - } - } catch (e) { - line.next({ data: cache.data, type: "offline" }); - throwIfNotNetworkError(e); - } - } - }, - }); - - getPostData$(timelineName: string, postId: number): Observable<BlobOrStatus> { - return this._postDataHub.getObservable({ timelineName, postId }).pipe( - map((state): BlobOrStatus => state.data ?? "error"), - startWith("loading") - ); - } - - createPost( - timelineName: string, - request: TimelineCreatePostRequest - ): Observable<unknown> { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .postPost(timelineName, request, user.token) - .then(() => { - void this.syncPosts(timelineName); - }) - ); - } - - deletePost(timelineName: string, postId: number): Observable<unknown> { - const user = checkLogin(); - return from( - getHttpTimelineClient() - .deletePost(timelineName, postId, user.token) - .then(() => { - void this.syncPosts(timelineName); - }) - ); - } - - isMemberOf(username: string, timeline: TimelineInfo): boolean { - return timeline.members.findIndex((m) => m.username == username) >= 0; - } - - hasReadPermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo - ): boolean { - if (user != null && user.administrator) return true; - - const { visibility } = timeline; - if (visibility === "Public") { - return true; - } else if (visibility === "Register") { - if (user != null) return true; - } else if (visibility === "Private") { - if ( - user != null && - (user.username === timeline.owner.username || - this.isMemberOf(user.username, timeline)) - ) { - return true; - } - } - return false; - } - - hasPostPermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo - ): boolean { - if (user != null && user.administrator) return true; - - return ( - user != null && - (timeline.owner.username === user.username || - this.isMemberOf(user.username, timeline)) - ); - } - - hasManagePermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo - ): boolean { - if (user != null && user.administrator) return true; - - return user != null && user.username == timeline.owner.username; - } - - hasModifyPostPermission( - user: UserAuthInfo | null | undefined, - timeline: TimelineInfo, - post: TimelinePostInfo - ): boolean { - if (user != null && user.administrator) return true; - - return ( - user != null && - (user.username === timeline.owner.username || - user.username === post.author.username) - ); - } -} - -export const timelineService = new TimelineService(); - -const timelineNameReg = XRegExp("^[-_\\p{L}]*$", "u"); - -export function validateTimelineName(name: string): boolean { - return timelineNameReg.test(name); -} - -export function useTimelineInfo( - timelineName: string -): TimelineWithSyncStatus | undefined { - const [state, setState] = React.useState<TimelineWithSyncStatus | undefined>( - undefined - ); - React.useEffect(() => { - const subscription = timelineService - .getTimeline$(timelineName) - .subscribe((data) => { - setState(data); - }); - return () => { - subscription.unsubscribe(); - }; - }, [timelineName]); - return state; -} - -export function usePostList( - timelineName: string | null | undefined -): TimelinePostsWithSyncState | undefined { - const [state, setState] = React.useState< - TimelinePostsWithSyncState | undefined - >(undefined); - React.useEffect(() => { - if (timelineName == null) { - setState(undefined); - return; - } - - const subscription = timelineService - .getPosts$(timelineName) - .subscribe((data) => { - setState(data); - }); - return () => { - subscription.unsubscribe(); - }; - }, [timelineName]); - return state; -} - -export async function getAllCachedTimelineNames(): Promise<string[]> { - const keys = await dataStorage.keys(); - return keys - .filter( - (key) => - key.startsWith("timeline.") && (key.match(/\./g) ?? []).length === 1 - ) - .map((key) => key.substr("timeline.".length)); -} diff --git a/Timeline/ClientApp/src/app/services/user.ts b/Timeline/ClientApp/src/app/services/user.ts deleted file mode 100644 index f253fc19..00000000 --- a/Timeline/ClientApp/src/app/services/user.ts +++ /dev/null @@ -1,393 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { BehaviorSubject, Observable, from } from "rxjs"; -import { map, filter } from "rxjs/operators"; - -import { UiLogicError } from "@/common"; -import { convertError } from "@/utilities/rxjs"; - -import { HttpNetworkError, BlobWithEtag, NotModified } from "@/http/common"; -import { - getHttpTokenClient, - HttpCreateTokenBadCredentialError, -} from "@/http/token"; -import { - getHttpUserClient, - HttpUserNotExistError, - HttpUser, -} from "@/http/user"; - -import { dataStorage, throwIfNotNetworkError } from "./common"; -import { DataHub } from "./DataHub"; -import { pushAlert } from "./alert"; - -export type User = HttpUser; - -export interface UserAuthInfo { - username: string; - administrator: boolean; -} - -export interface UserWithToken extends User { - token: string; -} - -export interface LoginCredentials { - username: string; - password: string; -} - -export class BadCredentialError { - message = "login.badCredential"; -} - -const USER_STORAGE_KEY = "currentuser"; - -export class UserService { - private userSubject = new BehaviorSubject<UserWithToken | null | undefined>( - undefined - ); - - get user$(): Observable<UserWithToken | null | undefined> { - return this.userSubject; - } - - get currentUser(): UserWithToken | null | undefined { - return this.userSubject.value; - } - - async checkLoginState(): Promise<UserWithToken | null> { - if (this.currentUser !== undefined) { - console.warn("Already checked user. Can't check twice."); - } - - const savedUser = await dataStorage.getItem<UserWithToken | null>( - USER_STORAGE_KEY - ); - - if (savedUser == null) { - this.userSubject.next(null); - return null; - } - - this.userSubject.next(savedUser); - - const savedToken = savedUser.token; - try { - const res = await getHttpTokenClient().verify({ token: savedToken }); - const user: UserWithToken = { ...res.user, token: savedToken }; - await dataStorage.setItem<UserWithToken>(USER_STORAGE_KEY, user); - this.userSubject.next(user); - pushAlert({ - type: "success", - message: { - type: "i18n", - key: "user.welcomeBack", - }, - }); - return user; - } catch (error) { - if (error instanceof HttpNetworkError) { - pushAlert({ - type: "danger", - message: { type: "i18n", key: "user.verifyTokenFailedNetwork" }, - }); - return savedUser; - } else { - await dataStorage.removeItem(USER_STORAGE_KEY); - this.userSubject.next(null); - pushAlert({ - type: "danger", - message: { type: "i18n", key: "user.verifyTokenFailed" }, - }); - return null; - } - } - } - - async login( - credentials: LoginCredentials, - rememberMe: boolean - ): Promise<void> { - if (this.currentUser) { - throw new UiLogicError("Already login."); - } - try { - const res = await getHttpTokenClient().create({ - ...credentials, - expire: 30, - }); - const user: UserWithToken = { - ...res.user, - token: res.token, - }; - if (rememberMe) { - await dataStorage.setItem<UserWithToken>(USER_STORAGE_KEY, user); - } - this.userSubject.next(user); - } catch (e) { - if (e instanceof HttpCreateTokenBadCredentialError) { - throw new BadCredentialError(); - } else { - throw e; - } - } - } - - async logout(): Promise<void> { - if (this.currentUser === undefined) { - throw new UiLogicError("Please check user first."); - } - if (this.currentUser === null) { - throw new UiLogicError("No login."); - } - await dataStorage.removeItem(USER_STORAGE_KEY); - this.userSubject.next(null); - } - - changePassword( - oldPassword: string, - newPassword: string - ): Observable<unknown> { - if (this.currentUser == undefined) { - throw new UiLogicError("Not login or checked now, can't log out."); - } - const $ = from( - getHttpUserClient().changePassword( - { - oldPassword, - newPassword, - }, - this.currentUser.token - ) - ); - $.subscribe(() => { - void this.logout(); - }); - return $; - } -} - -export const userService = new UserService(); - -export function useRawUser(): UserWithToken | null | undefined { - const [user, setUser] = useState<UserWithToken | null | undefined>( - userService.currentUser - ); - useEffect(() => { - const subscription = userService.user$.subscribe((u) => setUser(u)); - return () => { - subscription.unsubscribe(); - }; - }); - return user; -} - -export function useUser(): UserWithToken | null { - const [user, setUser] = useState<UserWithToken | null>(() => { - const initUser = userService.currentUser; - if (initUser === undefined) { - throw new UiLogicError( - "This is a logic error in user module. Current user can't be undefined in useUser." - ); - } - return initUser; - }); - useEffect(() => { - const sub = userService.user$.subscribe((u) => { - if (u === undefined) { - throw new UiLogicError( - "This is a logic error in user module. User emitted can't be undefined later." - ); - } - setUser(u); - }); - return () => { - sub.unsubscribe(); - }; - }); - return user; -} - -export function useUserLoggedIn(): UserWithToken { - const user = useUser(); - if (user == null) { - throw new UiLogicError("You assert user has logged in but actually not."); - } - return user; -} - -export function checkLogin(): UserWithToken { - const user = userService.currentUser; - if (user == null) { - throw new UiLogicError("You must login to perform the operation."); - } - return user; -} - -export class UserNotExistError extends Error {} - -export class UserInfoService { - saveUser(user: HttpUser): void { - const key = user.username; - void this._userHub.optionalInitLineWithSyncAction(key, async (line) => { - await this.doSaveUser(user); - line.next({ user, type: "synced" }); - }); - } - - saveUsers(users: HttpUser[]): void { - return users.forEach((user) => this.saveUser(user)); - } - - private getCachedUser(username: string): Promise<User | null> { - return dataStorage.getItem<HttpUser | null>(`user.${username}`); - } - - private doSaveUser(user: HttpUser): Promise<void> { - return dataStorage.setItem<HttpUser>(`user.${user.username}`, user).then(); - } - - syncUser(username: string): Promise<void> { - return this._userHub.getLineOrCreate(username).sync(); - } - - private _userHub = new DataHub< - string, - | { user: User; type: "cache" | "synced" | "offline" } - | { user?: undefined; type: "notexist" | "offline" } - >({ - sync: async (key, line) => { - if (line.value == undefined) { - const cache = await this.getCachedUser(key); - if (cache != null) { - line.next({ user: cache, type: "cache" }); - } - } - - try { - const res = await getHttpUserClient().get(key); - await this.doSaveUser(res); - line.next({ user: res, type: "synced" }); - } catch (e) { - if (e instanceof HttpUserNotExistError) { - line.next({ type: "notexist" }); - } else { - const cache = await this.getCachedUser(key); - line.next({ user: cache ?? undefined, type: "offline" }); - throwIfNotNetworkError(e); - } - } - }, - }); - - getUser$(username: string): Observable<User> { - return this._userHub.getObservable(username).pipe( - map((state) => state?.user), - filter((user): user is User => user != null) - ); - } - - private getCachedAvatar(username: string): Promise<BlobWithEtag | null> { - return dataStorage.getItem<BlobWithEtag | null>(`user.${username}.avatar`); - } - - private saveAvatar(username: string, data: BlobWithEtag): Promise<void> { - return dataStorage - .setItem<BlobWithEtag>(`user.${username}.avatar`, data) - .then(); - } - - syncAvatar(username: string): Promise<void> { - return this._avatarHub.getLineOrCreate(username).sync(); - } - - private _avatarHub = new DataHub< - string, - | { data: Blob; type: "cache" | "synced" | "offline" } - | { data?: undefined; type: "notexist" | "offline" } - >({ - sync: async (key, line) => { - const cache = await this.getCachedAvatar(key); - if (line.value == null) { - if (cache != null) { - line.next({ data: cache.data, type: "cache" }); - } - } - - if (cache == null) { - try { - const avatar = await getHttpUserClient().getAvatar(key); - await this.saveAvatar(key, avatar); - line.next({ data: avatar.data, type: "synced" }); - } catch (e) { - line.next({ type: "offline" }); - throwIfNotNetworkError(e); - } - } else { - try { - const res = await getHttpUserClient().getAvatar(key, cache.etag); - if (res instanceof NotModified) { - line.next({ data: cache.data, type: "synced" }); - } else { - const avatar = res; - await this.saveAvatar(key, avatar); - line.next({ data: avatar.data, type: "synced" }); - } - } catch (e) { - line.next({ data: cache.data, type: "offline" }); - throwIfNotNetworkError(e); - } - } - }, - }); - - getAvatar$(username: string): Observable<Blob> { - return this._avatarHub.getObservable(username).pipe( - map((state) => state.data), - filter((blob): blob is Blob => blob != null) - ); - } - - getUserInfo(username: string): Observable<User> { - return from(getHttpUserClient().get(username)).pipe( - convertError(HttpUserNotExistError, UserNotExistError) - ); - } - - async setAvatar(username: string, blob: Blob): Promise<void> { - const user = checkLogin(); - await getHttpUserClient().putAvatar(username, blob, user.token); - this._avatarHub.getLine(username)?.next({ data: blob, type: "synced" }); - } - - async setNickname(username: string, nickname: string): Promise<void> { - const user = checkLogin(); - return getHttpUserClient() - .patch(username, { nickname }, user.token) - .then((user) => { - this.saveUser(user); - }); - } -} - -export const userInfoService = new UserInfoService(); - -export function useAvatar(username?: string): Blob | undefined { - const [state, setState] = React.useState<Blob | undefined>(undefined); - React.useEffect(() => { - if (username == null) { - setState(undefined); - return; - } - - const subscription = userInfoService - .getAvatar$(username) - .subscribe((blob) => { - setState(blob); - }); - return () => { - subscription.unsubscribe(); - }; - }, [username]); - return state; -} diff --git a/Timeline/ClientApp/src/app/tsconfig.json b/Timeline/ClientApp/src/app/tsconfig.json deleted file mode 100644 index 14e6327f..00000000 --- a/Timeline/ClientApp/src/app/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "lib": [ - "dom", - "dom.iterable", - "esnext" - ] - }, - "include": [ - "." - ] -} diff --git a/Timeline/ClientApp/src/app/typings.d.ts b/Timeline/ClientApp/src/app/typings.d.ts deleted file mode 100644 index 34381682..00000000 --- a/Timeline/ClientApp/src/app/typings.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -declare module "*.png" { - const content: string; - export default content; -} - -declare module "*.jpeg" { - const content: string; - export default content; -} - -declare module "*.jpg" { - const content: string; - export default content; -} - -declare module "*.gif" { - const content: string; - export default content; -} - -declare module "*.svg" { - const content: string; - export default content; -} diff --git a/Timeline/ClientApp/src/app/utilities/rxjs.ts b/Timeline/ClientApp/src/app/utilities/rxjs.ts deleted file mode 100644 index 0730b899..00000000 --- a/Timeline/ClientApp/src/app/utilities/rxjs.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { OperatorFunction } from "rxjs"; -import { catchError } from "rxjs/operators"; - -export function convertError<T, NewError>( - oldErrorType: { new (...args: never[]): unknown }, - newErrorType: { new (): NewError } -): OperatorFunction<T, T> { - return catchError((error) => { - if (error instanceof oldErrorType) { - throw new newErrorType(); - } - throw error; - }); -} diff --git a/Timeline/ClientApp/src/app/utilities/url.ts b/Timeline/ClientApp/src/app/utilities/url.ts deleted file mode 100644 index 17ead5b2..00000000 --- a/Timeline/ClientApp/src/app/utilities/url.ts +++ /dev/null @@ -1,52 +0,0 @@ -//copied from https://stackoverflow.com/questions/5999118/how-can-i-add-or-update-a-query-string-parameter -export function updateQueryString( - key: string, - value: undefined | string | null, - url: string -): string { - const re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"); - let hash; - - if (re.test(url)) { - if (typeof value !== "undefined" && value !== null) { - return url.replace(re, "$1" + key + "=" + value + "$2$3"); - } else { - hash = url.split("#"); - url = hash[0].replace(re, "$1$3").replace(/(&|\?)$/, ""); - if (typeof hash[1] !== "undefined" && hash[1] !== null) { - url += "#" + hash[1]; - } - return url; - } - } else { - if (typeof value !== "undefined" && value !== null) { - const separator = url.includes("?") ? "&" : "?"; - hash = url.split("#"); - url = hash[0] + separator + key + "=" + value; - if (typeof hash[1] !== "undefined" && hash[1] !== null) { - url += "#" + hash[1]; - } - return url; - } else { - return url; - } - } -} - -export function applyQueryParameters<T>(url: string, query: T): string { - if (query == null) return url; - - for (const [key, value] of Object.entries(query)) { - if (typeof value === "string") url = updateQueryString(key, value, url); - else if (typeof value === "number") - url = updateQueryString(key, String(value), url); - else if (typeof value === "boolean") - url = updateQueryString(key, value ? "true" : "false", url); - else if (value instanceof Date) - url = updateQueryString(key, value.toISOString(), url); - else { - console.error("Unknown query parameter type. Param: ", value); - } - } - return url; -} diff --git a/Timeline/ClientApp/src/app/views/about/about.sass b/Timeline/ClientApp/src/app/views/about/about.sass deleted file mode 100644 index 3b5840cd..00000000 --- a/Timeline/ClientApp/src/app/views/about/about.sass +++ /dev/null @@ -1,4 +0,0 @@ -.about-link-icon - @extend .mx-2 - width: 1.2em - height: 1.2em diff --git a/Timeline/ClientApp/src/app/views/about/author-avatar.png b/Timeline/ClientApp/src/app/views/about/author-avatar.png Binary files differdeleted file mode 100644 index d890d8d0..00000000 --- a/Timeline/ClientApp/src/app/views/about/author-avatar.png +++ /dev/null diff --git a/Timeline/ClientApp/src/app/views/about/github.png b/Timeline/ClientApp/src/app/views/about/github.png Binary files differdeleted file mode 100644 index ea6ff545..00000000 --- a/Timeline/ClientApp/src/app/views/about/github.png +++ /dev/null diff --git a/Timeline/ClientApp/src/app/views/about/index.tsx b/Timeline/ClientApp/src/app/views/about/index.tsx deleted file mode 100644 index e7771cec..00000000 --- a/Timeline/ClientApp/src/app/views/about/index.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React from "react"; -import { useTranslation, Trans } from "react-i18next"; - -import authorAvatarUrl from "./author-avatar.png"; -import githubLogoUrl from "./github.png"; - -const frontendCredits: { - name: string; - url: string; -}[] = [ - { - name: "reactjs", - url: "https://reactjs.org", - }, - { - name: "typescript", - url: "https://www.typescriptlang.org", - }, - { - name: "bootstrap", - url: "https://getbootstrap.com", - }, - { - name: "react-bootstrap", - url: "https://react-bootstrap.github.io", - }, - { - name: "babeljs", - url: "https://babeljs.io", - }, - { - name: "webpack", - url: "https://webpack.js.org", - }, - { - name: "sass", - url: "https://sass-lang.com", - }, - { - name: "eslint", - url: "https://eslint.org", - }, - { - name: "prettier", - url: "https://prettier.io", - }, - { - name: "pepjs", - url: "https://github.com/jquery/PEP", - }, - { - name: "react-inlinesvg", - url: "https://github.com/gilbarbara/react-inlinesvg", - }, -]; - -const backendCredits: { - name: string; - url: string; -}[] = [ - { - name: "ASP.NET Core", - url: "https://dotnet.microsoft.com/learn/aspnet/what-is-aspnet-core", - }, - { name: "sqlite", url: "https://sqlite.org" }, - { - name: "ImageSharp", - url: "https://github.com/SixLabors/ImageSharp", - }, -]; - -const AboutPage: React.FC = () => { - const { t } = useTranslation(); - - return ( - <div className="mt-appbar px-2 mb-4"> - <div className="container mt-4 py-3 shadow border border-primary rounded bg-light"> - <h4 id="author-info">{t("about.author.title")}</h4> - <div> - <div className="d-flex"> - <img - src={authorAvatarUrl} - className="align-self-start avatar large rounded-circle" - /> - <div> - <p> - <small>{t("about.author.fullname")}</small> - <span className="text-primary">杨宇千</span> - </p> - <p> - <small>{t("about.author.nickname")}</small> - <span className="text-primary">crupest</span> - </p> - <p> - <small>{t("about.author.introduction")}</small> - {t("about.author.introductionContent")} - </p> - </div> - </div> - <p> - <small>{t("about.author.links")}</small> - <a - href="https://github.com/crupest" - target="_blank" - rel="noopener noreferrer" - > - <img src={githubLogoUrl} className="about-link-icon text-body" /> - </a> - </p> - </div> - </div> - <div className="container mt-4 py-3 shadow border border-primary rounded bg-light"> - <h4>{t("about.site.title")}</h4> - <p> - <Trans i18nKey="about.site.content"> - 0<span className="text-primary">1</span>2<b>3</b>4 - <a href="#author-info">5</a>6 - </Trans> - </p> - <p> - <a - href="https://github.com/crupest/Timeline" - target="_blank" - rel="noopener noreferrer" - > - {t("about.site.repo")} - </a> - </p> - </div> - <div className="container mt-4 py-3 shadow border border-primary rounded bg-light"> - <h4>{t("about.credits.title")}</h4> - <p>{t("about.credits.content")}</p> - <p>{t("about.credits.frontend")}</p> - <ul> - {frontendCredits.map((item, index) => { - return ( - <li key={index}> - <a href={item.url} target="_blank" rel="noopener noreferrer"> - {item.name} - </a> - </li> - ); - })} - <li>...</li> - </ul> - <p>{t("about.credits.backend")}</p> - <ul> - {backendCredits.map((item, index) => { - return ( - <li key={index}> - <a href={item.url} target="_blank" rel="noopener noreferrer"> - {item.name} - </a> - </li> - ); - })} - <li>...</li> - </ul> - </div> - </div> - ); -}; - -export default AboutPage; diff --git a/Timeline/ClientApp/src/app/views/admin/Admin.tsx b/Timeline/ClientApp/src/app/views/admin/Admin.tsx deleted file mode 100644 index 9c0250e7..00000000 --- a/Timeline/ClientApp/src/app/views/admin/Admin.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { Fragment } from "react"; -import { - Redirect, - Route, - Switch, - useRouteMatch, - useHistory, -} from "react-router"; -import { Nav } from "react-bootstrap"; - -import { UserWithToken } from "@/services/user"; - -import UserAdmin from "./UserAdmin"; - -interface AdminProps { - user: UserWithToken; -} - -const Admin: React.FC<AdminProps> = (props) => { - const match = useRouteMatch(); - const history = useHistory(); - type TabNames = "users" | "more"; - - const tabName = history.location.pathname.replace(match.path + "/", ""); - - function toggle(newTab: TabNames): void { - history.push(`${match.url}/${newTab}`); - } - - const createRoute = ( - name: string, - body: React.ReactNode - ): React.ReactNode => { - return ( - <Route path={`${match.path}/${name}`}> - <div style={{ height: 56 }} className="flex-fix-length" /> - <Nav variant="tabs"> - <Nav.Item> - <Nav.Link - active={tabName === "users"} - onClick={() => { - toggle("users"); - }} - > - Users - </Nav.Link> - </Nav.Item> - <Nav.Item> - <Nav.Link - active={tabName === "more"} - onClick={() => { - toggle("more"); - }} - > - More - </Nav.Link> - </Nav.Item> - </Nav> - {body} - </Route> - ); - }; - - return ( - <Fragment> - <Switch> - <Redirect from={match.path} to={`${match.path}/users`} exact /> - {createRoute("users", <UserAdmin user={props.user} />)} - {createRoute("more", <div>More Page Works</div>)} - </Switch> - </Fragment> - ); -}; - -export default Admin; diff --git a/Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx b/Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx deleted file mode 100644 index 18b77ca8..00000000 --- a/Timeline/ClientApp/src/app/views/admin/UserAdmin.tsx +++ /dev/null @@ -1,460 +0,0 @@ -import React, { useState, useEffect } from "react"; -import axios from "axios"; -import { - ListGroup, - Row, - Col, - Dropdown, - Spinner, - Button, -} from "react-bootstrap"; - -import OperationDialog from "../common/OperationDialog"; -import { User, UserWithToken } from "@/services/user"; - -const apiBaseUrl = "/api"; - -async function fetchUserList(_token: string): Promise<User[]> { - const res = await axios.get<User[]>(`${apiBaseUrl}/users`); - return res.data; -} - -interface CreateUserInfo { - username: string; - password: string; - administrator: boolean; -} - -async function createUser(user: CreateUserInfo, token: string): Promise<User> { - const res = await axios.post<User>( - `${apiBaseUrl}/userop/createuser?token=${token}`, - user - ); - return res.data; -} - -function deleteUser(username: string, token: string): Promise<void> { - return axios.delete(`${apiBaseUrl}/users/${username}?token=${token}`); -} - -function changeUsername( - oldUsername: string, - newUsername: string, - token: string -): Promise<void> { - return axios.patch(`${apiBaseUrl}/users/${oldUsername}?token=${token}`, { - username: newUsername, - }); -} - -function changePassword( - username: string, - newPassword: string, - token: string -): Promise<void> { - return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, { - password: newPassword, - }); -} - -function changePermission( - username: string, - newPermission: boolean, - token: string -): Promise<void> { - return axios.patch(`${apiBaseUrl}/users/${username}?token=${token}`, { - administrator: newPermission, - }); -} - -const kChangeUsername = "changeusername"; -const kChangePassword = "changepassword"; -const kChangePermission = "changepermission"; -const kDelete = "delete"; - -type TChangeUsername = typeof kChangeUsername; -type TChangePassword = typeof kChangePassword; -type TChangePermission = typeof kChangePermission; -type TDelete = typeof kDelete; - -type ContextMenuItem = - | TChangeUsername - | TChangePassword - | TChangePermission - | TDelete; - -interface UserCardProps { - onContextMenu: (item: ContextMenuItem) => void; - user: User; -} - -const UserItem: React.FC<UserCardProps> = (props) => { - const user = props.user; - - const createClickCallback = (item: ContextMenuItem): (() => void) => { - return () => { - props.onContextMenu(item); - }; - }; - - return ( - <ListGroup.Item className="container"> - <Row className="align-items-center"> - <Col> - <p className="mb-0 text-primary">{user.username}</p> - <small - className={user.administrator ? "text-danger" : "text-secondary"} - > - {user.administrator ? "administrator" : "user"} - </small> - </Col> - <Col className="col-auto"> - <Dropdown> - <Dropdown.Toggle variant="warning" className="text-light"> - Manage - </Dropdown.Toggle> - <Dropdown.Menu> - <Dropdown.Item onClick={createClickCallback(kChangeUsername)}> - Change Username - </Dropdown.Item> - <Dropdown.Item onClick={createClickCallback(kChangePassword)}> - Change Password - </Dropdown.Item> - <Dropdown.Item onClick={createClickCallback(kChangePermission)}> - Change Permission - </Dropdown.Item> - <Dropdown.Item - className="text-danger" - onClick={createClickCallback(kDelete)} - > - Delete - </Dropdown.Item> - </Dropdown.Menu> - </Dropdown> - </Col> - </Row> - </ListGroup.Item> - ); -}; - -interface DialogProps { - open: boolean; - close: () => void; -} - -interface CreateUserDialogProps extends DialogProps { - process: (user: CreateUserInfo) => Promise<void>; -} - -const CreateUserDialog: React.FC<CreateUserDialogProps> = (props) => { - return ( - <OperationDialog - title="Create" - titleColor="create" - inputPrompt="You are creating a new user." - inputScheme={[ - { type: "text", label: "Username" }, - { type: "text", label: "Password" }, - { type: "bool", label: "Administrator" }, - ]} - onProcess={([username, password, administrator]) => - props.process({ - username: username as string, - password: password as string, - administrator: administrator as boolean, - }) - } - close={props.close} - open={props.open} - /> - ); -}; - -const UsernameLabel: React.FC = (props) => { - return <span style={{ color: "blue" }}>{props.children}</span>; -}; - -interface UserDeleteDialogProps extends DialogProps { - username: string; - process: () => Promise<void>; -} - -const UserDeleteDialog: React.FC<UserDeleteDialogProps> = (props) => { - return ( - <OperationDialog - open={props.open} - close={props.close} - title="Dangerous" - titleColor="dangerous" - inputPrompt={() => ( - <> - {"You are deleting user "} - <UsernameLabel>{props.username}</UsernameLabel> - {" !"} - </> - )} - onProcess={props.process} - /> - ); -}; - -interface UserModifyDialogProps<T> extends DialogProps { - username: string; - process: (value: T) => Promise<void>; -} - -const UserChangeUsernameDialog: React.FC<UserModifyDialogProps<string>> = ( - props -) => { - return ( - <OperationDialog - open={props.open} - close={props.close} - title="Caution" - titleColor="dangerous" - inputPrompt={() => ( - <> - {"You are change the username of user "} - <UsernameLabel>{props.username}</UsernameLabel> - {" !"} - </> - )} - inputScheme={[{ type: "text", label: "New Username" }]} - onProcess={([newUsername]) => { - return props.process(newUsername as string); - }} - /> - ); -}; - -const UserChangePasswordDialog: React.FC<UserModifyDialogProps<string>> = ( - props -) => { - return ( - <OperationDialog - open={props.open} - close={props.close} - title="Caution" - titleColor="dangerous" - inputPrompt={() => ( - <> - {"You are change the password of user "} - <UsernameLabel>{props.username}</UsernameLabel> - {" !"} - </> - )} - inputScheme={[{ type: "text", label: "New Password" }]} - onProcess={([newPassword]) => { - return props.process(newPassword as string); - }} - /> - ); -}; - -interface UserChangePermissionDialogProps extends DialogProps { - username: string; - newPermission: boolean; - process: () => Promise<void>; -} - -const UserChangePermissionDialog: React.FC<UserChangePermissionDialogProps> = ( - props -) => { - return ( - <OperationDialog - open={props.open} - close={props.close} - title="Caution" - titleColor="dangerous" - inputPrompt={() => ( - <> - {"You are change user "} - <UsernameLabel>{props.username}</UsernameLabel> - {" to "} - <span style={{ color: "orange" }}> - {props.newPermission ? "administrator" : "normal user"} - </span> - {" !"} - </> - )} - onProcess={props.process} - /> - ); -}; - -interface UserAdminProps { - user: UserWithToken; -} - -const UserAdmin: React.FC<UserAdminProps> = (props) => { - type DialogInfo = - | null - | { - type: "create"; - } - | { type: TDelete; username: string } - | { - type: TChangeUsername; - username: string; - } - | { - type: TChangePassword; - username: string; - } - | { - type: TChangePermission; - username: string; - newPermission: boolean; - }; - - const [users, setUsers] = useState<User[] | null>(null); - const [dialog, setDialog] = useState<DialogInfo>(null); - - const token = props.user.token; - - useEffect(() => { - let subscribe = true; - void fetchUserList(props.user.token).then((us) => { - if (subscribe) { - setUsers(us); - } - }); - return () => { - subscribe = false; - }; - }, [props.user]); - - let dialogNode: React.ReactNode; - if (dialog) - switch (dialog.type) { - case "create": - dialogNode = ( - <CreateUserDialog - open - close={() => setDialog(null)} - process={async (user) => { - const u = await createUser(user, token); - setUsers((oldUsers) => [...(oldUsers ?? []), u]); - }} - /> - ); - break; - case "delete": - dialogNode = ( - <UserDeleteDialog - open - close={() => setDialog(null)} - username={dialog.username} - process={async () => { - await deleteUser(dialog.username, token); - setUsers((oldUsers) => - (oldUsers ?? []).filter((u) => u.username !== dialog.username) - ); - }} - /> - ); - break; - case kChangeUsername: - dialogNode = ( - <UserChangeUsernameDialog - open - close={() => setDialog(null)} - username={dialog.username} - process={async (newUsername) => { - await changeUsername(dialog.username, newUsername, token); - setUsers((oldUsers) => { - const users = (oldUsers ?? []).slice(); - const findedUser = users.find( - (u) => u.username === dialog.username - ); - if (findedUser) findedUser.username = newUsername; - return users; - }); - }} - /> - ); - break; - case kChangePassword: - dialogNode = ( - <UserChangePasswordDialog - open - close={() => setDialog(null)} - username={dialog.username} - process={async (newPassword) => { - await changePassword(dialog.username, newPassword, token); - }} - /> - ); - break; - case kChangePermission: { - const newPermission = dialog.newPermission; - dialogNode = ( - <UserChangePermissionDialog - open - close={() => setDialog(null)} - username={dialog.username} - newPermission={newPermission} - process={async () => { - await changePermission(dialog.username, newPermission, token); - setUsers((oldUsers) => { - const users = (oldUsers ?? []).slice(); - const findedUser = users.find( - (u) => u.username === dialog.username - ); - if (findedUser) findedUser.administrator = newPermission; - return users; - }); - }} - /> - ); - break; - } - } - - if (users) { - const userComponents = users.map((user) => { - return ( - <UserItem - key={user.username} - user={user} - onContextMenu={(item) => { - setDialog( - item === kChangePermission - ? { - type: kChangePermission, - username: user.username, - newPermission: !user.administrator, - } - : { - type: item, - username: user.username, - } - ); - }} - /> - ); - }); - - return ( - <> - <Button - variant="success" - onClick={() => - setDialog({ - type: "create", - }) - } - className="align-self-end" - > - Create User - </Button> - {userComponents} - {dialogNode} - </> - ); - } else { - return <Spinner animation="border" />; - } -}; - -export default UserAdmin; diff --git a/Timeline/ClientApp/src/app/views/common/AppBar.tsx b/Timeline/ClientApp/src/app/views/common/AppBar.tsx deleted file mode 100644 index ee4ead8f..00000000 --- a/Timeline/ClientApp/src/app/views/common/AppBar.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { LinkContainer } from "react-router-bootstrap"; -import { Navbar, Nav } from "react-bootstrap"; - -import { useUser, useAvatar } from "@/services/user"; - -import TimelineLogo from "./TimelineLogo"; -import BlobImage from "./BlobImage"; - -const AppBar: React.FC = (_) => { - const user = useUser(); - const avatar = useAvatar(user?.username); - - const { t } = useTranslation(); - - const isAdministrator = user && user.administrator; - - return ( - <Navbar bg="primary" variant="dark" expand="md" sticky="top"> - <LinkContainer to="/"> - <Navbar.Brand className="d-flex align-items-center"> - <TimelineLogo style={{ height: "1em" }} /> - Timeline - </Navbar.Brand> - </LinkContainer> - - <Navbar.Toggle /> - <Navbar.Collapse> - <Nav className="mr-auto"> - <LinkContainer to="/settings"> - <Nav.Link>{t("nav.settings")}</Nav.Link> - </LinkContainer> - - <LinkContainer to="/about"> - <Nav.Link>{t("nav.about")}</Nav.Link> - </LinkContainer> - - {isAdministrator && ( - <LinkContainer to="/admin"> - <Nav.Link>Administration</Nav.Link> - </LinkContainer> - )} - </Nav> - <Nav className="ml-auto mr-2"> - {user != null ? ( - <LinkContainer to={`/users/${user.username}`}> - <BlobImage - className="avatar small rounded-circle bg-white" - blob={avatar} - /> - </LinkContainer> - ) : ( - <LinkContainer to="/login"> - <Nav.Link>{t("nav.login")}</Nav.Link> - </LinkContainer> - )} - </Nav> - </Navbar.Collapse> - </Navbar> - ); -}; - -export default AppBar; diff --git a/Timeline/ClientApp/src/app/views/common/BlobImage.tsx b/Timeline/ClientApp/src/app/views/common/BlobImage.tsx deleted file mode 100644 index 0dd25c52..00000000 --- a/Timeline/ClientApp/src/app/views/common/BlobImage.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from "react"; - -const BlobImage: React.FC< - Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src"> & { - blob?: Blob | unknown; - } -> = (props) => { - const { blob, ...otherProps } = props; - - const [url, setUrl] = React.useState<string | undefined>(undefined); - - React.useEffect(() => { - if (blob instanceof Blob) { - const url = URL.createObjectURL(blob); - setUrl(url); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setUrl(undefined); - } - }, [blob]); - - return <img {...otherProps} src={url} />; -}; - -export default BlobImage; diff --git a/Timeline/ClientApp/src/app/views/common/ImageCropper.tsx b/Timeline/ClientApp/src/app/views/common/ImageCropper.tsx deleted file mode 100644 index b9db8b99..00000000 --- a/Timeline/ClientApp/src/app/views/common/ImageCropper.tsx +++ /dev/null @@ -1,306 +0,0 @@ -import React from "react"; -import clsx from "clsx"; - -import { UiLogicError } from "@/common"; - -export interface Clip { - left: number; - top: number; - width: number; -} - -interface NormailizedClip extends Clip { - height: number; -} - -interface ImageInfo { - width: number; - height: number; - landscape: boolean; - ratio: number; - maxClipWidth: number; - maxClipHeight: number; -} - -interface ImageCropperSavedState { - clip: NormailizedClip; - x: number; - y: number; - pointerId: number; -} - -export interface ImageCropperProps { - clip: Clip | null; - imageUrl: string; - onChange: (clip: Clip) => void; - imageElementCallback?: (element: HTMLImageElement | null) => void; - className?: string; -} - -const ImageCropper = (props: ImageCropperProps): React.ReactElement => { - const { clip, imageUrl, onChange, imageElementCallback, className } = props; - - const [oldState, setOldState] = React.useState<ImageCropperSavedState | null>( - null - ); - const [imageInfo, setImageInfo] = React.useState<ImageInfo | null>(null); - - const normalizeClip = (c: Clip | null | undefined): NormailizedClip => { - if (c == null) { - return { left: 0, top: 0, width: 0, height: 0 }; - } - - return { - left: c.left || 0, - top: c.top || 0, - width: c.width || 0, - height: imageInfo != null ? (c.width || 0) / imageInfo.ratio : 0, - }; - }; - - const c = normalizeClip(clip); - - const imgElementRef = React.useRef<HTMLImageElement | null>(null); - - const onImageRef = React.useCallback( - (e: HTMLImageElement | null) => { - imgElementRef.current = e; - if (imageElementCallback != null && e == null) { - imageElementCallback(null); - } - }, - [imageElementCallback] - ); - - const onImageLoad = React.useCallback( - (e: React.SyntheticEvent<HTMLImageElement>) => { - const img = e.currentTarget; - const landscape = img.naturalWidth >= img.naturalHeight; - - const info = { - width: img.naturalWidth, - height: img.naturalHeight, - landscape, - ratio: img.naturalHeight / img.naturalWidth, - maxClipWidth: landscape ? img.naturalHeight / img.naturalWidth : 1, - maxClipHeight: landscape ? 1 : img.naturalWidth / img.naturalHeight, - }; - setImageInfo(info); - onChange({ left: 0, top: 0, width: info.maxClipWidth }); - if (imageElementCallback != null) { - imageElementCallback(img); - } - }, - [onChange, imageElementCallback] - ); - - const onPointerDown = React.useCallback( - (e: React.PointerEvent) => { - if (oldState != null) return; - e.currentTarget.setPointerCapture(e.pointerId); - setOldState({ - x: e.clientX, - y: e.clientY, - clip: c, - pointerId: e.pointerId, - }); - }, - [oldState, c] - ); - - const onPointerUp = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null || oldState.pointerId !== e.pointerId) return; - e.currentTarget.releasePointerCapture(e.pointerId); - setOldState(null); - }, - [oldState] - ); - - const onPointerMove = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null) return; - - const oldClip = oldState.clip; - - const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y }; - - const { current: imgElement } = imgElementRef; - - if (imgElement == null) throw new UiLogicError("Image element is null."); - - const moveRatio = { - x: movement.x / imgElement.width, - y: movement.y / imgElement.height, - }; - - const newRatio = { - x: oldClip.left + moveRatio.x, - y: oldClip.top + moveRatio.y, - }; - if (newRatio.x < 0) { - newRatio.x = 0; - } else if (newRatio.x > 1 - oldClip.width) { - newRatio.x = 1 - oldClip.width; - } - if (newRatio.y < 0) { - newRatio.y = 0; - } else if (newRatio.y > 1 - oldClip.height) { - newRatio.y = 1 - oldClip.height; - } - - onChange({ left: newRatio.x, top: newRatio.y, width: oldClip.width }); - }, - [oldState, onChange] - ); - - const onHandlerPointerMove = React.useCallback( - (e: React.PointerEvent) => { - if (oldState == null) return; - - const oldClip = oldState.clip; - - const movement = { x: e.clientX - oldState.x, y: e.clientY - oldState.y }; - - const ratio = imageInfo == null ? 1 : imageInfo.ratio; - - const { current: imgElement } = imgElementRef; - - if (imgElement == null) throw new UiLogicError("Image element is null."); - - const moveRatio = { - x: movement.x / imgElement.width, - y: movement.x / imgElement.width / ratio, - }; - - const newRatio = { - x: oldClip.width + moveRatio.x, - y: oldClip.height + moveRatio.y, - }; - - const maxRatio = { - x: Math.min(1 - oldClip.left, newRatio.x), - y: Math.min(1 - oldClip.top, newRatio.y), - }; - - const maxWidthRatio = Math.min(maxRatio.x, maxRatio.y * ratio); - - let newWidth; - if (newRatio.x < 0) { - newWidth = 0; - } else if (newRatio.x > maxWidthRatio) { - newWidth = maxWidthRatio; - } else { - newWidth = newRatio.x; - } - - onChange({ left: oldClip.left, top: oldClip.top, width: newWidth }); - }, - [imageInfo, oldState, onChange] - ); - - const toPercentage = (n: number): string => `${n}%`; - - // fuck!!! I just can't find a better way to implement this in pure css - const containerStyle: React.CSSProperties = (() => { - if (imageInfo == null) { - return { width: "100%", paddingTop: "100%", height: 0 }; - } else { - if (imageInfo.ratio > 1) { - return { - width: toPercentage(100 / imageInfo.ratio), - paddingTop: "100%", - height: 0, - }; - } else { - return { - width: "100%", - paddingTop: toPercentage(100 * imageInfo.ratio), - height: 0, - }; - } - } - })(); - - return ( - <div - className={clsx("image-cropper-container", className)} - style={containerStyle} - > - <img ref={onImageRef} src={imageUrl} onLoad={onImageLoad} alt="to crop" /> - <div className="image-cropper-mask-container"> - <div - className="image-cropper-mask" - touch-action="none" - style={{ - left: toPercentage(c.left * 100), - top: toPercentage(c.top * 100), - width: toPercentage(c.width * 100), - height: toPercentage(c.height * 100), - }} - onPointerMove={onPointerMove} - onPointerDown={onPointerDown} - onPointerUp={onPointerUp} - /> - </div> - <div - className="image-cropper-handler" - touch-action="none" - style={{ - left: `calc(${(c.left + c.width) * 100}% - 15px)`, - top: `calc(${(c.top + c.height) * 100}% - 15px)`, - }} - onPointerMove={onHandlerPointerMove} - onPointerDown={onPointerDown} - onPointerUp={onPointerUp} - /> - </div> - ); -}; - -export default ImageCropper; - -export function applyClipToImage( - image: HTMLImageElement, - clip: Clip, - mimeType: string -): Promise<Blob> { - return new Promise((resolve, reject) => { - const naturalSize = { - width: image.naturalWidth, - height: image.naturalHeight, - }; - const clipArea = { - x: naturalSize.width * clip.left, - y: naturalSize.height * clip.top, - length: naturalSize.width * clip.width, - }; - - const canvas = document.createElement("canvas"); - canvas.width = clipArea.length; - canvas.height = clipArea.length; - const context = canvas.getContext("2d"); - - if (context == null) throw new Error("Failed to create context."); - - context.drawImage( - image, - clipArea.x, - clipArea.y, - clipArea.length, - clipArea.length, - 0, - 0, - clipArea.length, - clipArea.length - ); - - canvas.toBlob((blob) => { - if (blob == null) { - reject(new Error("canvas.toBlob returns null")); - } else { - resolve(blob); - } - }, mimeType); - }); -} diff --git a/Timeline/ClientApp/src/app/views/common/LoadingButton.tsx b/Timeline/ClientApp/src/app/views/common/LoadingButton.tsx deleted file mode 100644 index 154334a7..00000000 --- a/Timeline/ClientApp/src/app/views/common/LoadingButton.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; -import { Button, ButtonProps, Spinner } from "react-bootstrap"; - -const LoadingButton: React.FC<{ loading?: boolean } & ButtonProps> = ({ - loading, - variant, - disabled, - ...otherProps -}) => { - return ( - <Button - variant={variant != null ? `outline-${variant}` : "outline-primary"} - disabled={disabled || loading} - {...otherProps} - > - {otherProps.children} - {loading ? ( - <Spinner - className="ml-1" - variant={variant} - animation="grow" - size="sm" - /> - ) : null} - </Button> - ); -}; - -export default LoadingButton; diff --git a/Timeline/ClientApp/src/app/views/common/LoadingPage.tsx b/Timeline/ClientApp/src/app/views/common/LoadingPage.tsx deleted file mode 100644 index 590fafa0..00000000 --- a/Timeline/ClientApp/src/app/views/common/LoadingPage.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import { Spinner } from "react-bootstrap"; - -const LoadingPage: React.FC = () => { - return ( - <div className="position-fixed w-100 h-100 d-flex justify-content-center align-items-center"> - <Spinner variant="primary" animation="border" /> - </div> - ); -}; - -export default LoadingPage; diff --git a/Timeline/ClientApp/src/app/views/common/OperationDialog.tsx b/Timeline/ClientApp/src/app/views/common/OperationDialog.tsx deleted file mode 100644 index 841392a6..00000000 --- a/Timeline/ClientApp/src/app/views/common/OperationDialog.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { Form, Button, Modal } from "react-bootstrap"; - -import { UiLogicError } from "@/common"; - -import LoadingButton from "./LoadingButton"; - -interface DefaultErrorPromptProps { - error?: string; -} - -const DefaultErrorPrompt: React.FC<DefaultErrorPromptProps> = (props) => { - const { t } = useTranslation(); - - let result = <p className="text-danger">{t("operationDialog.error")}</p>; - - if (props.error != null) { - result = ( - <> - {result} - <p className="text-danger">{props.error}</p> - </> - ); - } - - return result; -}; - -export type OperationInputOptionalError = undefined | null | string; - -export interface OperationInputErrorInfo { - [index: number]: OperationInputOptionalError; -} - -export type OperationInputValidator<TValue> = ( - value: TValue, - values: (string | boolean)[] -) => OperationInputOptionalError | OperationInputErrorInfo; - -export interface OperationTextInputInfo { - type: "text"; - password?: boolean; - label?: string; - initValue?: string; - textFieldProps?: Omit< - React.InputHTMLAttributes<HTMLInputElement>, - "type" | "value" | "onChange" | "aria-relevant" - >; - helperText?: string; - validator?: OperationInputValidator<string>; -} - -export interface OperationBoolInputInfo { - type: "bool"; - label: string; - initValue?: boolean; -} - -export interface OperationSelectInputInfoOption { - value: string; - label: string; - icon?: React.ReactElement; -} - -export interface OperationSelectInputInfo { - type: "select"; - label: string; - options: OperationSelectInputInfoOption[]; - initValue?: string; -} - -export type OperationInputInfo = - | OperationTextInputInfo - | OperationBoolInputInfo - | OperationSelectInputInfo; - -interface OperationResult { - type: "success" | "failure"; - data: unknown; -} - -interface OperationDialogProps { - open: boolean; - close: () => void; - title: React.ReactNode; - titleColor?: "default" | "dangerous" | "create" | string; - onProcess: (inputs: (string | boolean)[]) => Promise<unknown>; - inputScheme?: OperationInputInfo[]; - inputPrompt?: string | (() => React.ReactNode); - processPrompt?: () => React.ReactNode; - successPrompt?: (data: unknown) => React.ReactNode; - failurePrompt?: (error: unknown) => React.ReactNode; - onSuccessAndClose?: () => void; -} - -const OperationDialog: React.FC<OperationDialogProps> = (props) => { - const inputScheme = props.inputScheme ?? []; - - const { t } = useTranslation(); - - type Step = "input" | "process" | OperationResult; - const [step, setStep] = useState<Step>("input"); - const [values, setValues] = useState<(boolean | string)[]>( - inputScheme.map((i) => { - if (i.type === "bool") { - return i.initValue ?? false; - } else if (i.type === "text" || i.type === "select") { - return i.initValue ?? ""; - } else { - throw new UiLogicError("Unknown input scheme."); - } - }) - ); - const [inputError, setInputError] = useState<OperationInputErrorInfo>({}); - - const close = (): void => { - if (step !== "process") { - props.close(); - if ( - typeof step === "object" && - step.type === "success" && - props.onSuccessAndClose - ) { - props.onSuccessAndClose(); - } - } else { - console.log("Attempt to close modal when processing."); - } - }; - - const onConfirm = (): void => { - setStep("process"); - props.onProcess(values).then( - (d: unknown) => { - setStep({ - type: "success", - data: d, - }); - }, - (e: unknown) => { - setStep({ - type: "failure", - data: e, - }); - } - ); - }; - - let body: React.ReactNode; - if (step === "input" || step === "process") { - const process = step === "process"; - - let inputPrompt = - typeof props.inputPrompt === "function" - ? props.inputPrompt() - : props.inputPrompt; - inputPrompt = <h6>{inputPrompt}</h6>; - - const updateValue = ( - index: number, - newValue: string | boolean - ): (string | boolean)[] => { - const oldValues = values; - const newValues = oldValues.slice(); - newValues[index] = newValue; - setValues(newValues); - return newValues; - }; - - const testErrorInfo = (errorInfo: OperationInputErrorInfo): boolean => { - for (let i = 0; i < inputScheme.length; i++) { - if (inputScheme[i].type === "text" && errorInfo[i] != null) { - return true; - } - } - return false; - }; - - const calculateError = ( - oldError: OperationInputErrorInfo, - index: number, - newError: OperationInputOptionalError | OperationInputErrorInfo - ): OperationInputErrorInfo => { - if (newError === undefined) { - return oldError; - } else if (newError === null || typeof newError === "string") { - return { ...oldError, [index]: newError }; - } else { - const newInputError: OperationInputErrorInfo = { ...oldError }; - for (const [index, error] of Object.entries(newError)) { - if (error !== undefined) { - newInputError[+index] = error as OperationInputOptionalError; - } - } - return newInputError; - } - }; - - const validateAll = (): boolean => { - let newInputError = inputError; - for (let i = 0; i < inputScheme.length; i++) { - const item = inputScheme[i]; - if (item.type === "text") { - newInputError = calculateError( - newInputError, - i, - item.validator?.(values[i] as string, values) - ); - } - } - const result = !testErrorInfo(newInputError); - setInputError(newInputError); - return result; - }; - - body = ( - <> - <Modal.Body> - {inputPrompt} - {inputScheme.map((item, index) => { - const value = values[index]; - const error: string | undefined = ((e) => - typeof e === "string" ? t(e) : undefined)(inputError?.[index]); - - if (item.type === "text") { - return ( - <Form.Group key={index}> - {item.label && <Form.Label>{t(item.label)}</Form.Label>} - <Form.Control - type={item.password === true ? "password" : "text"} - value={value as string} - onChange={(e) => { - const v = e.target.value; - const newValues = updateValue(index, v); - setInputError( - calculateError( - inputError, - index, - item.validator?.(v, newValues) - ) - ); - }} - isInvalid={error != null} - disabled={process} - /> - {error != null && ( - <Form.Control.Feedback type="invalid"> - {error} - </Form.Control.Feedback> - )} - {item.helperText && ( - <Form.Text>{t(item.helperText)}</Form.Text> - )} - </Form.Group> - ); - } else if (item.type === "bool") { - return ( - <Form.Group key={index}> - <Form.Check<"input"> - type="checkbox" - checked={value as boolean} - onChange={(event) => { - updateValue(index, event.currentTarget.checked); - }} - label={t(item.label)} - disabled={process} - /> - </Form.Group> - ); - } else if (item.type === "select") { - return ( - <Form.Group key={index}> - <Form.Label>{t(item.label)}</Form.Label> - <Form.Control - as="select" - value={value as string} - onChange={(event) => { - updateValue(index, event.target.value); - }} - disabled={process} - > - {item.options.map((option, i) => { - return ( - <option value={option.value} key={i}> - {option.icon} - {t(option.label)} - </option> - ); - })} - </Form.Control> - </Form.Group> - ); - } - })} - </Modal.Body> - <Modal.Footer> - <Button variant="outline-secondary" onClick={close}> - {t("operationDialog.cancel")} - </Button> - <LoadingButton - variant="primary" - loading={process} - disabled={testErrorInfo(inputError)} - onClick={() => { - if (validateAll()) { - onConfirm(); - } - }} - > - {t("operationDialog.confirm")} - </LoadingButton> - </Modal.Footer> - </> - ); - } else { - let content: React.ReactNode; - const result = step; - if (result.type === "success") { - content = - props.successPrompt?.(result.data) ?? t("operationDialog.success"); - if (typeof content === "string") - content = <p className="text-success">{content}</p>; - } else { - content = props.failurePrompt?.(result.data) ?? <DefaultErrorPrompt />; - if (typeof content === "string") - content = <DefaultErrorPrompt error={content} />; - } - body = ( - <> - <Modal.Body>{content}</Modal.Body> - <Modal.Footer> - <Button variant="primary" onClick={close}> - {t("operationDialog.ok")} - </Button> - </Modal.Footer> - </> - ); - } - - const title = typeof props.title === "string" ? t(props.title) : props.title; - - return ( - <Modal show={props.open} onHide={close}> - <Modal.Header - className={ - props.titleColor != null - ? "text-" + - (props.titleColor === "create" - ? "success" - : props.titleColor === "dangerous" - ? "danger" - : props.titleColor) - : undefined - } - > - {title} - </Modal.Header> - {body} - </Modal> - ); -}; - -export default OperationDialog; diff --git a/Timeline/ClientApp/src/app/views/common/SearchInput.tsx b/Timeline/ClientApp/src/app/views/common/SearchInput.tsx deleted file mode 100644 index 9833d515..00000000 --- a/Timeline/ClientApp/src/app/views/common/SearchInput.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useCallback } from "react"; -import clsx from "clsx"; -import { useTranslation } from "react-i18next"; -import { Spinner, Form, Button } from "react-bootstrap"; - -export interface SearchInputProps { - value: string; - onChange: (value: string) => void; - onButtonClick: () => void; - className?: string; - loading?: boolean; - buttonText?: string; - placeholder?: string; - additionalButton?: React.ReactNode; -} - -const SearchInput: React.FC<SearchInputProps> = (props) => { - const { onChange, onButtonClick } = props; - - const { t } = useTranslation(); - - const onInputChange = useCallback( - (event: React.ChangeEvent<HTMLInputElement>): void => { - onChange(event.currentTarget.value); - }, - [onChange] - ); - - const onInputKeyPress = useCallback( - (event: React.KeyboardEvent<HTMLInputElement>): void => { - if (event.key === "Enter") { - onButtonClick(); - } - }, - [onButtonClick] - ); - - return ( - <Form inline className={clsx("my-2", props.className)}> - <Form.Control - className="mr-sm-2 flex-grow-1" - value={props.value} - onChange={onInputChange} - onKeyPress={onInputKeyPress} - placeholder={props.placeholder} - /> - <div className="mt-2 mt-sm-0 order-sm-last ml-sm-3"> - {props.additionalButton} - </div> - <div className="mt-2 mt-sm-0 ml-auto ml-sm-0"> - {props.loading ? ( - <Spinner variant="primary" animation="border" /> - ) : ( - <Button variant="outline-primary" onClick={props.onButtonClick}> - {props.buttonText ?? t("search")} - </Button> - )} - </div> - </Form> - ); -}; - -export default SearchInput; diff --git a/Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx b/Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx deleted file mode 100644 index 27d188fc..00000000 --- a/Timeline/ClientApp/src/app/views/common/TimelineLogo.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { SVGAttributes } from "react"; - -export interface TimelineLogoProps extends SVGAttributes<SVGElement> { - color?: string; -} - -const TimelineLogo: React.FC<TimelineLogoProps> = (props) => { - const { color, ...forwardProps } = props; - const coercedColor = color ?? "currentcolor"; - return ( - <svg - className={props.className} - viewBox="0 0 100 100" - fill="none" - strokeWidth="12" - stroke={coercedColor} - {...forwardProps} - > - <line x1="50" y1="0" x2="50" y2="25" /> - <circle cx="50" cy="50" r="22" /> - <line x1="50" y1="75" x2="50" y2="100" /> - </svg> - ); -}; - -export default TimelineLogo; diff --git a/Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx b/Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx deleted file mode 100644 index 29f6a69f..00000000 --- a/Timeline/ClientApp/src/app/views/common/UserTimelineLogo.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { SVGAttributes } from "react"; - -export interface UserTimelineLogoProps extends SVGAttributes<SVGElement> { - color?: string; -} - -const UserTimelineLogo: React.FC<UserTimelineLogoProps> = (props) => { - const { color, ...forwardProps } = props; - const coercedColor = color ?? "currentcolor"; - - return ( - <svg viewBox="0 0 100 100" {...forwardProps}> - <g fill="none" stroke={coercedColor} strokeWidth="12"> - <line x1="50" x2="50" y1="0" y2="25" /> - <circle cx="50" cy="50" r="22" /> - <line x1="50" x2="50" y1="75" y2="100" /> - </g> - <g fill={color}> - <circle cx="85" cy="75" r="10" /> - <path d="m70,100c0,0 15,-30 30,0.25" /> - </g> - </svg> - ); -}; - -export default UserTimelineLogo; diff --git a/Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx b/Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx deleted file mode 100644 index c74f18e2..00000000 --- a/Timeline/ClientApp/src/app/views/common/alert/AlertHost.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useCallback } from "react"; -import without from "lodash/without"; -import concat from "lodash/concat"; -import { useTranslation } from "react-i18next"; -import { Alert } from "react-bootstrap"; - -import { - alertService, - AlertInfoEx, - kAlertHostId, - AlertInfo, -} from "@/services/alert"; - -interface AutoCloseAlertProps { - alert: AlertInfo; - close: () => void; -} - -export const AutoCloseAlert: React.FC<AutoCloseAlertProps> = (props) => { - const { alert } = props; - const { dismissTime } = alert; - - const { t } = useTranslation(); - - React.useEffect(() => { - const tag = - dismissTime === "never" - ? null - : typeof dismissTime === "number" - ? window.setTimeout(props.close, dismissTime) - : window.setTimeout(props.close, 5000); - return () => { - if (tag != null) { - window.clearTimeout(tag); - } - }; - }, [dismissTime, props.close]); - - return ( - <Alert - className="m-3" - variant={alert.type ?? "primary"} - onClose={props.close} - dismissible - > - {(() => { - const { message } = alert; - if (typeof message === "function") { - const Message = message; - return <Message />; - } else if (typeof message === "object" && message.type === "i18n") { - return t(message.key); - } else return alert.message; - })()} - </Alert> - ); -}; - -// oh what a bad name! -interface AlertInfoExEx extends AlertInfoEx { - close: () => void; -} - -const AlertHost: React.FC = () => { - const [alerts, setAlerts] = React.useState<AlertInfoExEx[]>([]); - - // react guarantee that state setters are stable, so we don't need to add it to dependency list - - const consume = useCallback((alert: AlertInfoEx): void => { - const alertEx: AlertInfoExEx = { - ...alert, - close: () => { - setAlerts((oldAlerts) => { - return without(oldAlerts, alertEx); - }); - }, - }; - setAlerts((oldAlerts) => { - return concat(oldAlerts, alertEx); - }); - }, []); - - React.useEffect(() => { - alertService.registerConsumer(consume); - return () => { - alertService.unregisterConsumer(consume); - }; - }, [consume]); - - return ( - <div id={kAlertHostId} className="alert-container"> - {alerts.map((alert) => { - return ( - <AutoCloseAlert key={alert.id} alert={alert} close={alert.close} /> - ); - })} - </div> - ); -}; - -export default AlertHost; diff --git a/Timeline/ClientApp/src/app/views/common/alert/alert.sass b/Timeline/ClientApp/src/app/views/common/alert/alert.sass deleted file mode 100644 index 5b6e65c2..00000000 --- a/Timeline/ClientApp/src/app/views/common/alert/alert.sass +++ /dev/null @@ -1,15 +0,0 @@ -.alert-container - position: fixed - z-index: $zindex-popover - -@include media-breakpoint-up(sm) - .alert-container - bottom: 0 - right: 0 - -@include media-breakpoint-down(sm) - .alert-container - bottom: 0 - right: 0 - left: 0 - text-align: center diff --git a/Timeline/ClientApp/src/app/views/common/common.sass b/Timeline/ClientApp/src/app/views/common/common.sass deleted file mode 100644 index 15d34d7c..00000000 --- a/Timeline/ClientApp/src/app/views/common/common.sass +++ /dev/null @@ -1,33 +0,0 @@ -.image-cropper-container - position: relative - box-sizing: border-box - user-select: none - -.image-cropper-container img - position: absolute - left: 0 - top: 0 - width: 100% - height: 100% - -.image-cropper-mask-container - position: absolute - left: 0 - top: 0 - right: 0 - bottom: 0 - overflow: hidden - -.image-cropper-mask - position: absolute - box-shadow: 0 0 0 10000px rgba(255, 255, 255, 80%) - touch-action: none - -.image-cropper-handler - position: absolute - width: 26px - height: 26px - border: black solid 2px - border-radius: 50% - background: white - touch-action: none diff --git a/Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx b/Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx deleted file mode 100644 index dcd39cbe..00000000 --- a/Timeline/ClientApp/src/app/views/home/BoardWithUser.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react"; -import { Row, Col } from "react-bootstrap"; -import { useTranslation } from "react-i18next"; - -import { UserWithToken } from "@/services/user"; -import { TimelineInfo } from "@/services/timeline"; -import { getHttpTimelineClient } from "@/http/timeline"; - -import TimelineBoard from "./TimelineBoard"; -import OfflineBoard from "./OfflineBoard"; - -const BoardWithUser: React.FC<{ user: UserWithToken }> = ({ user }) => { - const { t } = useTranslation(); - - const [ownTimelines, setOwnTimelines] = React.useState< - TimelineInfo[] | "offline" | "loading" - >("loading"); - const [joinTimelines, setJoinTimelines] = React.useState< - TimelineInfo[] | "offline" | "loading" - >("loading"); - - React.useEffect(() => { - let subscribe = true; - if (ownTimelines === "loading") { - void getHttpTimelineClient() - .listTimeline({ relate: user.username, relateType: "own" }) - .then( - (timelines) => { - if (subscribe) { - setOwnTimelines(timelines); - } - }, - () => { - setOwnTimelines("offline"); - } - ); - } - return () => { - subscribe = false; - }; - }, [user, ownTimelines]); - - React.useEffect(() => { - let subscribe = true; - if (joinTimelines === "loading") { - void getHttpTimelineClient() - .listTimeline({ relate: user.username, relateType: "join" }) - .then( - (timelines) => { - if (subscribe) { - setJoinTimelines(timelines); - } - }, - () => { - setJoinTimelines("offline"); - } - ); - } - return () => { - subscribe = false; - }; - }, [user, joinTimelines]); - - return ( - <Row className="my-2 justify-content-center"> - {ownTimelines === "offline" && joinTimelines === "offline" ? ( - <Col className="py-2" sm="8" lg="6"> - <OfflineBoard - onReload={() => { - setOwnTimelines("loading"); - setJoinTimelines("loading"); - }} - /> - </Col> - ) : ( - <> - <Col sm="6" lg="5" className="py-2"> - <TimelineBoard - title={t("home.ownTimeline")} - timelines={ownTimelines} - onReload={() => { - setOwnTimelines("loading"); - }} - /> - </Col> - <Col sm="6" lg="5" className="py-2"> - <TimelineBoard - title={t("home.joinTimeline")} - timelines={joinTimelines} - onReload={() => { - setJoinTimelines("loading"); - }} - /> - </Col> - </> - )} - </Row> - ); -}; - -export default BoardWithUser; diff --git a/Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx b/Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx deleted file mode 100644 index ebfddb50..00000000 --- a/Timeline/ClientApp/src/app/views/home/BoardWithoutUser.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; -import { Row, Col } from "react-bootstrap"; - -import { TimelineInfo } from "@/services/timeline"; -import { getHttpTimelineClient } from "@/http/timeline"; - -import TimelineBoard from "./TimelineBoard"; -import OfflineBoard from "./OfflineBoard"; - -const BoardWithoutUser: React.FC = () => { - const [publicTimelines, setPublicTimelines] = React.useState< - TimelineInfo[] | "offline" | "loading" - >("loading"); - - React.useEffect(() => { - let subscribe = true; - if (publicTimelines === "loading") { - void getHttpTimelineClient() - .listTimeline({ visibility: "Public" }) - .then( - (timelines) => { - if (subscribe) { - setPublicTimelines(timelines); - } - }, - () => { - setPublicTimelines("offline"); - } - ); - } - return () => { - subscribe = false; - }; - }, [publicTimelines]); - - return ( - <Row className="my-2 justify-content-center"> - {publicTimelines === "offline" ? ( - <Col sm="8" lg="6"> - <OfflineBoard - onReload={() => { - setPublicTimelines("loading"); - }} - /> - </Col> - ) : ( - <Col sm="8" lg="6"> - <TimelineBoard - timelines={publicTimelines} - onReload={() => { - setPublicTimelines("loading"); - }} - /> - </Col> - )} - </Row> - ); -}; - -export default BoardWithoutUser; diff --git a/Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx b/Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx deleted file mode 100644 index fc05bd74..00000000 --- a/Timeline/ClientApp/src/app/views/home/OfflineBoard.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import { Trans } from "react-i18next"; - -import { getAllCachedTimelineNames } from "@/services/timeline"; -import UserTimelineLogo from "../common/UserTimelineLogo"; -import TimelineLogo from "../common/TimelineLogo"; - -export interface OfflineBoardProps { - onReload: () => void; -} - -const OfflineBoard: React.FC<OfflineBoardProps> = ({ onReload }) => { - const [timelines, setTimelines] = React.useState<string[]>([]); - - React.useEffect(() => { - let subscribe = true; - void getAllCachedTimelineNames().then((t) => { - if (subscribe) setTimelines(t); - }); - return () => { - subscribe = false; - }; - }); - - return ( - <> - <Trans i18nKey="home.offlinePrompt"> - 0 - <a - href="#" - onClick={(e) => { - onReload(); - e.preventDefault(); - }} - > - 1 - </a> - 2 - </Trans> - {timelines.map((timeline) => { - const isPersonal = timeline.startsWith("@"); - const url = isPersonal - ? `/users/${timeline.slice(1)}` - : `/timelines/${timeline}`; - return ( - <div key={timeline} className="timeline-board-item"> - {isPersonal ? ( - <UserTimelineLogo className="icon" /> - ) : ( - <TimelineLogo className="icon" /> - )} - <Link to={url}>{timeline}</Link> - </div> - ); - })} - </> - ); -}; - -export default OfflineBoard; diff --git a/Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx b/Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx deleted file mode 100644 index a3d176e1..00000000 --- a/Timeline/ClientApp/src/app/views/home/TimelineBoard.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { Link } from "react-router-dom"; -import { Trans } from "react-i18next"; -import { Spinner } from "react-bootstrap"; - -import { TimelineInfo } from "@/services/timeline"; -import TimelineLogo from "../common/TimelineLogo"; -import UserTimelineLogo from "../common/UserTimelineLogo"; - -export interface TimelineBoardProps { - title?: string; - timelines: TimelineInfo[] | "offline" | "loading"; - onReload: () => void; - className?: string; -} - -const TimelineBoard: React.FC<TimelineBoardProps> = (props) => { - const { title, timelines, className } = props; - - return ( - <div className={clsx("timeline-board", className)}> - {title != null && <h3 className="text-center">{title}</h3>} - {(() => { - if (timelines === "loading") { - return ( - <div className="d-flex flex-grow-1 justify-content-center align-items-center"> - <Spinner variant="primary" animation="border" /> - </div> - ); - } else if (timelines === "offline") { - return ( - <div className="d-flex flex-grow-1 justify-content-center align-items-center"> - <Trans i18nKey="loadFailReload" parent="div"> - 0 - <a - href="#" - onClick={(e) => { - props.onReload(); - e.preventDefault(); - }} - > - 1 - </a> - 2 - </Trans> - </div> - ); - } else { - return timelines.map((timeline) => { - const { name } = timeline; - const isPersonal = name.startsWith("@"); - const url = isPersonal - ? `/users/${timeline.owner.username}` - : `/timelines/${name}`; - return ( - <div key={name} className="timeline-board-item"> - {isPersonal ? ( - <UserTimelineLogo className="icon" /> - ) : ( - <TimelineLogo className="icon" /> - )} - <Link to={url}>{name}</Link> - </div> - ); - }); - } - })()} - </div> - ); -}; - -export default TimelineBoard; diff --git a/Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx b/Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx deleted file mode 100644 index d9467719..00000000 --- a/Timeline/ClientApp/src/app/views/home/TimelineCreateDialog.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; - -import { validateTimelineName, timelineService } from "@/services/timeline"; -import OperationDialog from "../common/OperationDialog"; - -interface TimelineCreateDialogProps { - open: boolean; - close: () => void; -} - -const TimelineCreateDialog: React.FC<TimelineCreateDialogProps> = (props) => { - const history = useHistory(); - - let nameSaved: string; - - return ( - <OperationDialog - open={props.open} - close={props.close} - titleColor="success" - title="home.createDialog.title" - inputScheme={[ - { - type: "text", - label: "home.createDialog.name", - helperText: "home.createDialog.nameFormat", - validator: (name) => { - if (name.length === 0) { - return "home.createDialog.noEmpty"; - } else if (name.length > 26) { - return "home.createDialog.tooLong"; - } else if (!validateTimelineName(name)) { - return "home.createDialog.badFormat"; - } else { - return null; - } - }, - }, - ]} - onProcess={([name]) => { - nameSaved = name as string; - return timelineService.createTimeline(nameSaved).toPromise(); - }} - onSuccessAndClose={() => { - history.push(`timelines/${nameSaved}`); - }} - failurePrompt={(e) => `${e as string}`} - /> - ); -}; - -export default TimelineCreateDialog; diff --git a/Timeline/ClientApp/src/app/views/home/home.sass b/Timeline/ClientApp/src/app/views/home/home.sass deleted file mode 100644 index f5d6ffc3..00000000 --- a/Timeline/ClientApp/src/app/views/home/home.sass +++ /dev/null @@ -1,13 +0,0 @@ -.timeline-board-item - font-size: 1.1em - @extend .my-2 - .icon - height: 1.3em - @extend .mr-2 - -.timeline-board - @extend .cru-card - @extend .d-flex - @extend .flex-column - @extend .p-3 - min-height: 200px diff --git a/Timeline/ClientApp/src/app/views/home/index.tsx b/Timeline/ClientApp/src/app/views/home/index.tsx deleted file mode 100644 index 760adcea..00000000 --- a/Timeline/ClientApp/src/app/views/home/index.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; -import { useTranslation } from "react-i18next"; -import { Row, Container, Button, Col } from "react-bootstrap"; - -import { useUser } from "@/services/user"; -import SearchInput from "../common/SearchInput"; - -import BoardWithoutUser from "./BoardWithoutUser"; -import BoardWithUser from "./BoardWithUser"; -import TimelineCreateDialog from "./TimelineCreateDialog"; - -const HomePage: React.FC = () => { - const history = useHistory(); - - const { t } = useTranslation(); - - const user = useUser(); - - const [navText, setNavText] = React.useState<string>(""); - - const [dialog, setDialog] = React.useState<"create" | null>(null); - - const goto = React.useCallback((): void => { - if (navText === "") { - history.push("users/crupest"); - } else if (navText.startsWith("@")) { - history.push(`users/${navText.slice(1)}`); - } else { - history.push(`timelines/${navText}`); - } - }, [navText, history]); - - return ( - <> - <Container fluid> - <Row className="justify-content-center"> - <Col xs={12} sm={10} md={8} lg={6}> - <SearchInput - className="justify-content-center" - value={navText} - onChange={setNavText} - onButtonClick={goto} - buttonText={t("home.go")} - placeholder="@crupest" - additionalButton={ - user != null && ( - <Button - variant="outline-success" - onClick={() => { - setDialog("create"); - }} - > - {t("home.createButton")} - </Button> - ) - } - /> - </Col> - </Row> - {(() => { - if (user == null) { - return <BoardWithoutUser />; - } else { - return <BoardWithUser user={user} />; - } - })()} - </Container> - <footer className="text-right"> - <a - className="mx-3 text-muted" - href="http://beian.miit.gov.cn/" - target="_blank" - rel="noopener noreferrer" - > - <small>鄂ICP备18030913号-1</small> - </a> - <a - className="mx-3 text-muted" - href="http://www.beian.gov.cn/" - target="_blank" - rel="noopener noreferrer" - > - <small className="white-space-no-wrap">公安备案 42112102000124</small> - </a> - </footer> - {dialog === "create" && ( - <TimelineCreateDialog - open - close={() => { - setDialog(null); - }} - /> - )} - </> - ); -}; - -export default HomePage; diff --git a/Timeline/ClientApp/src/app/views/login/index.tsx b/Timeline/ClientApp/src/app/views/login/index.tsx deleted file mode 100644 index 61b9a525..00000000 --- a/Timeline/ClientApp/src/app/views/login/index.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; -import { useTranslation } from "react-i18next"; -import { Container, Form } from "react-bootstrap"; - -import { useUser, userService } from "@/services/user"; - -import AppBar from "../common/AppBar"; -import LoadingButton from "../common/LoadingButton"; - -const LoginPage: React.FC = (_) => { - const { t } = useTranslation(); - const history = useHistory(); - const [username, setUsername] = React.useState<string>(""); - const [usernameDirty, setUsernameDirty] = React.useState<boolean>(false); - const [password, setPassword] = React.useState<string>(""); - const [passwordDirty, setPasswordDirty] = React.useState<boolean>(false); - const [rememberMe, setRememberMe] = React.useState<boolean>(true); - const [process, setProcess] = React.useState<boolean>(false); - const [error, setError] = React.useState<string | null>(null); - - const user = useUser(); - - React.useEffect(() => { - if (user != null) { - const id = setTimeout(() => history.push("/"), 3000); - return () => { - clearTimeout(id); - }; - } - }, [history, user]); - - if (user != null) { - return ( - <> - <AppBar /> - <p className="mt-appbar">{t("login.alreadyLogin")}</p> - </> - ); - } - - const submit = (): void => { - if (username === "" || password === "") { - setUsernameDirty(true); - setPasswordDirty(true); - return; - } - - setProcess(true); - userService - .login( - { - username: username, - password: password, - }, - rememberMe - ) - .then( - () => { - if (history.length === 0) { - history.push("/"); - } else { - history.goBack(); - } - }, - (e: Error) => { - setProcess(false); - setError(e.message); - } - ); - }; - - const onEnterPressInPassword: React.KeyboardEventHandler = (e) => { - if (e.key === "Enter") { - submit(); - } - }; - - return ( - <Container fluid className="login-container mt-2"> - <h1 className="text-center">{t("welcome")}</h1> - <Form> - <Form.Group> - <Form.Label htmlFor="username">{t("user.username")}</Form.Label> - <Form.Control - id="username" - disabled={process} - onChange={(e) => { - setUsername(e.target.value); - setUsernameDirty(true); - }} - value={username} - isInvalid={usernameDirty && username === ""} - /> - {usernameDirty && username === "" && ( - <Form.Control.Feedback type="invalid"> - {t("login.emptyUsername")} - </Form.Control.Feedback> - )} - </Form.Group> - <Form.Group> - <Form.Label htmlFor="password">{t("user.password")}</Form.Label> - <Form.Control - id="password" - type="password" - disabled={process} - onChange={(e) => { - setPassword(e.target.value); - setPasswordDirty(true); - }} - value={password} - onKeyDown={onEnterPressInPassword} - isInvalid={passwordDirty && password === ""} - /> - {passwordDirty && password === "" && ( - <Form.Control.Feedback type="invalid"> - {t("login.emptyPassword")} - </Form.Control.Feedback> - )} - </Form.Group> - <Form.Group> - <Form.Check<"input"> - id="remember-me" - type="checkbox" - checked={rememberMe} - onChange={(e) => { - setRememberMe(e.currentTarget.checked); - }} - label={t("user.rememberMe")} - /> - </Form.Group> - {error ? <p className="text-danger">{t(error)}</p> : null} - <div className="text-right"> - <LoadingButton - loading={process} - variant="primary" - onClick={(e) => { - submit(); - e.preventDefault(); - }} - disabled={username === "" || password === "" ? true : undefined} - > - {t("user.login")} - </LoadingButton> - </div> - </Form> - </Container> - ); -}; - -export default LoginPage; diff --git a/Timeline/ClientApp/src/app/views/login/login.sass b/Timeline/ClientApp/src/app/views/login/login.sass deleted file mode 100644 index 0bf385f5..00000000 --- a/Timeline/ClientApp/src/app/views/login/login.sass +++ /dev/null @@ -1,2 +0,0 @@ -.login-container
- max-width: 600px
diff --git a/Timeline/ClientApp/src/app/views/settings/index.tsx b/Timeline/ClientApp/src/app/views/settings/index.tsx deleted file mode 100644 index 964e7442..00000000 --- a/Timeline/ClientApp/src/app/views/settings/index.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import React, { useState } from "react"; -import { useHistory } from "react-router"; -import { useTranslation } from "react-i18next"; -import { Form, Container, Row, Col, Button, Modal } from "react-bootstrap"; - -import { useUser, userService } from "@/services/user"; -import OperationDialog, { - OperationInputErrorInfo, -} from "../common/OperationDialog"; - -interface ChangePasswordDialogProps { - open: boolean; - close: () => void; -} - -const ChangePasswordDialog: React.FC<ChangePasswordDialogProps> = (props) => { - const history = useHistory(); - const { t } = useTranslation(); - - const [redirect, setRedirect] = useState<boolean>(false); - - return ( - <OperationDialog - open={props.open} - title={t("settings.dialogChangePassword.title")} - titleColor="dangerous" - inputPrompt={t("settings.dialogChangePassword.prompt")} - inputScheme={[ - { - type: "text", - label: t("settings.dialogChangePassword.inputOldPassword"), - password: true, - validator: (v) => - v === "" - ? "settings.dialogChangePassword.errorEmptyOldPassword" - : null, - }, - { - type: "text", - label: t("settings.dialogChangePassword.inputNewPassword"), - password: true, - validator: (v, values) => { - const error: OperationInputErrorInfo = {}; - error[1] = - v === "" - ? "settings.dialogChangePassword.errorEmptyNewPassword" - : null; - if (v === values[2]) { - error[2] = null; - } else { - if (values[2] !== "") { - error[2] = "settings.dialogChangePassword.errorRetypeNotMatch"; - } - } - return error; - }, - }, - { - type: "text", - label: t("settings.dialogChangePassword.inputRetypeNewPassword"), - password: true, - validator: (v, values) => - v !== values[1] - ? "settings.dialogChangePassword.errorRetypeNotMatch" - : null, - }, - ]} - onProcess={async ([oldPassword, newPassword]) => { - await userService - .changePassword(oldPassword as string, newPassword as string) - .toPromise(); - await userService.logout(); - setRedirect(true); - }} - close={() => { - props.close(); - if (redirect) { - history.push("/login"); - } - }} - /> - ); -}; - -const ConfirmLogoutDialog: React.FC<{ - toggle: () => void; - onConfirm: () => void; -}> = ({ toggle, onConfirm }) => { - const { t } = useTranslation(); - - return ( - <Modal show centered onHide={toggle}> - <Modal.Header> - <Modal.Title className="text-danger"> - {t("settings.dialogConfirmLogout.title")} - </Modal.Title> - </Modal.Header> - <Modal.Body>{t("settings.dialogConfirmLogout.prompt")}</Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={toggle}> - {t("operationDialog.cancel")} - </Button> - <Button variant="danger" onClick={onConfirm}> - {t("operationDialog.confirm")} - </Button> - </Modal.Footer> - </Modal> - ); -}; - -const SettingsPage: React.FC = (_) => { - const { i18n, t } = useTranslation(); - const user = useUser(); - const history = useHistory(); - - const [dialog, setDialog] = useState<null | "changepassword" | "logout">( - null - ); - - const language = i18n.language.slice(0, 2); - - return ( - <Container fluid> - {user ? ( - <> - <Row className="border-bottom p-3 cursor-pointer"> - <Col xs="12"> - <h5 - onClick={() => { - history.push(`/users/${user.username}`); - }} - > - {t("settings.gotoSelf")} - </h5> - </Col> - </Row> - <Row className="border-bottom p-3 cursor-pointer"> - <Col xs="12"> - <h5 - className="text-danger" - onClick={() => setDialog("changepassword")} - > - {t("settings.changePassword")} - </h5> - </Col> - </Row> - <Row className="border-bottom p-3 cursor-pointer"> - <Col xs="12"> - <h5 - className="text-danger" - onClick={() => { - setDialog("logout"); - }} - > - {t("settings.logout")} - </h5> - </Col> - </Row> - </> - ) : null} - <Row className="align-items-center border-bottom p-3"> - <Col xs="12" sm="auto"> - <h5>{t("settings.languagePrimary")}</h5> - <p>{t("settings.languageSecondary")}</p> - </Col> - <Col xs="auto" className="ml-auto"> - <Form.Control - as="select" - value={language} - onChange={(e) => { - void i18n.changeLanguage(e.target.value); - }} - > - <option value="zh">中文</option> - <option value="en">English</option> - </Form.Control> - </Col> - </Row> - {(() => { - switch (dialog) { - case "changepassword": - return ( - <ChangePasswordDialog - open - close={() => { - setDialog(null); - }} - /> - ); - case "logout": - return ( - <ConfirmLogoutDialog - toggle={() => setDialog(null)} - onConfirm={() => { - void userService.logout().then(() => { - history.push("/"); - }); - }} - /> - ); - default: - return null; - } - })()} - </Container> - ); -}; - -export default SettingsPage; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx b/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx deleted file mode 100644 index 3c52150f..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import Svg from "react-inlinesvg"; -import arrowsAngleContractIcon from "bootstrap-icons/icons/arrows-angle-contract.svg"; -import arrowsAngleExpandIcon from "bootstrap-icons/icons/arrows-angle-expand.svg"; - -const CollapseButton: React.FC<{ - collapse: boolean; - onClick: () => void; - className?: string; - style?: React.CSSProperties; -}> = ({ collapse, onClick, className, style }) => { - return ( - <Svg - src={collapse ? arrowsAngleExpandIcon : arrowsAngleContractIcon} - onClick={onClick} - className={clsx("text-primary icon-button", className)} - style={style} - /> - ); -}; - -export default CollapseButton; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx b/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx deleted file mode 100644 index a8de20aa..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import clsx from "clsx"; - -import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI"; -import SyncStatusBadge from "../timeline-common/SyncStatusBadge"; -import CollapseButton from "../timeline-common/CollapseButton"; - -const InfoCardTemplate: React.FC< - Pick< - TimelineCardComponentProps<"">, - "collapse" | "toggleCollapse" | "syncStatus" | "className" - > & { children: React.ReactElement[] } -> = ({ collapse, toggleCollapse, syncStatus, className, children }) => { - return ( - <div className={clsx("cru-card p-2 clearfix", className)}> - <div className="float-right d-flex align-items-center"> - <SyncStatusBadge status={syncStatus} className="mr-2" /> - <CollapseButton collapse={collapse} onClick={toggleCollapse} /> - </div> - - <div style={{ display: collapse ? "none" : "block" }}>{children}</div> - </div> - ); -}; - -export default InfoCardTemplate; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx b/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx deleted file mode 100644 index e67cfb43..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { useTranslation } from "react-i18next"; - -import { UiLogicError } from "@/common"; - -export type TimelineSyncStatus = "syncing" | "synced" | "offline"; - -const SyncStatusBadge: React.FC<{ - status: TimelineSyncStatus; - style?: React.CSSProperties; - className?: string; -}> = ({ status, style, className }) => { - const { t } = useTranslation(); - - return ( - <div style={style} className={clsx("timeline-sync-state-badge", className)}> - {(() => { - switch (status) { - case "syncing": { - return ( - <> - <span className="timeline-sync-state-badge-pin bg-warning" /> - <span className="text-warning"> - {t("timeline.postSyncState.syncing")} - </span> - </> - ); - } - case "synced": { - return ( - <> - <span className="timeline-sync-state-badge-pin bg-success" /> - <span className="text-success"> - {t("timeline.postSyncState.synced")} - </span> - </> - ); - } - case "offline": { - return ( - <> - <span className="timeline-sync-state-badge-pin bg-danger" /> - <span className="text-danger"> - {t("timeline.postSyncState.offline")} - </span> - </> - ); - } - default: - throw new UiLogicError("Unknown sync state."); - } - })()} - </div> - ); -}; - -export default SyncStatusBadge; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx b/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx deleted file mode 100644 index fd051d45..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from "react"; -import clsx from "clsx"; - -import { TimelinePostInfo } from "@/services/timeline"; - -import TimelineItem from "./TimelineItem"; - -export interface TimelinePostInfoEx extends TimelinePostInfo { - deletable: boolean; -} - -export type TimelineDeleteCallback = (index: number, id: number) => void; - -export interface TimelineProps { - className?: string; - posts: TimelinePostInfoEx[]; - onDelete: TimelineDeleteCallback; - onResize?: () => void; - containerRef?: React.Ref<HTMLDivElement>; -} - -const Timeline: React.FC<TimelineProps> = (props) => { - const { posts, onDelete, onResize } = props; - - const [indexShowDeleteButton, setIndexShowDeleteButton] = React.useState< - number - >(-1); - - const onItemClick = React.useCallback(() => { - setIndexShowDeleteButton(-1); - }, []); - - const onToggleDelete = React.useMemo(() => { - return posts.map((post, i) => { - return post.deletable - ? () => { - setIndexShowDeleteButton((oldIndexShowDeleteButton) => { - return oldIndexShowDeleteButton !== i ? i : -1; - }); - } - : undefined; - }); - }, [posts]); - - const onItemDelete = React.useMemo(() => { - return posts.map((post, i) => { - return () => { - onDelete(i, post.id); - }; - }); - }, [posts, onDelete]); - - return ( - <div ref={props.containerRef} className={clsx("timeline", props.className)}> - {(() => { - const length = posts.length; - return posts.map((post, i) => { - const toggleMore = onToggleDelete[i]; - - return ( - <TimelineItem - post={post} - key={post.id} - current={length - 1 === i} - more={ - toggleMore - ? { - isOpen: indexShowDeleteButton === i, - toggle: toggleMore, - onDelete: onItemDelete[i], - } - : undefined - } - onClick={onItemClick} - onResize={onResize} - /> - ); - }); - })()} - </div> - ); -}; - -export default Timeline; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx deleted file mode 100644 index 4db23371..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import Svg from "react-inlinesvg"; -import chevronDownIcon from "bootstrap-icons/icons/chevron-down.svg"; -import trashIcon from "bootstrap-icons/icons/trash.svg"; -import { Modal, Button } from "react-bootstrap"; - -import { useAvatar } from "@/services/user"; -import { TimelinePostInfo } from "@/services/timeline"; - -import BlobImage from "../common/BlobImage"; - -const TimelinePostDeleteConfirmDialog: React.FC<{ - toggle: () => void; - onConfirm: () => void; -}> = ({ toggle, onConfirm }) => { - const { t } = useTranslation(); - - return ( - <Modal toggle={toggle} isOpen centered> - <Modal.Header> - <Modal.Title className="text-danger"> - {t("timeline.post.deleteDialog.title")} - </Modal.Title> - </Modal.Header> - <Modal.Body>{t("timeline.post.deleteDialog.prompt")}</Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={toggle}> - {t("operationDialog.cancel")} - </Button> - <Button - variant="danger" - onClick={() => { - onConfirm(); - toggle(); - }} - > - {t("operationDialog.confirm")} - </Button> - </Modal.Footer> - </Modal> - ); -}; - -export interface TimelineItemProps { - post: TimelinePostInfo; - current?: boolean; - more?: { - isOpen: boolean; - toggle: () => void; - onDelete: () => void; - }; - onClick?: () => void; - onResize?: () => void; - className?: string; - style?: React.CSSProperties; -} - -const TimelineItem: React.FC<TimelineItemProps> = (props) => { - const { i18n } = useTranslation(); - - const current = props.current === true; - - const { more, onResize } = props; - - const avatar = useAvatar(props.post.author.username); - - const [deleteDialog, setDeleteDialog] = React.useState<boolean>(false); - const toggleDeleteDialog = React.useCallback( - () => setDeleteDialog((old) => !old), - [] - ); - - return ( - <div - className={clsx( - "timeline-item position-relative", - current && "current", - props.className - )} - onClick={props.onClick} - style={props.style} - > - <div className="timeline-line-area-container"> - <div className="timeline-line-area"> - <div className="timeline-line-segment start"></div> - <div className="timeline-line-node-container"> - <div className="timeline-line-node"></div> - </div> - <div className="timeline-line-segment end"></div> - {current && <div className="timeline-line-segment current-end" />} - </div> - </div> - <div className="timeline-content-area"> - <div> - <span className="mr-2"> - <span className="text-primary white-space-no-wrap mr-2"> - {props.post.time.toLocaleString(i18n.languages)} - </span> - <small className="text-dark">{props.post.author.nickname}</small> - </span> - {more != null ? ( - <Svg - src={chevronDownIcon} - className="text-info icon-button" - onClick={(e) => { - more.toggle(); - e.stopPropagation(); - }} - /> - ) : null} - </div> - <div className="timeline-content"> - <Link - className="float-left m-2" - to={"/users/" + props.post.author.username} - > - <BlobImage - onLoad={onResize} - blob={avatar} - className="avatar rounded" - /> - </Link> - {(() => { - const { content } = props.post; - if (content.type === "text") { - return content.text; - } else { - return ( - <BlobImage - onLoad={onResize} - blob={content.data} - className="timeline-content-image" - /> - ); - } - })()} - </div> - </div> - {more != null && more.isOpen ? ( - <> - <div - className="position-absolute position-lt w-100 h-100 mask d-flex justify-content-center align-items-center" - onClick={more.toggle} - > - <Svg - src={trashIcon} - className="text-danger icon-button large" - onClick={(e) => { - toggleDeleteDialog(); - e.stopPropagation(); - }} - /> - </div> - {deleteDialog ? ( - <TimelinePostDeleteConfirmDialog - toggle={() => { - toggleDeleteDialog(); - more.toggle(); - }} - onConfirm={more.onDelete} - /> - ) : null} - </> - ) : null} - </div> - ); -}; - -export default TimelineItem; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx deleted file mode 100644 index 67a8543a..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineMember.tsx +++ /dev/null @@ -1,211 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { Container, ListGroup, Modal, Row, Col, Button } from "react-bootstrap"; - -import { User, useAvatar } from "@/services/user"; - -import SearchInput from "../common/SearchInput"; -import BlobImage from "../common/BlobImage"; - -const TimelineMemberItem: React.FC<{ - user: User; - owner: boolean; - onRemove?: (username: string) => void; -}> = ({ user, owner, onRemove }) => { - const { t } = useTranslation(); - - const avatar = useAvatar(user.username); - - return ( - <ListGroup.Item className="container"> - <Row> - <Col xs="auto"> - <BlobImage blob={avatar} className="avatar small" /> - </Col> - <Col> - <Row>{user.nickname}</Row> - <Row> - <small>{"@" + user.username}</small> - </Row> - </Col> - {(() => { - if (owner) { - return null; - } - if (onRemove == null) { - return null; - } - return ( - <Button - className="align-self-center" - variant="danger" - onClick={() => { - onRemove(user.username); - }} - > - {t("timeline.member.remove")} - </Button> - ); - })()} - </Row> - </ListGroup.Item> - ); -}; - -export interface TimelineMemberCallbacks { - onCheckUser: (username: string) => Promise<User | null>; - onAddUser: (user: User) => Promise<void>; - onRemoveUser: (username: string) => void; -} - -export interface TimelineMemberProps { - members: User[]; - edit: TimelineMemberCallbacks | null | undefined; -} - -const TimelineMember: React.FC<TimelineMemberProps> = (props) => { - const { t } = useTranslation(); - - const [userSearchText, setUserSearchText] = useState<string>(""); - const [userSearchState, setUserSearchState] = useState< - | { - type: "user"; - data: User; - } - | { type: "error"; data: string } - | { type: "loading" } - | { type: "init" } - >({ type: "init" }); - - const userSearchAvatar = useAvatar( - userSearchState.type === "user" ? userSearchState.data.username : undefined - ); - - const members = props.members; - - return ( - <Container className="px-4"> - <ListGroup className="my-3"> - {members.map((member, index) => ( - <TimelineMemberItem - key={member.username} - user={member} - owner={index === 0} - onRemove={props.edit?.onRemoveUser} - /> - ))} - </ListGroup> - {(() => { - const edit = props.edit; - if (edit != null) { - return ( - <> - <SearchInput - value={userSearchText} - onChange={(v) => { - setUserSearchText(v); - }} - loading={userSearchState.type === "loading"} - onButtonClick={() => { - if (userSearchText === "") { - setUserSearchState({ - type: "error", - data: "login.emptyUsername", - }); - return; - } - - setUserSearchState({ type: "loading" }); - edit.onCheckUser(userSearchText).then( - (u) => { - if (u == null) { - setUserSearchState({ - type: "error", - data: "timeline.userNotExist", - }); - } else { - setUserSearchState({ type: "user", data: u }); - } - }, - (e) => { - setUserSearchState({ - type: "error", - data: `${e as string}`, - }); - } - ); - }} - /> - {(() => { - if (userSearchState.type === "user") { - const u = userSearchState.data; - const addable = - members.findIndex((m) => m.username === u.username) === -1; - return ( - <> - {!addable ? ( - <p>{t("timeline.member.alreadyMember")}</p> - ) : null} - <Container className="mb-3"> - <Row> - <Col className="col-auto"> - <BlobImage - blob={userSearchAvatar} - className="avatar small" - /> - </Col> - <Col> - <Row>{u.nickname}</Row> - <Row> - <small>{"@" + u.username}</small> - </Row> - </Col> - <Button - variant="primary" - className="align-self-center" - disabled={!addable} - onClick={() => { - void edit.onAddUser(u).then((_) => { - setUserSearchText(""); - setUserSearchState({ type: "init" }); - }); - }} - > - {t("timeline.member.add")} - </Button> - </Row> - </Container> - </> - ); - } else if (userSearchState.type === "error") { - return ( - <p className="text-danger">{t(userSearchState.data)}</p> - ); - } - })()} - </> - ); - } else { - return null; - } - })()} - </Container> - ); -}; - -export default TimelineMember; - -export interface TimelineMemberDialogProps extends TimelineMemberProps { - open: boolean; - onClose: () => void; -} - -export const TimelineMemberDialog: React.FC<TimelineMemberDialogProps> = ( - props -) => { - return ( - <Modal show centered onHide={props.onClose}> - <TimelineMember {...props} /> - </Modal> - ); -}; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx deleted file mode 100644 index d5c91622..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplate.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { of } from "rxjs"; -import { catchError } from "rxjs/operators"; - -import { UiLogicError } from "@/common"; -import { pushAlert } from "@/services/alert"; -import { useUser, userInfoService, UserNotExistError } from "@/services/user"; -import { - timelineService, - usePostList, - useTimelineInfo, -} from "@/services/timeline"; - -import { TimelineDeleteCallback } from "./Timeline"; -import { TimelineMemberDialog } from "./TimelineMember"; -import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; -import { TimelinePageTemplateUIProps } from "./TimelinePageTemplateUI"; -import { TimelinePostSendCallback } from "./TimelinePostEdit"; - -export interface TimelinePageTemplateProps<TManageItem> { - name: string; - onManage: (item: TManageItem) => void; - UiComponent: React.ComponentType< - Omit<TimelinePageTemplateUIProps<TManageItem>, "CardComponent"> - >; - notFoundI18nKey: string; -} - -export default function TimelinePageTemplate<TManageItem>( - props: TimelinePageTemplateProps<TManageItem> -): React.ReactElement | null { - const { t } = useTranslation(); - - const { name } = props; - - const service = timelineService; - - const user = useUser(); - - const [dialog, setDialog] = React.useState<null | "property" | "member">( - null - ); - - const timelineState = useTimelineInfo(name); - - const timeline = timelineState?.timeline; - - const postListState = usePostList(name); - - const error: string | undefined = (() => { - if (timelineState != null) { - const { type, timeline } = timelineState; - if (type === "offline" && timeline == null) return "Network Error"; - if (type === "synced" && timeline == null) - return t(props.notFoundI18nKey); - } - return undefined; - })(); - - const closeDialog = React.useCallback((): void => { - setDialog(null); - }, []); - - let dialogElement: React.ReactElement | undefined; - - if (dialog === "property") { - if (timeline == null) { - throw new UiLogicError( - "Timeline is null but attempt to open change property dialog." - ); - } - - dialogElement = ( - <TimelinePropertyChangeDialog - open - close={closeDialog} - oldInfo={{ - visibility: timeline.visibility, - description: timeline.description, - }} - onProcess={(req) => { - return service.changeTimelineProperty(name, req).toPromise().then(); - }} - /> - ); - } else if (dialog === "member") { - if (timeline == null) { - throw new UiLogicError( - "Timeline is null but attempt to open change property dialog." - ); - } - - dialogElement = ( - <TimelineMemberDialog - open - onClose={closeDialog} - members={[timeline.owner, ...timeline.members]} - edit={ - service.hasManagePermission(user, timeline) - ? { - onCheckUser: (u) => { - return userInfoService - .getUserInfo(u) - .pipe( - catchError((e) => { - if (e instanceof UserNotExistError) { - return of(null); - } else { - throw e; - } - }) - ) - .toPromise(); - }, - onAddUser: (u) => { - return service.addMember(name, u.username).toPromise().then(); - }, - onRemoveUser: (u) => { - service.removeMember(name, u); - }, - } - : null - } - /> - ); - } - - const { UiComponent } = props; - - const onDelete: TimelineDeleteCallback = React.useCallback( - (index, id) => { - service.deletePost(name, id).subscribe(null, () => { - pushAlert({ - type: "danger", - message: t("timeline.deletePostFailed"), - }); - }); - }, - [service, name, t] - ); - - const onPost: TimelinePostSendCallback = React.useCallback( - (req) => { - return service.createPost(name, req).toPromise().then(); - }, - [service, name] - ); - - const onManageProp = props.onManage; - - const onManage = React.useCallback( - (item: "property" | TManageItem) => { - if (item === "property") { - setDialog(item); - } else { - onManageProp(item); - } - }, - [onManageProp] - ); - - return ( - <> - <UiComponent - error={error} - timeline={timeline ?? undefined} - postListState={postListState} - onDelete={onDelete} - onPost={ - timeline != null && service.hasPostPermission(user, timeline) - ? onPost - : undefined - } - onManage={ - timeline != null && service.hasManagePermission(user, timeline) - ? onManage - : undefined - } - onMember={() => setDialog("member")} - /> - {dialogElement} - </> - ); -} diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx deleted file mode 100644 index 6c2c43c1..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { fromEvent } from "rxjs"; -import { Spinner } from "react-bootstrap"; - -import { getAlertHost } from "@/services/alert"; -import { useEventEmiiter, UiLogicError } from "@/common"; -import { - TimelineInfo, - TimelinePostsWithSyncState, - timelineService, -} from "@/services/timeline"; -import { userService } from "@/services/user"; - -import Timeline, { - TimelinePostInfoEx, - TimelineDeleteCallback, -} from "./Timeline"; -import TimelineTop from "./TimelineTop"; -import TimelinePostEdit, { TimelinePostSendCallback } from "./TimelinePostEdit"; -import { TimelineSyncStatus } from "./SyncStatusBadge"; - -export interface TimelineCardComponentProps<TManageItems> { - timeline: TimelineInfo; - onManage?: (item: TManageItems | "property") => void; - onMember: () => void; - className?: string; - collapse: boolean; - syncStatus: TimelineSyncStatus; - toggleCollapse: () => void; -} - -export interface TimelinePageTemplateUIProps<TManageItems> { - timeline?: TimelineInfo; - postListState?: TimelinePostsWithSyncState; - CardComponent: React.ComponentType<TimelineCardComponentProps<TManageItems>>; - onMember: () => void; - onManage?: (item: TManageItems | "property") => void; - onPost?: TimelinePostSendCallback; - onDelete: TimelineDeleteCallback; - error?: string; -} - -export default function TimelinePageTemplateUI<TManageItems>( - props: TimelinePageTemplateUIProps<TManageItems> -): React.ReactElement | null { - const { timeline, postListState } = props; - - const { t } = useTranslation(); - - const bottomSpaceRef = React.useRef<HTMLDivElement | null>(null); - - const onPostEditHeightChange = React.useCallback((height: number): void => { - const { current: bottomSpaceDiv } = bottomSpaceRef; - if (bottomSpaceDiv != null) { - bottomSpaceDiv.style.height = `${height}px`; - } - if (height === 0) { - const alertHost = getAlertHost(); - if (alertHost != null) { - alertHost.style.removeProperty("margin-bottom"); - } - } else { - const alertHost = getAlertHost(); - if (alertHost != null) { - alertHost.style.marginBottom = `${height}px`; - } - } - }, []); - - const timelineRef = React.useRef<HTMLDivElement | null>(null); - - const [getResizeEvent, triggerResizeEvent] = useEventEmiiter(); - - React.useEffect(() => { - const { current: timelineElement } = timelineRef; - if (timelineElement != null) { - let loadingScrollToBottom = true; - let pinBottom = false; - - const isAtBottom = (): boolean => - window.innerHeight + window.scrollY + 10 >= document.body.scrollHeight; - - const disableLoadingScrollToBottom = (): void => { - loadingScrollToBottom = false; - if (isAtBottom()) pinBottom = true; - }; - - const checkAndScrollToBottom = (): void => { - if (loadingScrollToBottom || pinBottom) { - window.scrollTo(0, document.body.scrollHeight); - } - }; - - const subscriptions = [ - fromEvent(timelineElement, "wheel").subscribe( - disableLoadingScrollToBottom - ), - fromEvent(timelineElement, "pointerdown").subscribe( - disableLoadingScrollToBottom - ), - fromEvent(timelineElement, "keydown").subscribe( - disableLoadingScrollToBottom - ), - fromEvent(window, "scroll").subscribe(() => { - if (loadingScrollToBottom) return; - - if (isAtBottom()) { - pinBottom = true; - } else { - pinBottom = false; - } - }), - fromEvent(window, "resize").subscribe(checkAndScrollToBottom), - getResizeEvent().subscribe(checkAndScrollToBottom), - ]; - - return () => { - subscriptions.forEach((s) => s.unsubscribe()); - }; - } - }, [getResizeEvent, triggerResizeEvent, timeline, postListState]); - - const genCardCollapseLocalStorageKey = (uniqueId: string): string => - `timeline.${uniqueId}.cardCollapse`; - - const cardCollapseLocalStorageKey = - timeline != null ? genCardCollapseLocalStorageKey(timeline.uniqueId) : null; - - const [cardCollapse, setCardCollapse] = React.useState<boolean>(true); - React.useEffect(() => { - if (cardCollapseLocalStorageKey != null) { - const savedCollapse = - window.localStorage.getItem(cardCollapseLocalStorageKey) === "true"; - setCardCollapse(savedCollapse); - } - }, [cardCollapseLocalStorageKey]); - - const toggleCardCollapse = (): void => { - const newState = !cardCollapse; - setCardCollapse(newState); - if (timeline != null) { - window.localStorage.setItem( - genCardCollapseLocalStorageKey(timeline.uniqueId), - newState.toString() - ); - } - }; - - let body: React.ReactElement; - - if (props.error != null) { - body = <p className="text-danger">{t(props.error)}</p>; - } else { - if (timeline != null) { - let timelineBody: React.ReactElement; - if (postListState != null) { - if (postListState.type === "notexist") { - throw new UiLogicError( - "Timeline is not null but post list state is notexist." - ); - } - if (postListState.type === "forbid") { - timelineBody = ( - <p className="text-danger">{t("timeline.messageCantSee")}</p> - ); - } else { - const posts: TimelinePostInfoEx[] = postListState.posts.map( - (post) => ({ - ...post, - deletable: timelineService.hasModifyPostPermission( - userService.currentUser, - timeline, - post - ), - }) - ); - - timelineBody = ( - <Timeline - containerRef={timelineRef} - posts={posts} - onDelete={props.onDelete} - onResize={triggerResizeEvent} - /> - ); - if (props.onPost != null) { - timelineBody = ( - <> - {timelineBody} - <div ref={bottomSpaceRef} className="flex-fix-length" /> - <TimelinePostEdit - className="fixed-bottom" - onPost={props.onPost} - onHeightChange={onPostEditHeightChange} - timelineUniqueId={timeline.uniqueId} - /> - </> - ); - } - } - } else { - timelineBody = ( - <div className="full-viewport-center-child"> - <Spinner variant="primary" animation="grow" /> - </div> - ); - } - - const { CardComponent } = props; - const syncStatus: TimelineSyncStatus = - postListState == null || postListState.syncing - ? "syncing" - : postListState.type === "synced" - ? "synced" - : "offline"; - - body = ( - <> - <CardComponent - className="timeline-template-card" - timeline={timeline} - onManage={props.onManage} - onMember={props.onMember} - syncStatus={syncStatus} - collapse={cardCollapse} - toggleCollapse={toggleCardCollapse} - /> - <TimelineTop height="56px" /> - {timelineBody} - </> - ); - } else { - body = ( - <div className="full-viewport-center-child"> - <Spinner variant="primary" animation="grow" /> - </div> - ); - } - } - - return body; -} diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx deleted file mode 100644 index dfa2f879..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePostEdit.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import { useTranslation } from "react-i18next"; -import Svg from "react-inlinesvg"; -import { Button, Spinner, Row, Col, Form } from "react-bootstrap"; -import textIcon from "bootstrap-icons/icons/card-text.svg"; -import imageIcon from "bootstrap-icons/icons/image.svg"; - -import { UiLogicError } from "@/common"; - -import { pushAlert } from "@/services/alert"; -import { TimelineCreatePostRequest } from "@/services/timeline"; - -interface TimelinePostEditImageProps { - onSelect: (blob: Blob | null) => void; -} - -const TimelinePostEditImage: React.FC<TimelinePostEditImageProps> = (props) => { - const { onSelect } = props; - const { t } = useTranslation(); - - const [file, setFile] = React.useState<File | null>(null); - const [fileUrl, setFileUrl] = React.useState<string | null>(null); - const [error, setError] = React.useState<string | null>(null); - - React.useEffect(() => { - if (file != null) { - const url = URL.createObjectURL(file); - setFileUrl(url); - return () => { - URL.revokeObjectURL(url); - }; - } - }, [file]); - - const onInputChange: React.ChangeEventHandler<HTMLInputElement> = React.useCallback( - (e) => { - const files = e.target.files; - if (files == null || files.length === 0) { - setFile(null); - setFileUrl(null); - } else { - setFile(files[0]); - } - onSelect(null); - setError(null); - }, - [onSelect] - ); - - const onImgLoad = React.useCallback(() => { - onSelect(file); - }, [onSelect, file]); - - const onImgError = React.useCallback(() => { - setError("loadImageError"); - }, []); - - return ( - <> - <Form.File - label={t("chooseImage")} - onChange={onInputChange} - accept="image/*" - className="mx-3 my-1 d-inline-block" - /> - {fileUrl && error == null && ( - <img - src={fileUrl} - className="timeline-post-edit-image" - onLoad={onImgLoad} - onError={onImgError} - /> - )} - {error != null && <div className="text-danger">{t(error)}</div>} - </> - ); -}; - -export type TimelinePostSendCallback = ( - content: TimelineCreatePostRequest -) => Promise<void>; - -export interface TimelinePostEditProps { - className?: string; - onPost: TimelinePostSendCallback; - onHeightChange?: (height: number) => void; - timelineUniqueId: string; -} - -const TimelinePostEdit: React.FC<TimelinePostEditProps> = (props) => { - const { onPost } = props; - - const { t } = useTranslation(); - - const [state, setState] = React.useState<"input" | "process">("input"); - const [kind, setKind] = React.useState<"text" | "image">("text"); - const [text, setText] = React.useState<string>(""); - const [imageBlob, setImageBlob] = React.useState<Blob | null>(null); - - const draftLocalStorageKey = `timeline.${props.timelineUniqueId}.postDraft`; - - React.useEffect(() => { - setText(window.localStorage.getItem(draftLocalStorageKey) ?? ""); - }, [draftLocalStorageKey]); - - const canSend = kind === "text" || (kind === "image" && imageBlob != null); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const containerRef = React.useRef<HTMLDivElement>(null!); - - const notifyHeightChange = (): void => { - if (props.onHeightChange) { - props.onHeightChange(containerRef.current.clientHeight); - } - }; - - React.useEffect(() => { - if (props.onHeightChange) { - props.onHeightChange(containerRef.current.clientHeight); - } - return () => { - if (props.onHeightChange) { - props.onHeightChange(0); - } - }; - }); - - const toggleKind = React.useCallback(() => { - setKind((oldKind) => (oldKind === "text" ? "image" : "text")); - setImageBlob(null); - }, []); - - const onSend = React.useCallback(() => { - setState("process"); - - const req: TimelineCreatePostRequest = (() => { - switch (kind) { - case "text": - return { - content: { - type: "text", - text: text, - }, - } as TimelineCreatePostRequest; - case "image": - if (imageBlob == null) { - throw new UiLogicError( - "Content type is image but image blob is null." - ); - } - return { - content: { - type: "image", - data: imageBlob, - }, - } as TimelineCreatePostRequest; - default: - throw new UiLogicError("Unknown content type."); - } - })(); - - onPost(req).then( - (_) => { - if (kind === "text") { - setText(""); - window.localStorage.removeItem(draftLocalStorageKey); - } - setState("input"); - setKind("text"); - }, - (_) => { - pushAlert({ - type: "danger", - message: t("timeline.sendPostFailed"), - }); - setState("input"); - } - ); - }, [onPost, kind, text, imageBlob, t, draftLocalStorageKey]); - - const onImageSelect = React.useCallback((blob: Blob | null) => { - setImageBlob(blob); - }, []); - - return ( - <div - ref={containerRef} - className={clsx("container-fluid bg-light", props.className)} - > - <Row> - <Col className="px-1 py-1"> - {kind === "text" ? ( - <Form.Control - as="textarea" - className="w-100 h-100 timeline-post-edit" - value={text} - disabled={state === "process"} - onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => { - const value = event.currentTarget.value; - setText(value); - window.localStorage.setItem(draftLocalStorageKey, value); - }} - /> - ) : ( - <TimelinePostEditImage onSelect={onImageSelect} /> - )} - </Col> - <Col xs="auto" className="align-self-end m-1"> - {(() => { - if (state === "input") { - return ( - <> - <div className="d-block text-center mt-1 mb-2"> - <Svg - onLoad={notifyHeightChange} - src={kind === "text" ? imageIcon : textIcon} - className="icon-button" - onClick={toggleKind} - /> - </div> - <Button - variant="primary" - onClick={onSend} - disabled={!canSend} - > - {t("timeline.send")} - </Button> - </> - ); - } else { - return <Spinner variant="primary" animation="border" />; - } - })()} - </Col> - </Row> - </div> - ); -}; - -export default TimelinePostEdit; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx deleted file mode 100644 index 87638f31..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from "react"; - -import { - TimelineVisibility, - kTimelineVisibilities, - TimelineChangePropertyRequest, -} from "@/services/timeline"; - -import OperationDialog, { - OperationSelectInputInfoOption, -} from "../common/OperationDialog"; - -export interface TimelinePropertyInfo { - visibility: TimelineVisibility; - description: string; -} - -export interface TimelinePropertyChangeDialogProps { - open: boolean; - close: () => void; - oldInfo: TimelinePropertyInfo; - onProcess: (request: TimelineChangePropertyRequest) => Promise<void>; -} - -const labelMap: { [key in TimelineVisibility]: string } = { - Private: "timeline.visibility.private", - Public: "timeline.visibility.public", - Register: "timeline.visibility.register", -}; - -const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps> = ( - props -) => { - return ( - <OperationDialog - title={"timeline.dialogChangeProperty.title"} - titleColor="default" - inputScheme={[ - { - type: "select", - label: "timeline.dialogChangeProperty.visibility", - options: kTimelineVisibilities.map<OperationSelectInputInfoOption>( - (v) => ({ - label: labelMap[v], - value: v, - }) - ), - initValue: props.oldInfo.visibility, - }, - { - type: "text", - label: "timeline.dialogChangeProperty.description", - initValue: props.oldInfo.description, - }, - ]} - open={props.open} - close={props.close} - onProcess={([newVisibility, newDescription]) => { - const req: TimelineChangePropertyRequest = {}; - if (newVisibility !== props.oldInfo.visibility) { - req.visibility = newVisibility as TimelineVisibility; - } - if (newDescription !== props.oldInfo.description) { - req.description = newDescription as string; - } - return props.onProcess(req); - }} - /> - ); -}; - -export default TimelinePropertyChangeDialog; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx deleted file mode 100644 index 93a2a32c..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; - -export interface TimelineTopProps { - height?: number | string; - children?: React.ReactElement; -} - -const TimelineTop: React.FC<TimelineTopProps> = ({ height, children }) => { - return ( - <div style={{ height: height }} className="timeline-top"> - <div className="timeline-line-area-container"> - <div className="timeline-line-area"> - <div className="timeline-line-segment"></div> - </div> - </div> - {children} - </div> - ); -}; - -export default TimelineTop; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass b/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass deleted file mode 100644 index 4151bfcc..00000000 --- a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass +++ /dev/null @@ -1,146 +0,0 @@ -@use 'sass:color' - -.timeline - z-index: 0 - position: relative - - &-item - display: flex - -$timeline-line-width: 7px -$timeline-line-node-radius: 18px -$timeline-line-color: $primary -$timeline-line-color-current: #36c2e6 - -@keyframes timeline-line-node-noncurrent - from - background: $timeline-line-color - - to - background: color.adjust($timeline-line-color, $lightness: +10%) - box-shadow: 0 0 20px 3px color.adjust($timeline-line-color, $lightness: +10%, $alpha: -0.1) - -@keyframes timeline-line-node-current - from - background: $timeline-line-color-current - - to - background: color.adjust($timeline-line-color-current, $lightness: +10%) - box-shadow: 0 0 20px 3px color.adjust($timeline-line-color-current, $lightness: +10%, $alpha: -0.1) - -.timeline-line - &-area-container - display: flex - justify-content: flex-end - padding-right: 5px - - flex: 0 0 auto - width: 60px - - &-area - display: flex - flex-direction: column - align-items: center - width: 30px - - &-segment - width: $timeline-line-width - background: $timeline-line-color - - &.start - height: 14px - flex: 0 0 auto - - &.end - flex: 1 1 auto - - &.current-end - height: 20px - flex: 0 0 auto - background: linear-gradient($timeline-line-color-current, transparent) - - &-node-container - flex: 0 0 auto - position: relative - width: $timeline-line-node-radius - height: $timeline-line-node-radius - - &-node - width: $timeline-line-node-radius + 2 - height: $timeline-line-node-radius + 2 - position: absolute - left: -1px - top: -1px - border-radius: 50% - box-sizing: border-box - z-index: 1 - animation: 1s infinite alternate - animation-name: timeline-line-node-noncurrent - -.timeline-top - display: flex - justify-content: space-between - - .timeline-line-segment - flex: 1 1 auto - -.current - .timeline-line - &-segment - - &.start - background: linear-gradient($timeline-line-color, $timeline-line-color-current) - - &.end - background: $timeline-line-color-current - - &-node - animation-name: timeline-line-node-current - -.timeline-content-area - padding: 10px 0 - flex-grow: 1 - -.timeline-item-delete-button - position: absolute - right: 0 - bottom: 0 - -.timeline-content - white-space: pre-line - -.timeline-content-image - max-width: 60% - max-height: 200px - -.timeline-post-edit-image - max-width: 100px - max-height: 100px - -.mask - background: change-color($color: white, $alpha: 0.8) - z-index: 100 - -.timeline-page-top-space - transition: height 0.5s - -.timeline-sync-state-badge - font-size: 0.8em - padding: 3px 8px - border-radius: 5px - background: #e8fbff - -.timeline-sync-state-badge-pin - display: inline-block - width: 0.4em - height: 0.4em - border-radius: 50% - vertical-align: middle - margin-right: 0.6em - -.timeline-template-card - position: fixed - z-index: 1 - top: 56px - right: 0 - margin: 0.5em diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx deleted file mode 100644 index 894b8195..00000000 --- a/Timeline/ClientApp/src/app/views/timeline/TimelineDeleteDialog.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from "react"; -import { useHistory } from "react-router"; -import { Trans } from "react-i18next"; - -import { timelineService } from "@/services/timeline"; - -import OperationDialog from "../common/OperationDialog"; - -interface TimelineDeleteDialog { - open: boolean; - name: string; - close: () => void; -} - -const TimelineDeleteDialog: React.FC<TimelineDeleteDialog> = (props) => { - const history = useHistory(); - - const { name } = props; - - return ( - <OperationDialog - open={props.open} - close={props.close} - title="timeline.deleteDialog.title" - titleColor="danger" - inputPrompt={() => { - return ( - <Trans i18nKey="timeline.deleteDialog.inputPrompt"> - 0<code className="mx-2">{{ name }}</code>2 - </Trans> - ); - }} - inputScheme={[ - { - type: "text", - validator: (value) => { - if (value !== name) { - return "timeline.deleteDialog.notMatch"; - } else { - return null; - } - }, - }, - ]} - onProcess={() => { - return timelineService.deleteTimeline(name).toPromise(); - }} - onSuccessAndClose={() => { - history.replace("/"); - }} - /> - ); -}; - -export default TimelineDeleteDialog; diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx deleted file mode 100644 index 2d787709..00000000 --- a/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { Dropdown, Button } from "react-bootstrap"; - -import { useAvatar } from "@/services/user"; -import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline"; - -import BlobImage from "../common/BlobImage"; -import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI"; -import InfoCardTemplate from "../timeline-common/InfoCardTemplate"; - -export type OrdinaryTimelineManageItem = "delete"; - -export type TimelineInfoCardProps = TimelineCardComponentProps< - OrdinaryTimelineManageItem ->; - -const TimelineInfoCard: React.FC<TimelineInfoCardProps> = (props) => { - const { - timeline, - collapse, - onMember, - onManage, - syncStatus, - toggleCollapse, - } = props; - - const { t } = useTranslation(); - - const avatar = useAvatar(timeline?.owner?.username); - - return ( - <InfoCardTemplate - className={props.className} - syncStatus={syncStatus} - collapse={collapse} - toggleCollapse={toggleCollapse} - > - <h3 className="text-primary mx-3 d-inline-block align-middle"> - {timeline.name} - </h3> - <div className="d-inline-block align-middle"> - <BlobImage blob={avatar} className="avatar small rounded-circle" /> - {timeline.owner.nickname} - <small className="ml-3 text-secondary"> - @{timeline.owner.username} - </small> - </div> - <p className="mb-0">{timeline.description}</p> - <small className="mt-1 d-block"> - {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])} - </small> - <div className="text-right mt-2"> - {onManage != null ? ( - <Dropdown> - <Dropdown.Toggle variant="outline-primary"> - {t("timeline.manage")} - </Dropdown.Toggle> - <Dropdown.Menu> - <Dropdown.Item onClick={() => onManage("property")}> - {t("timeline.manageItem.property")} - </Dropdown.Item> - <Dropdown.Item onClick={onMember}> - {t("timeline.manageItem.member")} - </Dropdown.Item> - <Dropdown.Divider /> - <Dropdown.Item - className="text-danger" - onClick={() => onManage("delete")} - > - {t("timeline.manageItem.delete")} - </Dropdown.Item> - </Dropdown.Menu> - </Dropdown> - ) : ( - <Button variant="outline-primary" onClick={onMember}> - {t("timeline.memberButton")} - </Button> - )} - </div> - </InfoCardTemplate> - ); -}; - -export default TimelineInfoCard; diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx deleted file mode 100644 index 67ea699e..00000000 --- a/Timeline/ClientApp/src/app/views/timeline/TimelinePageUI.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; - -import TimelinePageTemplateUI, { - TimelinePageTemplateUIProps, -} from "../timeline-common/TimelinePageTemplateUI"; - -import TimelineInfoCard, { - OrdinaryTimelineManageItem, -} from "./TimelineInfoCard"; - -export type TimelinePageUIProps = Omit< - TimelinePageTemplateUIProps<OrdinaryTimelineManageItem>, - "CardComponent" ->; - -const TimelinePageUI: React.FC<TimelinePageUIProps> = (props) => { - return <TimelinePageTemplateUI {...props} CardComponent={TimelineInfoCard} />; -}; - -export default TimelinePageUI; diff --git a/Timeline/ClientApp/src/app/views/timeline/index.tsx b/Timeline/ClientApp/src/app/views/timeline/index.tsx deleted file mode 100644 index 225a1a59..00000000 --- a/Timeline/ClientApp/src/app/views/timeline/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import { useParams } from "react-router"; - -import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate"; - -import TimelinePageUI from "./TimelinePageUI"; -import { OrdinaryTimelineManageItem } from "./TimelineInfoCard"; -import TimelineDeleteDialog from "./TimelineDeleteDialog"; - -const TimelinePage: React.FC = (_) => { - const { name } = useParams<{ name: string }>(); - - const [dialog, setDialog] = React.useState<OrdinaryTimelineManageItem | null>( - null - ); - - let dialogElement: React.ReactElement | undefined; - if (dialog === "delete") { - dialogElement = ( - <TimelineDeleteDialog open close={() => setDialog(null)} name={name} /> - ); - } - - return ( - <> - <TimelinePageTemplate - name={name} - UiComponent={TimelinePageUI} - onManage={(item) => setDialog(item)} - notFoundI18nKey="timeline.timelineNotExist" - /> - {dialogElement} - </> - ); -}; - -export default TimelinePage; diff --git a/Timeline/ClientApp/src/app/views/timeline/timeline.sass b/Timeline/ClientApp/src/app/views/timeline/timeline.sass deleted file mode 100644 index e69de29b..00000000 --- a/Timeline/ClientApp/src/app/views/timeline/timeline.sass +++ /dev/null diff --git a/Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx b/Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx deleted file mode 100644 index ffa2218b..00000000 --- a/Timeline/ClientApp/src/app/views/user/ChangeAvatarDialog.tsx +++ /dev/null @@ -1,302 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { useTranslation } from "react-i18next"; -import { AxiosError } from "axios"; -import { Modal, Row, Button } from "react-bootstrap"; - -import { UiLogicError } from "@/common"; - -import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; - -export interface ChangeAvatarDialogProps { - open: boolean; - close: () => void; - process: (blob: Blob) => Promise<void>; -} - -const ChangeAvatarDialog: React.FC<ChangeAvatarDialogProps> = (props) => { - const { t } = useTranslation(); - - const [file, setFile] = React.useState<File | null>(null); - const [fileUrl, setFileUrl] = React.useState<string | null>(null); - const [clip, setClip] = React.useState<Clip | null>(null); - const [ - cropImgElement, - setCropImgElement, - ] = React.useState<HTMLImageElement | null>(null); - const [resultBlob, setResultBlob] = React.useState<Blob | null>(null); - const [resultUrl, setResultUrl] = React.useState<string | null>(null); - - const [state, setState] = React.useState< - | "select" - | "crop" - | "processcrop" - | "preview" - | "uploading" - | "success" - | "error" - >("select"); - - const [message, setMessage] = useState< - string | { type: "custom"; text: string } | null - >("userPage.dialogChangeAvatar.prompt.select"); - - const trueMessage = - message == null - ? null - : typeof message === "string" - ? t(message) - : message.text; - - const closeDialog = props.close; - - const close = React.useCallback((): void => { - if (!(state === "uploading")) { - closeDialog(); - } - }, [state, closeDialog]); - - useEffect(() => { - if (file != null) { - const url = URL.createObjectURL(file); - setClip(null); - setFileUrl(url); - setState("crop"); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setFileUrl(null); - setState("select"); - } - }, [file]); - - React.useEffect(() => { - if (resultBlob != null) { - const url = URL.createObjectURL(resultBlob); - setResultUrl(url); - setState("preview"); - return () => { - URL.revokeObjectURL(url); - }; - } else { - setResultUrl(null); - } - }, [resultBlob]); - - const onSelectFile = React.useCallback( - (e: React.ChangeEvent<HTMLInputElement>): void => { - const files = e.target.files; - if (files == null || files.length === 0) { - setFile(null); - } else { - setFile(files[0]); - } - }, - [] - ); - - const onCropNext = React.useCallback(() => { - if ( - cropImgElement == null || - clip == null || - clip.width === 0 || - file == null - ) { - throw new UiLogicError(); - } - - setState("processcrop"); - void applyClipToImage(cropImgElement, clip, file.type).then((b) => { - setResultBlob(b); - }); - }, [cropImgElement, clip, file]); - - const onCropPrevious = React.useCallback(() => { - setFile(null); - setState("select"); - }, []); - - const onPreviewPrevious = React.useCallback(() => { - setResultBlob(null); - setState("crop"); - }, []); - - const process = props.process; - - const upload = React.useCallback(() => { - if (resultBlob == null) { - throw new UiLogicError(); - } - - setState("uploading"); - process(resultBlob).then( - () => { - setState("success"); - }, - (e: unknown) => { - setState("error"); - setMessage({ type: "custom", text: (e as AxiosError).message }); - } - ); - }, [resultBlob, process]); - - const createPreviewRow = (): React.ReactElement => { - if (resultUrl == null) { - throw new UiLogicError(); - } - return ( - <Row className="justify-content-center"> - <img - className="change-avatar-img" - src={resultUrl} - alt={t("userPage.dialogChangeAvatar.previewImgAlt")} - /> - </Row> - ); - }; - - return ( - <Modal show={props.open} onHide={close}> - <Modal.Header> - <Modal.Title> {t("userPage.dialogChangeAvatar.title")}</Modal.Title> - </Modal.Header> - {(() => { - if (state === "select") { - return ( - <> - <Modal.Body className="container"> - <Row>{t("userPage.dialogChangeAvatar.prompt.select")}</Row> - <Row> - <input type="file" accept="image/*" onChange={onSelectFile} /> - </Row> - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={close}> - {t("operationDialog.cancel")} - </Button> - </Modal.Footer> - </> - ); - } else if (state === "crop") { - if (fileUrl == null) { - throw new UiLogicError(); - } - return ( - <> - <Modal.Body className="container"> - <Row className="justify-content-center"> - <ImageCropper - clip={clip} - onChange={setClip} - imageUrl={fileUrl} - imageElementCallback={setCropImgElement} - /> - </Row> - <Row>{t("userPage.dialogChangeAvatar.prompt.crop")}</Row> - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={close}> - {t("operationDialog.cancel")} - </Button> - <Button variant="secondary" onClick={onCropPrevious}> - {t("operationDialog.previousStep")} - </Button> - <Button - color="primary" - onClick={onCropNext} - disabled={ - cropImgElement == null || clip == null || clip.width === 0 - } - > - {t("operationDialog.nextStep")} - </Button> - </Modal.Footer> - </> - ); - } else if (state === "processcrop") { - return ( - <> - <Modal.Body className="container"> - <Row> - {t("userPage.dialogChangeAvatar.prompt.processingCrop")} - </Row> - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={close}> - {t("operationDialog.cancel")} - </Button> - <Button variant="secondary" onClick={onPreviewPrevious}> - {t("operationDialog.previousStep")} - </Button> - </Modal.Footer> - </> - ); - } else if (state === "preview") { - return ( - <> - <Modal.Body className="container"> - {createPreviewRow()} - <Row>{t("userPage.dialogChangeAvatar.prompt.preview")}</Row> - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={close}> - {t("operationDialog.cancel")} - </Button> - <Button variant="secondary" onClick={onPreviewPrevious}> - {t("operationDialog.previousStep")} - </Button> - <Button variant="primary" onClick={upload}> - {t("userPage.dialogChangeAvatar.upload")} - </Button> - </Modal.Footer> - </> - ); - } else if (state === "uploading") { - return ( - <> - <Modal.Body className="container"> - {createPreviewRow()} - <Row>{t("userPage.dialogChangeAvatar.prompt.uploading")}</Row> - </Modal.Body> - <Modal.Footer></Modal.Footer> - </> - ); - } else if (state === "success") { - return ( - <> - <Modal.Body className="container"> - <Row className="p-4 text-success"> - {t("operationDialog.success")} - </Row> - </Modal.Body> - <Modal.Footer> - <Button variant="success" onClick={close}> - {t("operationDialog.ok")} - </Button> - </Modal.Footer> - </> - ); - } else { - return ( - <> - <Modal.Body className="container"> - {createPreviewRow()} - <Row className="text-danger">{trueMessage}</Row> - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={close}> - {t("operationDialog.cancel")} - </Button> - <Button variant="primary" onClick={upload}> - {t("operationDialog.retry")} - </Button> - </Modal.Footer> - </> - ); - } - })()} - </Modal> - ); -}; - -export default ChangeAvatarDialog; diff --git a/Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx b/Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx deleted file mode 100644 index 251b18c5..00000000 --- a/Timeline/ClientApp/src/app/views/user/ChangeNicknameDialog.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; - -import OperationDialog from "../common/OperationDialog"; - -export interface ChangeNicknameDialogProps { - open: boolean; - close: () => void; - onProcess: (newNickname: string) => Promise<void>; -} - -const ChangeNicknameDialog: React.FC<ChangeNicknameDialogProps> = (props) => { - return ( - <OperationDialog - open={props.open} - title="userPage.dialogChangeNickname.title" - titleColor="default" - inputScheme={[ - { type: "text", label: "userPage.dialogChangeNickname.inputLabel" }, - ]} - onProcess={([newNickname]) => { - return props.onProcess(newNickname as string); - }} - close={props.close} - /> - ); -}; - -export default ChangeNicknameDialog; diff --git a/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx b/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx deleted file mode 100644 index 888fb18a..00000000 --- a/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; -import { Dropdown, Button } from "react-bootstrap"; - -import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline"; -import { useAvatar } from "@/services/user"; - -import BlobImage from "../common/BlobImage"; -import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI"; -import InfoCardTemplate from "../timeline-common/InfoCardTemplate"; - -export type PersonalTimelineManageItem = "avatar" | "nickname"; - -export type UserInfoCardProps = TimelineCardComponentProps< - PersonalTimelineManageItem ->; - -const UserInfoCard: React.FC<UserInfoCardProps> = (props) => { - const { - timeline, - collapse, - onMember, - onManage, - syncStatus, - toggleCollapse, - } = props; - const { t } = useTranslation(); - - const avatar = useAvatar(timeline?.owner?.username); - - return ( - <InfoCardTemplate - className={props.className} - syncStatus={syncStatus} - collapse={collapse} - toggleCollapse={toggleCollapse} - > - <div> - <BlobImage blob={avatar} className="avatar" /> - {timeline.owner.nickname} - <small className="ml-3 text-secondary"> - @{timeline.owner.username} - </small> - </div> - <p className="mb-0">{timeline.description}</p> - <small className="mt-1 d-block"> - {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])} - </small> - <div className="text-right mt-2"> - {onManage != null ? ( - <Dropdown> - <Dropdown.Toggle variant="outline-primary"> - {t("timeline.manage")} - </Dropdown.Toggle> - <Dropdown.Menu> - <Dropdown.Item onClick={() => onManage("nickname")}> - {t("timeline.manageItem.nickname")} - </Dropdown.Item> - <Dropdown.Item onClick={() => onManage("avatar")}> - {t("timeline.manageItem.avatar")} - </Dropdown.Item> - <Dropdown.Item onClick={() => onManage("property")}> - {t("timeline.manageItem.property")} - </Dropdown.Item> - <Dropdown.Item onClick={onMember}> - {t("timeline.manageItem.member")} - </Dropdown.Item> - </Dropdown.Menu> - </Dropdown> - ) : ( - <Button variant="outline-primary" onClick={onMember}> - {t("timeline.memberButton")} - </Button> - )} - </div> - </InfoCardTemplate> - ); -}; - -export default UserInfoCard; diff --git a/Timeline/ClientApp/src/app/views/user/UserPageUI.tsx b/Timeline/ClientApp/src/app/views/user/UserPageUI.tsx deleted file mode 100644 index d405399c..00000000 --- a/Timeline/ClientApp/src/app/views/user/UserPageUI.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; - -import TimelinePageTemplateUI, { - TimelinePageTemplateUIProps, -} from "../timeline-common/TimelinePageTemplateUI"; - -import UserInfoCard, { PersonalTimelineManageItem } from "./UserInfoCard"; - -export type UserPageUIProps = Omit< - TimelinePageTemplateUIProps<PersonalTimelineManageItem>, - "CardComponent" ->; - -const UserPageUI: React.FC<UserPageUIProps> = (props) => { - return <TimelinePageTemplateUI {...props} CardComponent={UserInfoCard} />; -}; - -export default UserPageUI; diff --git a/Timeline/ClientApp/src/app/views/user/index.tsx b/Timeline/ClientApp/src/app/views/user/index.tsx deleted file mode 100644 index 7c0b1563..00000000 --- a/Timeline/ClientApp/src/app/views/user/index.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React, { useState } from "react"; -import { useParams } from "react-router"; - -import { UiLogicError } from "@/common"; -import { useUser, userInfoService } from "@/services/user"; - -import TimelinePageTemplate from "../timeline-common/TimelinePageTemplate"; - -import UserPageUI from "./UserPageUI"; -import { PersonalTimelineManageItem } from "./UserInfoCard"; -import ChangeNicknameDialog from "./ChangeNicknameDialog"; -import ChangeAvatarDialog from "./ChangeAvatarDialog"; - -const UserPage: React.FC = (_) => { - const { username } = useParams<{ username: string }>(); - - const user = useUser(); - - const [dialog, setDialog] = useState<null | PersonalTimelineManageItem>(null); - - let dialogElement: React.ReactElement | undefined; - - const closeDialogHandler = (): void => { - setDialog(null); - }; - - if (dialog === "nickname") { - if (user == null) { - throw new UiLogicError("Change nickname without login."); - } - - dialogElement = ( - <ChangeNicknameDialog - open - close={closeDialogHandler} - onProcess={(newNickname) => - userInfoService.setNickname(username, newNickname) - } - /> - ); - } else if (dialog === "avatar") { - if (user == null) { - throw new UiLogicError("Change avatar without login."); - } - - dialogElement = ( - <ChangeAvatarDialog - open - close={closeDialogHandler} - process={(file) => userInfoService.setAvatar(username, file)} - /> - ); - } - - const onManage = React.useCallback((item: PersonalTimelineManageItem) => { - setDialog(item); - }, []); - - return ( - <> - <TimelinePageTemplate - name={`@${username}`} - UiComponent={UserPageUI} - onManage={onManage} - notFoundI18nKey="timeline.userNotExist" - /> - {dialogElement} - </> - ); -}; - -export default UserPage; diff --git a/Timeline/ClientApp/src/app/views/user/user.sass b/Timeline/ClientApp/src/app/views/user/user.sass deleted file mode 100644 index 5b7fcae7..00000000 --- a/Timeline/ClientApp/src/app/views/user/user.sass +++ /dev/null @@ -1,7 +0,0 @@ -.change-avatar-cropper-row - max-height: 400px - -.change-avatar-img - min-width: 50% - max-width: 100% - max-height: 400px diff --git a/Timeline/ClientApp/src/sw/sw.ts b/Timeline/ClientApp/src/sw/sw.ts deleted file mode 100644 index d6202f36..00000000 --- a/Timeline/ClientApp/src/sw/sw.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { precacheAndRoute, matchPrecache } from "workbox-precaching"; -import { setDefaultHandler } from "workbox-routing"; -import { NetworkOnly } from "workbox-strategies"; - -declare let self: ServiceWorkerGlobalScope; - -self.addEventListener("message", (event) => { - if (event.data && (event.data as { type: string }).type === "SKIP_WAITING") { - void self.skipWaiting(); - } -}); - -precacheAndRoute(self.__WB_MANIFEST); - -const networkOnly = new NetworkOnly(); - -setDefaultHandler((options) => { - const { request, url } = options; - if (url && url.pathname.startsWith("/api/")) { - return networkOnly.handle(options); - } - - if (request instanceof Request && request.destination === "document") - return matchPrecache("/index.html").then((r) => - r == null ? Response.error() : r - ); - else return networkOnly.handle(options); -}); diff --git a/Timeline/ClientApp/src/sw/tsconfig.json b/Timeline/ClientApp/src/sw/tsconfig.json deleted file mode 100644 index aac99e59..00000000 --- a/Timeline/ClientApp/src/sw/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "lib": [ - "esnext", - "webworker" - ] - }, - "include": [ - "." - ] -} diff --git a/Timeline/ClientApp/src/tsconfig.json b/Timeline/ClientApp/src/tsconfig.json deleted file mode 100644 index 6937be63..00000000 --- a/Timeline/ClientApp/src/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "esnext", - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "sourceMap": true, - "baseUrl": "./", - "paths": { - "@/*": [ - "app/*" - ] - } - } -} diff --git a/Timeline/ClientApp/webpack.common.js b/Timeline/ClientApp/webpack.common.js deleted file mode 100644 index 3779003e..00000000 --- a/Timeline/ClientApp/webpack.common.js +++ /dev/null @@ -1,86 +0,0 @@ -const path = require("path");
-const HtmlWebpackPlugin = require("html-webpack-plugin");
-const postcssPresetEnv = require("postcss-preset-env");
-const Config = require("webpack-chain");
-
-const config = new Config();
-
-config.entry("index").add(path.resolve(__dirname, "src/app/index.tsx"));
-
-config.module
- .rule("ts")
- .test(/\.ts(x?)$/)
- .exclude.add(/node_modules/)
- .end()
- .use("babel")
- .loader("babel-loader")
- .end()
- .use("ts")
- .loader("ts-loader")
- .end();
-
-config.module
- .rule("js")
- .test(/\.js(x?)$/)
- .exclude.add(/node_modules/)
- .end()
- .use("babel")
- .loader("babel-loader")
- .end();
-
-config.module
- .rule("css")
- .test(/\.css$/)
- .use("css")
- .loader("css-loader")
- .end()
- .use("postcss")
- .loader("postcss-loader")
- .end();
-
-config.module
- .rule("sass")
- .test(/\.(scss|sass)$/)
- .use("css")
- .loader("css-loader")
- .end()
- .use("postcss")
- .loader("postcss-loader")
- .end()
- .use("sass")
- .loader("sass-loader")
- .end();
-
-config.module
- .rule("file")
- .test(/\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot)$/i)
- .use("url")
- .loader("url-loader")
- .options({
- limit: 8192,
- });
-
-config.resolve.extensions
- .add("*")
- .add(".js")
- .add(".jsx")
- .add(".ts")
- .add(".tsx")
- .end();
-
-config.resolve.alias.set("@", path.resolve(__dirname, "src/app"));
-
-config.output
- .path(path.resolve(__dirname, "dist/"))
- .filename("[name].[contenthash].js")
- .chunkFilename("[name].[contenthash].js")
- .publicPath("/");
-
-config.plugin("html").use(HtmlWebpackPlugin, [
- {
- template: "src/app/index.ejs",
- title: "Timeline",
- },
-]);
-
-module.exports = config;
diff --git a/Timeline/ClientApp/webpack.config.dev.js b/Timeline/ClientApp/webpack.config.dev.js deleted file mode 100644 index c88e1aaf..00000000 --- a/Timeline/ClientApp/webpack.config.dev.js +++ /dev/null @@ -1,52 +0,0 @@ -const path = require("path");
-const webpack = require("webpack");
-
-const config = require("./webpack.common");
-
-config.mode("development");
-
-config.entry("index").add("react-hot-loader/patch");
-
-config.module
- .rule("ts")
- .use("babel")
- .options({
- plugins: ["react-hot-loader/babel"],
- });
-
-config.module
- .rule("js")
- .use("babel")
- .options({
- plugins: ["react-hot-loader/babel"],
- });
-
-config.module
- .rule("css")
- .use("style")
- .before("css")
- .loader("style-loader")
- .end();
-
-config.module
- .rule("sass")
- .use("style")
- .before("css")
- .loader("style-loader")
- .end();
-
-config.devtool("eval-cheap-module-source-map");
-
-config.resolve.alias.set("react-dom", "@hot-loader/react-dom");
-
-config.devServer
- .contentBase(path.resolve(__dirname, "public/"))
- .host("0.0.0.0")
- .port(3000)
- .historyApiFallback(true)
- .hotOnly(true)
- .allowedHosts.add(".myide.io");
-
-config.plugin("hot").use(webpack.HotModuleReplacementPlugin);
-
-module.exports = config.toConfig();
diff --git a/Timeline/ClientApp/webpack.config.prod.js b/Timeline/ClientApp/webpack.config.prod.js deleted file mode 100644 index 188cb940..00000000 --- a/Timeline/ClientApp/webpack.config.prod.js +++ /dev/null @@ -1,53 +0,0 @@ -const path = require("path");
-const { CleanWebpackPlugin } = require("clean-webpack-plugin");
-const CopyPlugin = require("copy-webpack-plugin");
-const WorkboxPlugin = require("workbox-webpack-plugin");
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-
-const config = require("./webpack.common");
-
-config.mode("production");
-
-config
- .entry("index")
- .add(path.resolve(__dirname, "src/app/service-worker.tsx"));
-
-config.module
- .rule("css")
- .use("mini-css-extract")
- .before("css")
- .loader(MiniCssExtractPlugin.loader)
- .end();
-
-config.module
- .rule("sass")
- .use("mini-css-extract")
- .before("css")
- .loader(MiniCssExtractPlugin.loader)
- .end();
-
-config.devtool("source-map");
-
-config.plugin("mini-css-extract").use(MiniCssExtractPlugin);
-
-config.plugin("clean").use(CleanWebpackPlugin);
-
-config.plugin("copy").use(CopyPlugin, [
- {
- patterns: [
- {
- from: path.resolve(__dirname, "public/"),
- to: path.resolve(__dirname, "dist/"),
- },
- ],
- },
-]);
-
-config.plugin("workbox").use(WorkboxPlugin.InjectManifest, [
- {
- swSrc: path.resolve(__dirname, "src/sw/sw.ts"),
- maximumFileSizeToCacheInBytes: 15000000,
- },
-]);
-
-module.exports = config.toConfig();
diff --git a/Timeline/Configs/ApplicationConfiguration.cs b/Timeline/Configs/ApplicationConfiguration.cs deleted file mode 100644 index df281adb..00000000 --- a/Timeline/Configs/ApplicationConfiguration.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Timeline.Configs
-{
- public static class ApplicationConfiguration
- {
- public const string WorkDirKey = "WorkDir";
- public const string DefaultWorkDir = "/timeline";
- public const string DatabaseFileName = "timeline.db";
- public const string DatabaseBackupDirectoryName = "backup";
- public const string DisableFrontEndKey = "DisableFrontEnd";
- public const string UseMockFrontEndKey = "UseMockFrontEnd";
- public const string UseProxyFrontEndKey = "UseProxyFrontEnd";
- }
-}
diff --git a/Timeline/Configs/JwtConfiguration.cs b/Timeline/Configs/JwtConfiguration.cs deleted file mode 100644 index af8052de..00000000 --- a/Timeline/Configs/JwtConfiguration.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Timeline.Configs
-{
- public class JwtConfiguration
- {
- public string Issuer { get; set; } = default!;
- public string Audience { get; set; } = default!;
-
- /// <summary>
- /// Set the default value of expire offset of jwt token.
- /// Unit is second. Default is 3600 * 24 seconds, aka 1 day.
- /// </summary>
- public long DefaultExpireOffset { get; set; } = 3600 * 24;
- }
-}
diff --git a/Timeline/Controllers/ControllerAuthExtensions.cs b/Timeline/Controllers/ControllerAuthExtensions.cs deleted file mode 100644 index 00a65454..00000000 --- a/Timeline/Controllers/ControllerAuthExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.AspNetCore.Mvc;
-using System;
-using System.Security.Claims;
-using Timeline.Auth;
-using static Timeline.Resources.Controllers.ControllerAuthExtensions;
-
-namespace Timeline.Controllers
-{
- public static class ControllerAuthExtensions
- {
- public static bool IsAdministrator(this ControllerBase controller)
- {
- return controller.User != null && controller.User.IsAdministrator();
- }
-
- public static long GetUserId(this ControllerBase controller)
- {
- var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
- if (claim == null)
- throw new InvalidOperationException(ExceptionNoUserIdentifierClaim);
-
- if (long.TryParse(claim.Value, out var value))
- return value;
-
- throw new InvalidOperationException(ExceptionUserIdentifierClaimBadFormat);
- }
-
- public static long? GetOptionalUserId(this ControllerBase controller)
- {
- var claim = controller.User.FindFirst(ClaimTypes.NameIdentifier);
- if (claim == null)
- return null;
-
- if (long.TryParse(claim.Value, out var value))
- return value;
-
- throw new InvalidOperationException(ExceptionUserIdentifierClaimBadFormat);
- }
- }
-}
diff --git a/Timeline/Controllers/Testing/TestingAuthController.cs b/Timeline/Controllers/Testing/TestingAuthController.cs deleted file mode 100644 index 4d3b3ec7..00000000 --- a/Timeline/Controllers/Testing/TestingAuthController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Timeline.Auth;
-
-namespace Timeline.Controllers.Testing
-{
- [Route("testing/auth")]
- [ApiController]
- public class TestingAuthController : Controller
- {
- [HttpGet("[action]")]
- [Authorize]
- public ActionResult Authorize()
- {
- return Ok();
- }
-
- [HttpGet("[action]")]
- [UserAuthorize]
- public new ActionResult User()
- {
- return Ok();
- }
-
- [HttpGet("[action]")]
- [AdminAuthorize]
- public ActionResult Admin()
- {
- return Ok();
- }
- }
-}
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs deleted file mode 100644 index 9a3147ea..00000000 --- a/Timeline/Controllers/TimelineController.cs +++ /dev/null @@ -1,491 +0,0 @@ -using AutoMapper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Threading.Tasks;
-using Timeline.Filters;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operations about timeline.
- /// </summary>
- [ApiController]
- [CatchTimelineNotExistException]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class TimelineController : Controller
- {
- private readonly ILogger<TimelineController> _logger;
-
- private readonly IUserService _userService;
- private readonly ITimelineService _service;
-
- private readonly IMapper _mapper;
-
- /// <summary>
- ///
- /// </summary>
- public TimelineController(ILogger<TimelineController> logger, IUserService userService, ITimelineService service, IMapper mapper)
- {
- _logger = logger;
- _userService = userService;
- _service = service;
- _mapper = mapper;
- }
-
- /// <summary>
- /// List all timelines.
- /// </summary>
- /// <param name="relate">A username. If set, only timelines related to the user will return.</param>
- /// <param name="relateType">Specify the relation type, may be 'own' or 'join'. If not set, both type will return.</param>
- /// <param name="visibility">"Private" or "Register" or "Public". If set, only timelines whose visibility is specified one will return.</param>
- /// <returns>The timeline list.</returns>
- [HttpGet("timelines")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task<ActionResult<List<TimelineInfo>>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType, [FromQuery] string? visibility)
- {
- List<TimelineVisibility>? visibilityFilter = null;
- if (visibility != null)
- {
- visibilityFilter = new List<TimelineVisibility>();
- var items = visibility.Split('|');
- foreach (var item in items)
- {
- if (item.Equals(nameof(TimelineVisibility.Private), StringComparison.OrdinalIgnoreCase))
- {
- if (!visibilityFilter.Contains(TimelineVisibility.Private))
- visibilityFilter.Add(TimelineVisibility.Private);
- }
- else if (item.Equals(nameof(TimelineVisibility.Register), StringComparison.OrdinalIgnoreCase))
- {
- if (!visibilityFilter.Contains(TimelineVisibility.Register))
- visibilityFilter.Add(TimelineVisibility.Register);
- }
- else if (item.Equals(nameof(TimelineVisibility.Public), StringComparison.OrdinalIgnoreCase))
- {
- if (!visibilityFilter.Contains(TimelineVisibility.Public))
- visibilityFilter.Add(TimelineVisibility.Public);
- }
- else
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_QueryVisibilityUnknown, item));
- }
- }
- }
-
- TimelineUserRelationship? relationship = null;
- if (relate != null)
- {
- try
- {
- var relatedUserId = await _userService.GetUserIdByUsername(relate);
-
- relationship = new TimelineUserRelationship(relateType switch
- {
- "own" => TimelineUserRelationshipType.Own,
- "join" => TimelineUserRelationshipType.Join,
- _ => TimelineUserRelationshipType.Default
- }, relatedUserId);
- }
- catch (UserNotExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.QueryRelateNotExist());
- }
- }
-
- var timelines = await _service.GetTimelines(relationship, visibilityFilter);
- var result = _mapper.Map<List<TimelineInfo>>(timelines);
- return result;
- }
-
- /// <summary>
- /// Get info of a timeline.
- /// </summary>
- /// <param name="name">The timeline name.</param>
- /// <param name="checkUniqueId">A unique id. If specified and if-modified-since is also specified, the timeline info will return when unique id is not the specified one even if it is not modified.</param>
- /// <param name="queryIfModifiedSince">Same effect as If-Modified-Since header and take precedence than it.</param>
- /// <param name="headerIfModifiedSince">If specified, will return 304 if not modified.</param>
- /// <returns>The timeline info.</returns>
- [HttpGet("timelines/{name}")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status304NotModified)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][GeneralTimelineName] string name, [FromQuery] string? checkUniqueId, [FromQuery(Name = "ifModifiedSince")] DateTime? queryIfModifiedSince, [FromHeader(Name = "If-Modified-Since")] DateTime? headerIfModifiedSince)
- {
- DateTime? ifModifiedSince = null;
- if (queryIfModifiedSince.HasValue)
- {
- ifModifiedSince = queryIfModifiedSince.Value;
- }
- else if (headerIfModifiedSince != null)
- {
- ifModifiedSince = headerIfModifiedSince.Value;
- }
-
- bool returnNotModified = false;
-
- if (ifModifiedSince.HasValue)
- {
- var lastModified = await _service.GetTimelineLastModifiedTime(name);
- if (lastModified < ifModifiedSince.Value)
- {
- if (checkUniqueId != null)
- {
- var uniqueId = await _service.GetTimelineUniqueId(name);
- if (uniqueId == checkUniqueId)
- {
- returnNotModified = true;
- }
- }
- else
- {
- returnNotModified = true;
- }
- }
- }
-
- if (returnNotModified)
- {
- return StatusCode(StatusCodes.Status304NotModified);
- }
- else
- {
- var timeline = await _service.GetTimeline(name);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
- }
- }
-
- /// <summary>
- /// Get posts of a timeline.
- /// </summary>
- /// <param name="name">The name of the timeline.</param>
- /// <param name="modifiedSince">If set, only posts modified since the time will return.</param>
- /// <param name="includeDeleted">If set to true, deleted post will also return.</param>
- /// <returns>The post list.</returns>
- [HttpGet("timelines/{name}/posts")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<List<TimelinePostInfo>>> PostListGet([FromRoute][GeneralTimelineName] string name, [FromQuery] DateTime? modifiedSince, [FromQuery] bool? includeDeleted)
- {
- if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- List<TimelinePost> posts = await _service.GetPosts(name, modifiedSince, includeDeleted ?? false);
-
- var result = _mapper.Map<List<TimelinePostInfo>>(posts);
- return result;
- }
-
- /// <summary>
- /// Get the data of a post. Usually a image post.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="id">The id of the post.</param>
- /// <param name="ifNoneMatch">If-None-Match header.</param>
- /// <returns>The data.</returns>
- [HttpGet("timelines/{name}/posts/{id}/data")]
- [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")]
- [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> PostDataGet([FromRoute][GeneralTimelineName] string name, [FromRoute] long id, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
- {
- _ = ifNoneMatch;
- if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- return await DataCacheHelper.GenerateActionResult(this, () => _service.GetPostDataETag(name, id), async () =>
- {
- var data = await _service.GetPostData(name, id);
- return data;
- });
- }
- catch (TimelinePostNotExistException)
- {
- return NotFound(ErrorResponse.TimelineController.PostNotExist());
- }
- catch (TimelinePostNoDataException)
- {
- return BadRequest(ErrorResponse.TimelineController.PostNoData());
- }
- }
-
- /// <summary>
- /// Create a new post.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="body"></param>
- /// <returns>Info of new post.</returns>
- [HttpPost("timelines/{name}/posts")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePostCreateRequest body)
- {
- var id = this.GetUserId();
- if (!this.IsAdministrator() && !await _service.IsMemberOf(name, id))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- var content = body.Content;
-
- TimelinePost post;
-
- if (content.Type == TimelinePostContentTypes.Text)
- {
- var text = content.Text;
- if (text == null)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_TextContentTextRequired));
- }
- post = await _service.CreateTextPost(name, id, text, body.Time);
- }
- else if (content.Type == TimelinePostContentTypes.Image)
- {
- var base64Data = content.Data;
- if (base64Data == null)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataRequired));
- }
- byte[] data;
- try
- {
- data = Convert.FromBase64String(base64Data);
- }
- catch (FormatException)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotBase64));
- }
-
- try
- {
- post = await _service.CreateImagePost(name, id, data, body.Time);
- }
- catch (ImageException)
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ImageContentDataNotImage));
- }
- }
- else
- {
- return BadRequest(ErrorResponse.Common.CustomMessage_InvalidModel(Resources.Messages.TimelineController_ContentUnknownType));
- }
-
- var result = _mapper.Map<TimelinePostInfo>(post);
- return result;
- }
-
- /// <summary>
- /// Delete a post.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="id">Post id.</param>
- /// <returns>Info of deletion.</returns>
- [HttpDelete("timelines/{name}/posts/{id}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<CommonDeleteResponse>> PostDelete([FromRoute][GeneralTimelineName] string name, [FromRoute] long id)
- {
- if (!this.IsAdministrator() && !await _service.HasPostModifyPermission(name, id, this.GetUserId()))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
- try
- {
- await _service.DeletePost(name, id);
- return CommonDeleteResponse.Delete();
- }
- catch (TimelinePostNotExistException)
- {
- return CommonDeleteResponse.NotExist();
- }
- }
-
- /// <summary>
- /// Change properties of a timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="body"></param>
- /// <returns>The new info.</returns>
- [HttpPatch("timelines/{name}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<TimelineInfo>> TimelinePatch([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePatchRequest body)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
- await _service.ChangeProperty(name, _mapper.Map<TimelineChangePropertyRequest>(body));
- var timeline = await _service.GetTimeline(name);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
- }
-
- /// <summary>
- /// Add a member to timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="member">The new member's username.</param>
- [HttpPut("timelines/{name}/members/{member}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult> TimelineMemberPut([FromRoute][GeneralTimelineName] string name, [FromRoute][Username] string member)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- await _service.ChangeMember(name, new List<string> { member }, null);
- return Ok();
- }
- catch (UserNotExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.MemberPut_NotExist());
- }
- }
-
- /// <summary>
- /// Remove a member from timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <param name="member">The member's username.</param>
- [HttpDelete("timelines/{name}/members/{member}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult> TimelineMemberDelete([FromRoute][GeneralTimelineName] string name, [FromRoute][Username] string member)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- await _service.ChangeMember(name, null, new List<string> { member });
- return Ok(CommonDeleteResponse.Delete());
- }
- catch (UserNotExistException)
- {
- return Ok(CommonDeleteResponse.NotExist());
- }
- }
-
- /// <summary>
- /// Create a timeline.
- /// </summary>
- /// <param name="body"></param>
- /// <returns>Info of new timeline.</returns>
- [HttpPost("timelines")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task<ActionResult<TimelineInfo>> TimelineCreate([FromBody] TimelineCreateRequest body)
- {
- var userId = this.GetUserId();
-
- try
- {
- var timeline = await _service.CreateTimeline(body.Name, userId);
- var result = _mapper.Map<TimelineInfo>(timeline);
- return result;
- }
- catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.Timeline)
- {
- return BadRequest(ErrorResponse.TimelineController.NameConflict());
- }
- }
-
- /// <summary>
- /// Delete a timeline.
- /// </summary>
- /// <param name="name">Timeline name.</param>
- /// <returns>Info of deletion.</returns>
- [HttpDelete("timelines/{name}")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<CommonDeleteResponse>> TimelineDelete([FromRoute][TimelineName] string name)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- await _service.DeleteTimeline(name);
- return CommonDeleteResponse.Delete();
- }
- catch (TimelineNotExistException)
- {
- return CommonDeleteResponse.NotExist();
- }
- }
-
- [HttpPost("timelineop/changename")]
- [Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<TimelineInfo>> TimelineOpChangeName([FromBody] TimelineChangeNameRequest body)
- {
- if (!this.IsAdministrator() && !(await _service.HasManagePermission(body.OldName, this.GetUserId())))
- {
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- try
- {
- var timeline = await _service.ChangeTimelineName(body.OldName, body.NewName);
- return Ok(_mapper.Map<TimelineInfo>(timeline));
- }
- catch (EntityAlreadyExistException)
- {
- return BadRequest(ErrorResponse.TimelineController.NameConflict());
- }
- }
- }
-}
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs deleted file mode 100644 index 8f2ca600..00000000 --- a/Timeline/Controllers/TokenController.cs +++ /dev/null @@ -1,142 +0,0 @@ -using AutoMapper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Globalization;
-using System.Threading.Tasks;
-using Timeline.Helpers;
-using Timeline.Models.Http;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Controllers.TokenController;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operation about tokens.
- /// </summary>
- [Route("token")]
- [ApiController]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class TokenController : Controller
- {
- private readonly IUserTokenManager _userTokenManager;
- private readonly ILogger<TokenController> _logger;
- private readonly IClock _clock;
-
- private readonly IMapper _mapper;
-
- /// <summary></summary>
- public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock, IMapper mapper)
- {
- _userTokenManager = userTokenManager;
- _logger = logger;
- _clock = clock;
- _mapper = mapper;
- }
-
- /// <summary>
- /// Create a new token for a user.
- /// </summary>
- /// <returns>Result of token creation.</returns>
- [HttpPost("create")]
- [AllowAnonymous]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task<ActionResult<CreateTokenResponse>> Create([FromBody] CreateTokenRequest request)
- {
- void LogFailure(string reason, Exception? e = null)
- {
- _logger.LogInformation(e, Log.Format(LogCreateFailure,
- ("Reason", reason),
- ("Username", request.Username),
- ("Password", request.Password),
- ("Expire (in days)", request.Expire)
- ));
- }
-
- try
- {
- DateTime? expireTime = null;
- if (request.Expire != null)
- expireTime = _clock.GetCurrentTime().AddDays(request.Expire.Value);
-
- var result = await _userTokenManager.CreateToken(request.Username, request.Password, expireTime);
-
- _logger.LogInformation(Log.Format(LogCreateSuccess,
- ("Username", request.Username),
- ("Expire At", expireTime?.ToString(CultureInfo.CurrentCulture.DateTimeFormat) ?? "default")
- ));
- return Ok(new CreateTokenResponse
- {
- Token = result.Token,
- User = _mapper.Map<UserInfo>(result.User)
- });
- }
- catch (UserNotExistException e)
- {
- LogFailure(LogUserNotExist, e);
- return BadRequest(ErrorResponse.TokenController.Create_BadCredential());
- }
- catch (BadPasswordException e)
- {
- LogFailure(LogBadPassword, e);
- return BadRequest(ErrorResponse.TokenController.Create_BadCredential());
- }
- }
-
- /// <summary>
- /// Verify a token.
- /// </summary>
- /// <returns>Result of token verification.</returns>
- [HttpPost("verify")]
- [AllowAnonymous]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task<ActionResult<VerifyTokenResponse>> Verify([FromBody] VerifyTokenRequest request)
- {
- void LogFailure(string reason, Exception? e = null, params (string, object?)[] otherProperties)
- {
- var properties = new (string, object?)[2 + otherProperties.Length];
- properties[0] = ("Reason", reason);
- properties[1] = ("Token", request.Token);
- otherProperties.CopyTo(properties, 2);
- _logger.LogInformation(e, Log.Format(LogVerifyFailure, properties));
- }
-
- try
- {
- var result = await _userTokenManager.VerifyToken(request.Token);
- _logger.LogInformation(Log.Format(LogVerifySuccess,
- ("Username", result.Username), ("Token", request.Token)));
- return Ok(new VerifyTokenResponse
- {
- User = _mapper.Map<UserInfo>(result)
- });
- }
- catch (UserTokenTimeExpireException e)
- {
- LogFailure(LogVerifyExpire, e, ("Expire Time", e.ExpireTime), ("Verify Time", e.VerifyTime));
- return BadRequest(ErrorResponse.TokenController.Verify_TimeExpired());
- }
- catch (UserTokenBadVersionException e)
- {
- LogFailure(LogVerifyOldVersion, e, ("Token Version", e.TokenVersion), ("Required Version", e.RequiredVersion));
- return BadRequest(ErrorResponse.TokenController.Verify_OldVersion());
-
- }
- catch (UserTokenBadFormatException e)
- {
- LogFailure(LogVerifyBadFormat, e);
- return BadRequest(ErrorResponse.TokenController.Verify_BadFormat());
- }
- catch (UserNotExistException e)
- {
- LogFailure(LogVerifyUserNotExist, e);
- return BadRequest(ErrorResponse.TokenController.Verify_UserNotExist());
- }
- }
- }
-}
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs deleted file mode 100644 index bc4afa30..00000000 --- a/Timeline/Controllers/UserAvatarController.cs +++ /dev/null @@ -1,174 +0,0 @@ -using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Threading.Tasks;
-using Timeline.Auth;
-using Timeline.Filters;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Controllers.UserAvatarController;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operations about user avatar.
- /// </summary>
- [ApiController]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class UserAvatarController : Controller
- {
- private readonly ILogger<UserAvatarController> _logger;
-
- private readonly IUserService _userService;
- private readonly IUserAvatarService _service;
-
- /// <summary>
- ///
- /// </summary>
- public UserAvatarController(ILogger<UserAvatarController> logger, IUserService userService, IUserAvatarService service)
- {
- _logger = logger;
- _userService = userService;
- _service = service;
- }
-
- /// <summary>
- /// Get avatar of a user.
- /// </summary>
- /// <param name="username">Username of the user to get avatar of.</param>
- /// <param name="ifNoneMatch">If-None-Match header.</param>
- /// <returns>Avatar data.</returns>
- [HttpGet("users/{username}/avatar")]
- [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")]
- [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
- [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<IActionResult> Get([FromRoute][Username] string username, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
- {
- _ = ifNoneMatch;
- long id;
- try
- {
- id = await _userService.GetUserIdByUsername(username);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
- return NotFound(ErrorResponse.UserCommon.NotExist());
- }
-
- return await DataCacheHelper.GenerateActionResult(this, () => _service.GetAvatarETag(id), async () =>
- {
- var avatar = await _service.GetAvatar(id);
- return avatar.ToCacheableData();
- });
- }
-
- /// <summary>
- /// Set avatar of a user. You have to be administrator to change other's.
- /// </summary>
- /// <param name="username">Username of the user to set avatar of.</param>
- /// <param name="body">The avatar data.</param>
- [HttpPut("users/{username}/avatar")]
- [Authorize]
- [Consumes("image/png", "image/jpeg", "image/gif", "image/webp")]
- [MaxContentLength(1000 * 1000 * 10)]
- [ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<IActionResult> Put([FromRoute][Username] string username, [FromBody] ByteData body)
- {
- if (!User.IsAdministrator() && User.Identity.Name != username)
- {
- _logger.LogInformation(Log.Format(LogPutForbid,
- ("Operator Username", User.Identity.Name), ("Username To Put Avatar", username)));
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- long id;
- try
- {
- id = await _userService.GetUserIdByUsername(username);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username)));
- return BadRequest(ErrorResponse.UserCommon.NotExist());
- }
-
- try
- {
- var etag = await _service.SetAvatar(id, new Avatar
- {
- Data = body.Data,
- Type = body.ContentType
- });
-
- _logger.LogInformation(Log.Format(LogPutSuccess,
- ("Username", username), ("Mime Type", Request.ContentType)));
-
- Response.Headers.Append("ETag", new EntityTagHeaderValue($"\"{etag}\"").ToString());
-
- return Ok();
- }
- catch (ImageException e)
- {
- _logger.LogInformation(e, Log.Format(LogPutUserBadFormat, ("Username", username)));
- return BadRequest(e.Error switch
- {
- ImageException.ErrorReason.CantDecode => ErrorResponse.UserAvatar.BadFormat_CantDecode(),
- ImageException.ErrorReason.UnmatchedFormat => ErrorResponse.UserAvatar.BadFormat_UnmatchedFormat(),
- ImageException.ErrorReason.NotSquare => ErrorResponse.UserAvatar.BadFormat_BadSize(),
- _ =>
- throw new Exception(ExceptionUnknownAvatarFormatError)
- });
- }
- }
-
- /// <summary>
- /// Reset the avatar to the default one. You have to be administrator to reset other's.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <response code="200">Succeeded to reset.</response>
- /// <response code="400">Error code is 10010001 if user does not exist.</response>
- /// <response code="401">You have not logged in.</response>
- /// <response code="403">You are not administrator.</response>
- [HttpDelete("users/{username}/avatar")]
- [ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [Authorize]
- public async Task<IActionResult> Delete([FromRoute][Username] string username)
- {
- if (!User.IsAdministrator() && User.Identity.Name != username)
- {
- _logger.LogInformation(Log.Format(LogDeleteForbid,
- ("Operator Username", User.Identity.Name), ("Username To Delete Avatar", username)));
- return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
- }
-
- long id;
- try
- {
- id = await _userService.GetUserIdByUsername(username);
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username)));
- return BadRequest(ErrorResponse.UserCommon.NotExist());
- }
-
- await _service.SetAvatar(id, null);
- return Ok();
- }
- }
-}
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs deleted file mode 100644 index 02c09aab..00000000 --- a/Timeline/Controllers/UserController.cs +++ /dev/null @@ -1,195 +0,0 @@ -using AutoMapper;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Auth;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Http;
-using Timeline.Models.Validation;
-using Timeline.Services;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Controllers.UserController;
-using static Timeline.Resources.Messages;
-
-namespace Timeline.Controllers
-{
- /// <summary>
- /// Operations about users.
- /// </summary>
- [ApiController]
- [ProducesErrorResponseType(typeof(CommonResponse))]
- public class UserController : Controller
- {
- private readonly ILogger<UserController> _logger;
- private readonly IUserService _userService;
- private readonly IUserDeleteService _userDeleteService;
- private readonly IMapper _mapper;
-
- /// <summary></summary>
- public UserController(ILogger<UserController> logger, IUserService userService, IUserDeleteService userDeleteService, IMapper mapper)
- {
- _logger = logger;
- _userService = userService;
- _userDeleteService = userDeleteService;
- _mapper = mapper;
- }
-
- private UserInfo ConvertToUserInfo(User user) => _mapper.Map<UserInfo>(user);
-
- /// <summary>
- /// Get all users.
- /// </summary>
- /// <returns>All user list.</returns>
- [HttpGet("users")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<UserInfo[]>> List()
- {
- var users = await _userService.GetUsers();
- var result = users.Select(u => ConvertToUserInfo(u)).ToArray();
- return Ok(result);
- }
-
- /// <summary>
- /// Get a user's info.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <returns>User info.</returns>
- [HttpGet("users/{username}")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<UserInfo>> Get([FromRoute][Username] string username)
- {
- try
- {
- var user = await _userService.GetUserByUsername(username);
- return Ok(ConvertToUserInfo(user));
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username)));
- return NotFound(ErrorResponse.UserCommon.NotExist());
- }
- }
-
- /// <summary>
- /// Change a user's property.
- /// </summary>
- /// <param name="body"></param>
- /// <param name="username">Username of the user to change.</param>
- /// <returns>The new user info.</returns>
- [HttpPatch("users/{username}"), Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<UserInfo>> Patch([FromBody] UserPatchRequest body, [FromRoute][Username] string username)
- {
- if (this.IsAdministrator())
- {
- try
- {
- var user = await _userService.ModifyUser(username, _mapper.Map<User>(body));
- return Ok(ConvertToUserInfo(user));
- }
- catch (UserNotExistException e)
- {
- _logger.LogInformation(e, Log.Format(LogPatchUserNotExist, ("Username", username)));
- return NotFound(ErrorResponse.UserCommon.NotExist());
- }
- catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User)
- {
- return BadRequest(ErrorResponse.UserController.UsernameConflict());
- }
- }
- else
- {
- if (User.Identity.Name != username)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(Common_Forbid_NotSelf));
-
- if (body.Username != null)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Username));
-
- if (body.Password != null)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Password));
-
- if (body.Administrator != null)
- return StatusCode(StatusCodes.Status403Forbidden,
- ErrorResponse.Common.CustomMessage_Forbid(UserController_Patch_Forbid_Administrator));
-
- var user = await _userService.ModifyUser(this.GetUserId(), _mapper.Map<User>(body));
- return Ok(ConvertToUserInfo(user));
- }
- }
-
- /// <summary>
- /// Delete a user and all his related data. You have to be administrator.
- /// </summary>
- /// <param name="username">Username of the user to delete.</param>
- /// <returns>Info of deletion.</returns>
- [HttpDelete("users/{username}"), AdminAuthorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<CommonDeleteResponse>> Delete([FromRoute][Username] string username)
- {
- var delete = await _userDeleteService.DeleteUser(username);
- if (delete)
- return Ok(CommonDeleteResponse.Delete());
- else
- return Ok(CommonDeleteResponse.NotExist());
- }
-
- /// <summary>
- /// Create a new user. You have to be administrator.
- /// </summary>
- /// <returns>The new user's info.</returns>
- [HttpPost("userop/createuser"), AdminAuthorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status403Forbidden)]
- public async Task<ActionResult<UserInfo>> CreateUser([FromBody] CreateUserRequest body)
- {
- try
- {
- var user = await _userService.CreateUser(_mapper.Map<User>(body));
- return Ok(ConvertToUserInfo(user));
- }
- catch (EntityAlreadyExistException e) when (e.EntityName == EntityNames.User)
- {
- return BadRequest(ErrorResponse.UserController.UsernameConflict());
- }
- }
-
- /// <summary>
- /// Change password with old password.
- /// </summary>
- [HttpPost("userop/changepassword"), Authorize]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task<ActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
- {
- try
- {
- await _userService.ChangePassword(this.GetUserId(), request.OldPassword, request.NewPassword);
- return Ok();
- }
- catch (BadPasswordException e)
- {
- _logger.LogInformation(e, Log.Format(LogChangePasswordBadPassword,
- ("Username", User.Identity.Name), ("Old Password", request.OldPassword)));
- return BadRequest(ErrorResponse.UserController.ChangePassword_BadOldPassword());
- }
- // User can't be non-existent or the token is bad.
- }
- }
-}
diff --git a/Timeline/Entities/DataEntity.cs b/Timeline/Entities/DataEntity.cs deleted file mode 100644 index b21e2dbf..00000000 --- a/Timeline/Entities/DataEntity.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("data")]
- public class DataEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("tag"), Required]
- public string Tag { get; set; } = default!;
-
- [Column("data"), Required]
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; set; } = default!;
-#pragma warning restore CA1819 // Properties should not return arrays
-
- [Column("ref"), Required]
- public int Ref { get; set; }
- }
-}
diff --git a/Timeline/Entities/DatabaseContext.cs b/Timeline/Entities/DatabaseContext.cs deleted file mode 100644 index ecadd703..00000000 --- a/Timeline/Entities/DatabaseContext.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.EntityFrameworkCore;
-
-namespace Timeline.Entities
-{
- public class DatabaseContext : DbContext
- {
- public DatabaseContext(DbContextOptions<DatabaseContext> options)
- : base(options)
- {
- }
-
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity<UserEntity>().Property(e => e.Version).HasDefaultValue(0);
- modelBuilder.Entity<UserEntity>().HasIndex(e => e.Username).IsUnique();
- modelBuilder.Entity<UserEntity>().Property(e => e.UniqueId).HasDefaultValueSql("lower(hex(randomblob(16)))");
- modelBuilder.Entity<UserEntity>().Property(e => e.UsernameChangeTime).HasDefaultValueSql("datetime('now', 'utc')");
- modelBuilder.Entity<UserEntity>().Property(e => e.CreateTime).HasDefaultValueSql("datetime('now', 'utc')");
- modelBuilder.Entity<UserEntity>().Property(e => e.LastModified).HasDefaultValueSql("datetime('now', 'utc')");
- modelBuilder.Entity<DataEntity>().HasIndex(e => e.Tag).IsUnique();
- modelBuilder.Entity<TimelineEntity>().Property(e => e.UniqueId).HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- modelBuilder.ApplyUtcDateTimeConverter();
- }
-
- public DbSet<UserEntity> Users { get; set; } = default!;
- public DbSet<UserAvatarEntity> UserAvatars { get; set; } = default!;
- public DbSet<TimelineEntity> Timelines { get; set; } = default!;
- public DbSet<TimelinePostEntity> TimelinePosts { get; set; } = default!;
- public DbSet<TimelineMemberEntity> TimelineMembers { get; set; } = default!;
- public DbSet<JwtTokenEntity> JwtToken { get; set; } = default!;
- public DbSet<DataEntity> Data { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/JwtTokenEntity.cs b/Timeline/Entities/JwtTokenEntity.cs deleted file mode 100644 index 40cb230a..00000000 --- a/Timeline/Entities/JwtTokenEntity.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("jwt_token")]
- public class JwtTokenEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Required, Column("key")]
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Key { get; set; } = default!;
-#pragma warning restore CA1819 // Properties should not return arrays
- }
-}
diff --git a/Timeline/Entities/TimelineEntity.cs b/Timeline/Entities/TimelineEntity.cs deleted file mode 100644 index 3e592673..00000000 --- a/Timeline/Entities/TimelineEntity.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-using Timeline.Models;
-
-namespace Timeline.Entities
-{
-#pragma warning disable CA2227 // Collection properties should be read only
- // TODO: Create index for this table.
- [Table("timelines")]
- public class TimelineEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("unique_id"), Required]
- public string UniqueId { get; set; } = default!;
-
- /// <summary>
- /// If null, then this timeline is a personal timeline.
- /// </summary>
- [Column("name")]
- public string? Name { get; set; }
-
- [Column("title")]
- public string? Title { get; set; }
-
- [Column("name_last_modified")]
- public DateTime NameLastModified { get; set; }
-
- [Column("description")]
- public string? Description { get; set; }
-
- [Column("owner")]
- public long OwnerId { get; set; }
-
- [ForeignKey(nameof(OwnerId))]
- public UserEntity Owner { get; set; } = default!;
-
- [Column("visibility")]
- public TimelineVisibility Visibility { get; set; }
-
- [Column("create_time")]
- public DateTime CreateTime { get; set; }
-
- [Column("last_modified")]
- public DateTime LastModified { get; set; }
-
- [Column("current_post_local_id")]
- public long CurrentPostLocalId { get; set; }
-
- public List<TimelineMemberEntity> Members { get; set; } = default!;
-
- public List<TimelinePostEntity> Posts { get; set; } = default!;
- }
-#pragma warning restore CA2227 // Collection properties should be read only
-}
diff --git a/Timeline/Entities/TimelineMemberEntity.cs b/Timeline/Entities/TimelineMemberEntity.cs deleted file mode 100644 index e76f2099..00000000 --- a/Timeline/Entities/TimelineMemberEntity.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("timeline_members")]
- public class TimelineMemberEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("user")]
- public long UserId { get; set; }
-
- [ForeignKey(nameof(UserId))]
- public UserEntity User { get; set; } = default!;
-
- [Column("timeline")]
- public long TimelineId { get; set; }
-
- [ForeignKey(nameof(TimelineId))]
- public TimelineEntity Timeline { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/TimelinePostEntity.cs b/Timeline/Entities/TimelinePostEntity.cs deleted file mode 100644 index 07367fba..00000000 --- a/Timeline/Entities/TimelinePostEntity.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [Table("timeline_posts")]
- public class TimelinePostEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("local_id")]
- public long LocalId { get; set; }
-
- [Column("timeline")]
- public long TimelineId { get; set; }
-
- [ForeignKey(nameof(TimelineId))]
- public TimelineEntity Timeline { get; set; } = default!;
-
- [Column("author")]
- public long? AuthorId { get; set; }
-
- [ForeignKey(nameof(AuthorId))]
- public UserEntity? Author { get; set; } = default!;
-
- [Column("content_type"), Required]
- public string ContentType { get; set; } = default!;
-
- [Column("content")]
- public string? Content { get; set; }
-
- [Column("extra_content")]
- public string? ExtraContent { get; set; }
-
- [Column("time")]
- public DateTime Time { get; set; }
-
- [Column("last_updated")]
- public DateTime LastUpdated { get; set; }
- }
-}
diff --git a/Timeline/Entities/UserAvatarEntity.cs b/Timeline/Entities/UserAvatarEntity.cs deleted file mode 100644 index 3c2720f7..00000000 --- a/Timeline/Entities/UserAvatarEntity.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "This is data base entity.")]
- [Table("user_avatars")]
- public class UserAvatarEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("data_tag")]
- public string? DataTag { get; set; }
-
- [Column("type")]
- public string? Type { get; set; }
-
- [Column("last_modified"), Required]
- public DateTime LastModified { get; set; }
-
- [Column("user"), Required]
- public long UserId { get; set; }
-
- [ForeignKey(nameof(UserId))]
- public UserEntity User { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/UserEntity.cs b/Timeline/Entities/UserEntity.cs deleted file mode 100644 index 0cfaa335..00000000 --- a/Timeline/Entities/UserEntity.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace Timeline.Entities
-{
- public static class UserRoles
- {
- public const string Admin = "admin";
- public const string User = "user";
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is an entity class.")]
- [Table("users")]
- public class UserEntity
- {
- [Column("id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- [Column("unique_id"), Required]
- public string UniqueId { get; set; } = default!;
-
- [Column("username"), Required]
- public string Username { get; set; } = default!;
-
- [Column("username_change_time")]
- public DateTime UsernameChangeTime { get; set; }
-
- [Column("password"), Required]
- public string Password { get; set; } = default!;
-
- [Column("roles"), Required]
- public string Roles { get; set; } = default!;
-
- [Column("version"), Required]
- public long Version { get; set; }
-
- [Column("nickname")]
- public string? Nickname { get; set; }
-
- [Column("create_time")]
- public DateTime CreateTime { get; set; }
-
- [Column("last_modified")]
- public DateTime LastModified { get; set; }
-
- public UserAvatarEntity? Avatar { get; set; }
-
- public List<TimelineEntity> Timelines { get; set; } = default!;
-
- public List<TimelinePostEntity> TimelinePosts { get; set; } = default!;
-
- public List<TimelineMemberEntity> TimelinesJoined { get; set; } = default!;
- }
-}
diff --git a/Timeline/Entities/UtcDateAnnotation.cs b/Timeline/Entities/UtcDateAnnotation.cs deleted file mode 100644 index 6600e701..00000000 --- a/Timeline/Entities/UtcDateAnnotation.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using System;
-
-namespace Timeline.Entities
-{
- // Copied from https://github.com/dotnet/efcore/issues/4711#issuecomment-589842988
- public static class UtcDateAnnotation
- {
- private const string IsUtcAnnotation = "IsUtc";
- private static readonly ValueConverter<DateTime, DateTime> UtcConverter =
- new ValueConverter<DateTime, DateTime>(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
-
- public static PropertyBuilder<TProperty> IsUtc<TProperty>(this PropertyBuilder<TProperty> builder, bool isUtc = true) =>
- builder.HasAnnotation(IsUtcAnnotation, isUtc);
-
- public static bool IsUtc(this IMutableProperty property) =>
- ((bool?)property.FindAnnotation(IsUtcAnnotation)?.Value) ?? true;
-
- /// <summary>
- /// Make sure this is called after configuring all your entities.
- /// </summary>
- public static void ApplyUtcDateTimeConverter(this ModelBuilder builder)
- {
- foreach (var entityType in builder.Model.GetEntityTypes())
- {
- foreach (var property in entityType.GetProperties())
- {
- if (!property.IsUtc())
- {
- continue;
- }
-
- if (property.ClrType == typeof(DateTime))
- {
- property.SetValueConverter(UtcConverter);
- }
- }
- }
- }
- }
-}
diff --git a/Timeline/Filters/Header.cs b/Timeline/Filters/Header.cs deleted file mode 100644 index cc5ddd9f..00000000 --- a/Timeline/Filters/Header.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Timeline.Models.Http;
-
-namespace Timeline.Filters
-{
- /// <summary>
- /// Restrict max content length.
- /// </summary>
- public class MaxContentLengthFilter : IResourceFilter
- {
- /// <summary>
- ///
- /// </summary>
- /// <param name="maxByteLength">Max length.</param>
- public MaxContentLengthFilter(long maxByteLength)
- {
- MaxByteLength = maxByteLength;
- }
-
- /// <summary>
- /// Max length.
- /// </summary>
- public long MaxByteLength { get; set; }
-
- /// <inheritdoc/>
- public void OnResourceExecuted(ResourceExecutedContext context)
- {
- }
-
- /// <inheritdoc/>
- public void OnResourceExecuting(ResourceExecutingContext context)
- {
- var contentLength = context.HttpContext.Request.ContentLength;
- if (contentLength != null && contentLength > MaxByteLength)
- {
- context.Result = new BadRequestObjectResult(ErrorResponse.Common.Content.TooBig(MaxByteLength + "B"));
- }
- }
- }
-
- /// <summary>
- /// Restrict max content length.
- /// </summary>
- public class MaxContentLengthAttribute : TypeFilterAttribute
- {
- /// <summary>
- ///
- /// </summary>
- /// <param name="maxByteLength">Max length.</param>
- public MaxContentLengthAttribute(long maxByteLength)
- : base(typeof(MaxContentLengthFilter))
- {
- MaxByteLength = maxByteLength;
- Arguments = new object[] { maxByteLength };
- }
-
- /// <summary>
- /// Max length.
- /// </summary>
- public long MaxByteLength { get; }
- }
-}
diff --git a/Timeline/Filters/Timeline.cs b/Timeline/Filters/Timeline.cs deleted file mode 100644 index 6a730ee7..00000000 --- a/Timeline/Filters/Timeline.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Timeline.Models.Http;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Filters
-{
- public class CatchTimelineNotExistExceptionAttribute : ExceptionFilterAttribute
- {
- public override void OnException(ExceptionContext context)
- {
- if (context.Exception is TimelineNotExistException e)
- {
- if (e.InnerException is UserNotExistException)
- {
- if (HttpMethods.IsGet(context.HttpContext.Request.Method))
- context.Result = new NotFoundObjectResult(ErrorResponse.UserCommon.NotExist());
- else
- context.Result = new BadRequestObjectResult(ErrorResponse.UserCommon.NotExist());
- }
- else
- {
- if (HttpMethods.IsGet(context.HttpContext.Request.Method))
- context.Result = new NotFoundObjectResult(ErrorResponse.TimelineController.NotExist());
- else
- context.Result = new BadRequestObjectResult(ErrorResponse.TimelineController.NotExist());
- }
- }
- }
- }
-}
diff --git a/Timeline/Formatters/BytesInputFormatter.cs b/Timeline/Formatters/BytesInputFormatter.cs deleted file mode 100644 index ac6537c9..00000000 --- a/Timeline/Formatters/BytesInputFormatter.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Formatters;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Threading.Tasks;
-using Timeline.Models;
-
-namespace Timeline.Formatters
-{
- /// <summary>
- /// Formatter that reads body as bytes.
- /// </summary>
- public class BytesInputFormatter : InputFormatter
- {
- /// <summary>
- ///
- /// </summary>
- public BytesInputFormatter()
- {
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/gif"));
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/webp"));
- }
-
- /// <inheritdoc/>
- public override bool CanRead(InputFormatterContext context)
- {
- if (context == null) throw new ArgumentNullException(nameof(context));
-
- if (context.ModelType == typeof(ByteData))
- return true;
-
- return false;
- }
-
- /// <inheritdoc/>
- public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
- {
- var request = context.HttpContext.Request;
- var contentLength = request.ContentLength;
-
- var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<BytesInputFormatter>>();
-
- if (contentLength == null)
- {
- logger.LogInformation("Failed to read body as bytes. Content-Length is not set.");
- return await InputFormatterResult.FailureAsync();
- }
-
- if (contentLength == 0)
- {
- logger.LogInformation("Failed to read body as bytes. Content-Length is 0.");
- return await InputFormatterResult.FailureAsync();
- }
-
- var bodyStream = request.Body;
-
- var data = new byte[contentLength.Value];
- var bytesRead = await bodyStream.ReadAsync(data);
-
- if (bytesRead != contentLength)
- {
- logger.LogInformation("Failed to read body as bytes. Actual length of body is smaller than Content-Length.");
- return await InputFormatterResult.FailureAsync();
- }
-
- var extraByte = new byte[1];
- if (await bodyStream.ReadAsync(extraByte) != 0)
- {
- logger.LogInformation("Failed to read body as bytes. Actual length of body is greater than Content-Length.");
- return await InputFormatterResult.FailureAsync();
- }
-
- return await InputFormatterResult.SuccessAsync(new ByteData(data, request.ContentType));
- }
- }
-}
diff --git a/Timeline/Formatters/StringInputFormatter.cs b/Timeline/Formatters/StringInputFormatter.cs deleted file mode 100644 index b1924268..00000000 --- a/Timeline/Formatters/StringInputFormatter.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Formatters;
-using Microsoft.Net.Http.Headers;
-using System.IO;
-using System.Net.Mime;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Timeline.Formatters
-{
- public class StringInputFormatter : TextInputFormatter
- {
- public StringInputFormatter()
- {
- SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(MediaTypeNames.Text.Plain));
- SupportedEncodings.Add(Encoding.UTF8);
- }
-
- public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
- {
- var request = context.HttpContext.Request;
- using var reader = new StreamReader(request.Body, effectiveEncoding);
- var stringContent = await reader.ReadToEndAsync();
- return await InputFormatterResult.SuccessAsync(stringContent);
- }
- }
-}
diff --git a/Timeline/GlobalSuppressions.cs b/Timeline/GlobalSuppressions.cs deleted file mode 100644 index 2b0da576..00000000 --- a/Timeline/GlobalSuppressions.cs +++ /dev/null @@ -1,14 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage
-// attributes that are applied to this project.
-// Project-level suppressions either have no target or are given
-// a specific target and scoped to a namespace, type, member, etc.
-
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "This is not a UI application.")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "This is not bad.")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "No need to check the null because it's ASP.Net's duty.", Scope = "namespaceanddescendants", Target = "Timeline.Controllers")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Migrations code are auto generated.", Scope = "namespaceanddescendants", Target = "Timeline.Migrations")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Generated error response identifiers.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "Generated error response identifiers.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Generated error response.", Scope = "type", Target = "Timeline.Models.Http.ErrorResponse")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1056:Uri properties should not be strings", Justification = "That's unnecessary.")]
-[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Adundant")]
diff --git a/Timeline/Helpers/DataCacheHelper.cs b/Timeline/Helpers/DataCacheHelper.cs deleted file mode 100644 index 1ad69708..00000000 --- a/Timeline/Helpers/DataCacheHelper.cs +++ /dev/null @@ -1,125 +0,0 @@ -using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Net.Http.Headers;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Models.Http;
-using static Timeline.Resources.Helper.DataCacheHelper;
-
-namespace Timeline.Helpers
-{
- public interface ICacheableData
- {
- string Type { get; }
-#pragma warning disable CA1819 // Properties should not return arrays
- byte[] Data { get; }
-#pragma warning restore CA1819 // Properties should not return arrays
- DateTime? LastModified { get; }
- }
-
- public class CacheableData : ICacheableData
- {
- public CacheableData(string type, byte[] data, DateTime? lastModified)
- {
- Type = type;
- Data = data;
- LastModified = lastModified;
- }
-
- public string Type { get; set; }
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; set; }
-#pragma warning restore CA1819 // Properties should not return arrays
- public DateTime? LastModified { get; set; }
- }
-
- public interface ICacheableDataProvider
- {
- Task<string> GetDataETag();
- Task<ICacheableData> GetData();
- }
-
- public class DelegateCacheableDataProvider : ICacheableDataProvider
- {
- private readonly Func<Task<string>> _getDataETagDelegate;
- private readonly Func<Task<ICacheableData>> _getDataDelegate;
-
- public DelegateCacheableDataProvider(Func<Task<string>> getDataETagDelegate, Func<Task<ICacheableData>> getDataDelegate)
- {
- _getDataETagDelegate = getDataETagDelegate;
- _getDataDelegate = getDataDelegate;
- }
-
- public Task<ICacheableData> GetData()
- {
- return _getDataDelegate();
- }
-
- public Task<string> GetDataETag()
- {
- return _getDataETagDelegate();
- }
- }
-
- public static class DataCacheHelper
- {
- public static async Task<ActionResult> GenerateActionResult(Controller controller, ICacheableDataProvider provider, TimeSpan? maxAge = null)
- {
- const string CacheControlHeaderKey = "Cache-Control";
- const string IfNonMatchHeaderKey = "If-None-Match";
- const string ETagHeaderKey = "ETag";
-
- string GenerateCacheControlHeaderValue()
- {
- var cacheControlHeader = new CacheControlHeaderValue()
- {
- NoCache = true,
- NoStore = false,
- MaxAge = maxAge ?? TimeSpan.FromDays(14),
- Private = true,
- MustRevalidate = true
- };
- return cacheControlHeader.ToString();
- }
-
- var loggerFactory = controller.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
- var logger = loggerFactory.CreateLogger(typeof(DataCacheHelper));
-
- var eTagValue = await provider.GetDataETag();
- eTagValue = '"' + eTagValue + '"';
- var eTag = new EntityTagHeaderValue(eTagValue);
-
-
- if (controller.Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value))
- {
- if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList))
- {
- logger.LogInformation(Log.Format(LogBadIfNoneMatch, ("Header Value", value)));
- return controller.BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat());
- }
-
- if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
- {
- logger.LogInformation(LogResultNotModified);
- controller.Response.Headers.Add(ETagHeaderKey, eTagValue);
- controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
-
- return controller.StatusCode(StatusCodes.Status304NotModified, null);
- }
- }
-
- var data = await provider.GetData();
- logger.LogInformation(LogResultData);
- controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
- return controller.File(data.Data, data.Type, data.LastModified, eTag);
- }
-
- public static Task<ActionResult> GenerateActionResult(Controller controller, Func<Task<string>> getDataETagDelegate, Func<Task<ICacheableData>> getDataDelegate, TimeSpan? maxAge = null)
- {
- return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDataETagDelegate, getDataDelegate), maxAge);
- }
- }
-}
diff --git a/Timeline/Helpers/DateTimeExtensions.cs b/Timeline/Helpers/DateTimeExtensions.cs deleted file mode 100644 index 374f3bc9..00000000 --- a/Timeline/Helpers/DateTimeExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System;
-
-namespace Timeline.Helpers
-{
- public static class DateTimeExtensions
- {
- public static DateTime MyToUtc(this DateTime dateTime)
- {
- if (dateTime.Kind == DateTimeKind.Utc) return dateTime;
- if (dateTime.Kind == DateTimeKind.Local) return dateTime.ToUniversalTime();
- return DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
- }
- }
-}
diff --git a/Timeline/Helpers/InvalidModelResponseFactory.cs b/Timeline/Helpers/InvalidModelResponseFactory.cs deleted file mode 100644 index 9b253e7d..00000000 --- a/Timeline/Helpers/InvalidModelResponseFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.AspNetCore.Mvc;
-using System.Text;
-using Timeline.Models.Http;
-
-namespace Timeline.Helpers
-{
- public static class InvalidModelResponseFactory
- {
- public static IActionResult Factory(ActionContext context)
- {
- var modelState = context.ModelState;
-
- var messageBuilder = new StringBuilder();
- foreach (var model in modelState)
- foreach (var error in model.Value.Errors)
- {
- messageBuilder.Append(model.Key);
- messageBuilder.Append(" : ");
- messageBuilder.AppendLine(error.ErrorMessage);
- }
-
- return new BadRequestObjectResult(ErrorResponse.Common.CustomMessage_InvalidModel(messageBuilder.ToString()));
- }
- }
-}
diff --git a/Timeline/Helpers/LanguageHelper.cs b/Timeline/Helpers/LanguageHelper.cs deleted file mode 100644 index b0156b8b..00000000 --- a/Timeline/Helpers/LanguageHelper.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Linq;
-
-namespace Timeline.Helpers
-{
- public static class LanguageHelper
- {
- public static bool AreSame(this bool firstBool, params bool[] otherBools)
- {
- return otherBools.All(b => b == firstBool);
- }
- }
-}
diff --git a/Timeline/Helpers/Log.cs b/Timeline/Helpers/Log.cs deleted file mode 100644 index af0b7e13..00000000 --- a/Timeline/Helpers/Log.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text;
-
-namespace Timeline.Helpers
-{
- public static class Log
- {
- public static string Format(string summary, params (string, object?)[] properties)
- {
- var builder = new StringBuilder();
- builder.Append(summary);
- foreach (var property in properties)
- {
- var (key, value) = property;
- builder.AppendLine();
- builder.Append(key);
- builder.Append(" : ");
- builder.Append(value);
- }
- return builder.ToString();
- }
- }
-}
diff --git a/Timeline/Migrations/20200105150407_Initialize.Designer.cs b/Timeline/Migrations/20200105150407_Initialize.Designer.cs deleted file mode 100644 index 99e4eaac..00000000 --- a/Timeline/Migrations/20200105150407_Initialize.Designer.cs +++ /dev/null @@ -1,266 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200105150407_Initialize")]
- partial class Initialize
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.0");
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.User", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("EncryptedPassword")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .IsRequired()
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<string>("RoleString")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Name")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatar", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserDetail", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_details");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.User", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.User", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.User", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatar", b =>
- {
- b.HasOne("Timeline.Entities.User", null)
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatar", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserDetail", b =>
- {
- b.HasOne("Timeline.Entities.User", null)
- .WithOne("Detail")
- .HasForeignKey("Timeline.Entities.UserDetail", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200105150407_Initialize.cs b/Timeline/Migrations/20200105150407_Initialize.cs deleted file mode 100644 index 4e12ef83..00000000 --- a/Timeline/Migrations/20200105150407_Initialize.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class Initialize : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "users",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- name = table.Column<string>(nullable: false),
- password = table.Column<string>(nullable: false),
- roles = table.Column<string>(nullable: false),
- version = table.Column<long>(nullable: false, defaultValue: 0L)
- .Annotation("Sqlite:Autoincrement", true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_users", x => x.id);
- });
-
- migrationBuilder.CreateTable(
- name: "timelines",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- name = table.Column<string>(nullable: true),
- description = table.Column<string>(nullable: true),
- owner = table.Column<long>(nullable: false),
- visibility = table.Column<int>(nullable: false),
- create_time = table.Column<DateTime>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_timelines", x => x.id);
- table.ForeignKey(
- name: "FK_timelines_users_owner",
- column: x => x.owner,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "user_avatars",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- data = table.Column<byte[]>(nullable: true),
- type = table.Column<string>(nullable: true),
- etag = table.Column<string>(nullable: true),
- last_modified = table.Column<DateTime>(nullable: false),
- UserId = table.Column<long>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_user_avatars", x => x.id);
- table.ForeignKey(
- name: "FK_user_avatars_users_UserId",
- column: x => x.UserId,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "user_details",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- nickname = table.Column<string>(nullable: true),
- UserId = table.Column<long>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_user_details", x => x.id);
- table.ForeignKey(
- name: "FK_user_details_users_UserId",
- column: x => x.UserId,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "timeline_members",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- user = table.Column<long>(nullable: false),
- timeline = table.Column<long>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_timeline_members", x => x.id);
- table.ForeignKey(
- name: "FK_timeline_members_timelines_timeline",
- column: x => x.timeline,
- principalTable: "timelines",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- table.ForeignKey(
- name: "FK_timeline_members_users_user",
- column: x => x.user,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateTable(
- name: "timeline_posts",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- timeline = table.Column<long>(nullable: false),
- author = table.Column<long>(nullable: false),
- content = table.Column<string>(nullable: true),
- time = table.Column<DateTime>(nullable: false),
- last_updated = table.Column<DateTime>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_timeline_posts", x => x.id);
- table.ForeignKey(
- name: "FK_timeline_posts_users_author",
- column: x => x.author,
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- table.ForeignKey(
- name: "FK_timeline_posts_timelines_timeline",
- column: x => x.timeline,
- principalTable: "timelines",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_members_timeline",
- table: "timeline_members",
- column: "timeline");
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_members_user",
- table: "timeline_members",
- column: "user");
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_posts_author",
- table: "timeline_posts",
- column: "author");
-
- migrationBuilder.CreateIndex(
- name: "IX_timeline_posts_timeline",
- table: "timeline_posts",
- column: "timeline");
-
- migrationBuilder.CreateIndex(
- name: "IX_timelines_owner",
- table: "timelines",
- column: "owner");
-
- migrationBuilder.CreateIndex(
- name: "IX_user_avatars_UserId",
- table: "user_avatars",
- column: "UserId",
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_user_details_UserId",
- table: "user_details",
- column: "UserId",
- unique: true);
-
- migrationBuilder.CreateIndex(
- name: "IX_users_name",
- table: "users",
- column: "name",
- unique: true);
-
- // Add a init user. Username is "administrator". Password is "crupest".
- migrationBuilder.InsertData("users", new string[] { "name", "password", "roles" },
- new object[] { "administrator", "AQAAAAEAACcQAAAAENsspZrk8Wo+UuMyg6QuWJsNvRg6gVu4K/TumVod3h9GVLX9zDVuQQds3o7V8QWJ2w==", "user,admin" });
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "timeline_members");
-
- migrationBuilder.DropTable(
- name: "timeline_posts");
-
- migrationBuilder.DropTable(
- name: "user_avatars");
-
- migrationBuilder.DropTable(
- name: "user_details");
-
- migrationBuilder.DropTable(
- name: "timelines");
-
- migrationBuilder.DropTable(
- name: "users");
- }
- }
-}
diff --git a/Timeline/Migrations/20200131100517_RefactorUser.Designer.cs b/Timeline/Migrations/20200131100517_RefactorUser.Designer.cs deleted file mode 100644 index 9b78eb15..00000000 --- a/Timeline/Migrations/20200131100517_RefactorUser.Designer.cs +++ /dev/null @@ -1,240 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200131100517_RefactorUser")]
- partial class RefactorUser
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.1");
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200131100517_RefactorUser.cs b/Timeline/Migrations/20200131100517_RefactorUser.cs deleted file mode 100644 index 8597ed50..00000000 --- a/Timeline/Migrations/20200131100517_RefactorUser.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class RefactorUser : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.RenameColumn(name: "name", table: "users", newName: "username");
- migrationBuilder.RenameIndex(name: "IX_users_name", table: "users", newName: "IX_users_username");
-
- migrationBuilder.AddColumn<string>(
- name: "nickname",
- table: "users",
- nullable: true);
-
- migrationBuilder.Sql(@"
-UPDATE users
- SET nickname = (
- SELECT nickname
- FROM user_details
- WHERE user_details.UserId = users.id
- );
- ");
-
- /*
- migrationBuilder.RenameColumn(name: "UserId", table: "user_avatars", newName: "user");
-
- migrationBuilder.DropForeignKey(
- name: "FK_user_avatars_users_UserId",
- table: "user_avatars");
-
- migrationBuilder.AddForeignKey(
- name: "FK_user_avatars_users_user",
- table: "user_avatars",
- column: "user",
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
-
- migrationBuilder.RenameIndex(
- name: "IX_user_avatars_UserId",
- table: "user_avatars",
- newName: "IX_user_avatars_user");
- */
-
- migrationBuilder.Sql(@"
-CREATE TABLE user_avatars_backup (
- id INTEGER NOT NULL
- CONSTRAINT PK_user_avatars PRIMARY KEY AUTOINCREMENT,
- data BLOB,
- type TEXT,
- etag TEXT,
- last_modified TEXT NOT NULL,
- user INTEGER NOT NULL,
- CONSTRAINT FK_user_avatars_users_user FOREIGN KEY (
- user
- )
- REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO user_avatars_backup (id, data, type, etag, last_modified, user)
- SELECT id, data, type, etag, last_modified, UserId FROM user_avatars;
-
-DROP TABLE user_avatars;
-
-ALTER TABLE user_avatars_backup
- RENAME TO user_avatars;
-
-CREATE UNIQUE INDEX IX_user_avatars_user ON user_avatars (user);
- ");
-
- // migrationBuilder.DropTable(name: "user_details");
-
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(@"
-CREATE TABLE user_avatars_backup (
- id INTEGER NOT NULL
- CONSTRAINT PK_user_avatars PRIMARY KEY AUTOINCREMENT,
- data BLOB,
- type TEXT,
- etag TEXT,
- last_modified TEXT NOT NULL,
- UserId INTEGER NOT NULL,
- CONSTRAINT FK_user_avatars_users_UserId FOREIGN KEY (
- user
- )
- REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO user_avatars_backup (id, data, type, etag, last_modified, UserId)
- SELECT id, data, type, etag, last_modified, user FROM user_avatars;
-
-DROP TABLE user_avatars;
-
-ALTER TABLE user_avatars_backup
- RENAME TO user_avatars;
-
-CREATE UNIQUE INDEX IX_user_avatars_UserId ON user_avatars (UserId);
- ");
-
- migrationBuilder.Sql(@"
-CREATE TABLE users_backup (
- id INTEGER NOT NULL
- CONSTRAINT PK_users PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL,
- password TEXT NOT NULL,
- roles TEXT NOT NULL,
- version INTEGER NOT NULL
- DEFAULT 0
-);
-
-INSERT INTO users_backup (id, name, password, roles, version)
- SELECT id, username, password, roles, version FROM users;
-
-DROP TABLE users;
-
-ALTER TABLE users_backup
- RENAME TO users;
-
-CREATE UNIQUE INDEX IX_users_name ON users (name);
- ");
- }
- }
-}
diff --git a/Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs b/Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs deleted file mode 100644 index eb328b52..00000000 --- a/Timeline/Migrations/20200221064341_AddJwtToken.Designer.cs +++ /dev/null @@ -1,257 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200221064341_AddJwtToken")]
- partial class AddJwtToken
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200221064341_AddJwtToken.cs b/Timeline/Migrations/20200221064341_AddJwtToken.cs deleted file mode 100644 index 628970c6..00000000 --- a/Timeline/Migrations/20200221064341_AddJwtToken.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System;
-using System.Security.Cryptography;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public static class JwtTokenGenerateHelper
- {
- public static byte[] GenerateKey()
- {
- using var random = RandomNumberGenerator.Create();
- var key = new byte[16];
- random.GetBytes(key);
- return key;
- }
- }
-
- public partial class AddJwtToken : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "jwt_token",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- key = table.Column<byte[]>(nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_jwt_token", x => x.id);
- });
-
-
- migrationBuilder.InsertData("jwt_token", "key", JwtTokenGenerateHelper.GenerateKey());
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "jwt_token");
- }
- }
-}
diff --git a/Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs b/Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs deleted file mode 100644 index cf6ae8a3..00000000 --- a/Timeline/Migrations/20200229103848_AddPostLocalId.Designer.cs +++ /dev/null @@ -1,265 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200229103848_AddPostLocalId")]
- partial class AddPostLocalId
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<string>("ETag")
- .HasColumnName("etag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200229103848_AddPostLocalId.cs b/Timeline/Migrations/20200229103848_AddPostLocalId.cs deleted file mode 100644 index 497b38a1..00000000 --- a/Timeline/Migrations/20200229103848_AddPostLocalId.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddPostLocalId : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
-
- migrationBuilder.AddColumn<long>(
- name: "current_post_local_id",
- table: "timelines",
- nullable: false,
- defaultValue: 0L);
-
- migrationBuilder.AddColumn<long>(
- name: "local_id",
- table: "timeline_posts",
- nullable: false,
- defaultValue: 0L);
-
- migrationBuilder.Sql(@"
-UPDATE timeline_posts
-SET local_id = (SELECT COUNT (*)
- FROM timeline_posts AS p
- WHERE p.timeline = timeline_posts.timeline
- AND p.id <= timeline_posts.id);
-
-UPDATE timelines
-SET current_post_local_id = (SELECT COUNT (*)
- FROM timeline_posts AS p
- WHERE p.timeline = timelines.id);
- ");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200306110049_AddDataTable.Designer.cs b/Timeline/Migrations/20200306110049_AddDataTable.Designer.cs deleted file mode 100644 index 336ffc18..00000000 --- a/Timeline/Migrations/20200306110049_AddDataTable.Designer.cs +++ /dev/null @@ -1,290 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200306110049_AddDataTable")]
- partial class AddDataTable
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200306110049_AddDataTable.cs b/Timeline/Migrations/20200306110049_AddDataTable.cs deleted file mode 100644 index e33bf4c9..00000000 --- a/Timeline/Migrations/20200306110049_AddDataTable.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddDataTable : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "data",
- columns: table => new
- {
- id = table.Column<long>(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- tag = table.Column<string>(nullable: false),
- data = table.Column<byte[]>(nullable: false),
- @ref = table.Column<int>(name: "ref", nullable: false)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_data", x => x.id);
- });
-
- migrationBuilder.CreateIndex(
- name: "IX_data_tag",
- table: "data",
- column: "tag",
- unique: true);
-
- migrationBuilder.Sql(@"
-ALTER TABLE user_avatars
- RENAME TO user_avatars_backup;
-
-CREATE TABLE user_avatars (
- id INTEGER NOT NULL
- CONSTRAINT PK_user_avatars PRIMARY KEY AUTOINCREMENT,
- data_tag TEXT,
- type TEXT,
- last_modified TEXT NOT NULL,
- user INTEGER NOT NULL,
- CONSTRAINT FK_user_avatars_users_user FOREIGN KEY (
- user
- )
- REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO user_avatars (id, data_tag, type, last_modified, user)
- SELECT id, etag, type, last_modified, user FROM user_avatars_backup;
-
-INSERT OR IGNORE INTO data (tag, data, ref)
- SELECT etag, data, 0 FROM user_avatars_backup;
-
-UPDATE data
-SET ref = (SELECT COUNT (*)
- FROM user_avatars_backup AS a
- WHERE a.etag == data.tag);
-
-DROP TABLE user_avatars_backup;
-
-CREATE UNIQUE INDEX IX_user_avatars_user ON user_avatars (user);
- ");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "data");
-
- migrationBuilder.DropColumn(
- name: "data_tag",
- table: "user_avatars");
-
- migrationBuilder.AddColumn<byte[]>(
- name: "data",
- table: "user_avatars",
- type: "BLOB",
- nullable: true);
-
- migrationBuilder.AddColumn<string>(
- name: "etag",
- table: "user_avatars",
- type: "TEXT",
- nullable: true);
- }
- }
-}
diff --git a/Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs b/Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs deleted file mode 100644 index f0c4dc08..00000000 --- a/Timeline/Migrations/20200306111553_DropUserDetails.Designer.cs +++ /dev/null @@ -1,290 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200306111553_DropUserDetails")]
- partial class DropUserDetails
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200306111553_DropUserDetails.cs b/Timeline/Migrations/20200306111553_DropUserDetails.cs deleted file mode 100644 index 0a176461..00000000 --- a/Timeline/Migrations/20200306111553_DropUserDetails.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class DropUserDetails : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(name: "user_details");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200312112552_AddImagePost.Designer.cs b/Timeline/Migrations/20200312112552_AddImagePost.Designer.cs deleted file mode 100644 index bd75a916..00000000 --- a/Timeline/Migrations/20200312112552_AddImagePost.Designer.cs +++ /dev/null @@ -1,299 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200312112552_AddImagePost")]
- partial class AddImagePost
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.2");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200312112552_AddImagePost.cs b/Timeline/Migrations/20200312112552_AddImagePost.cs deleted file mode 100644 index d5098ce0..00000000 --- a/Timeline/Migrations/20200312112552_AddImagePost.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-using Timeline.Models;
-
-namespace Timeline.Migrations
-{
- public partial class AddImagePost : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn<string>(
- name: "content_type",
- table: "timeline_posts",
- nullable: false,
- defaultValue: "");
-
- migrationBuilder.AddColumn<string>(
- name: "extra_content",
- table: "timeline_posts",
- nullable: true);
-
- migrationBuilder.Sql($@"
-UPDATE timeline_posts
-SET content_type = '{TimelinePostContentTypes.Text}';
- ");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "content_type",
- table: "timeline_posts");
-
- migrationBuilder.DropColumn(
- name: "extra_content",
- table: "timeline_posts");
- }
- }
-}
diff --git a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs b/Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs deleted file mode 100644 index adcc6308..00000000 --- a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.Designer.cs +++ /dev/null @@ -1,306 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200614061237_AddTimelineUniqueId")]
- partial class AddTimelineUniqueId
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.4");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs b/Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs deleted file mode 100644 index 7abbed79..00000000 --- a/Timeline/Migrations/20200614061237_AddTimelineUniqueId.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddTimelineUniqueId : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(
-@"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_timelines (
- id INTEGER NOT NULL CONSTRAINT PK_timelines PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- name TEXT NULL,
- description TEXT NULL,
- owner INTEGER NOT NULL,
- visibility INTEGER NOT NULL,
- create_time TEXT NOT NULL,
- current_post_local_id INTEGER NOT NULL DEFAULT 0,
- CONSTRAINT FK_timelines_users_owner FOREIGN KEY (owner) REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO new_timelines (id, name, description, owner, visibility, create_time, current_post_local_id)
- SELECT id, name, description, owner, visibility, create_time, current_post_local_id FROM timelines;
-
-DROP TABLE timelines;
-
-ALTER TABLE new_timelines
- RENAME TO timelines;
-
-CREATE INDEX IX_timelines_owner ON timelines (owner);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
- , true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- }
- }
-}
diff --git a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs b/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs deleted file mode 100644 index fd10dfa9..00000000 --- a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.Designer.cs +++ /dev/null @@ -1,314 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200618064936_TimelineAddModifiedTime")]
- partial class TimelineAddModifiedTime
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs b/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs deleted file mode 100644 index c277fe39..00000000 --- a/Timeline/Migrations/20200618064936_TimelineAddModifiedTime.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-namespace Timeline.Migrations
-{
- public partial class TimelineAddModifiedTime : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- var currentTime = new DateTimeToStringConverter().ConvertToProvider(DateTime.Now);
-
- migrationBuilder.Sql(
-@$"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_timelines (
- id INTEGER NOT NULL CONSTRAINT PK_timelines PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- name TEXT NULL,
- name_last_modified TEXT NOT NULL,
- description TEXT NULL,
- owner INTEGER NOT NULL,
- visibility INTEGER NOT NULL,
- create_time TEXT NOT NULL,
- last_modified TEXT NOT NULL,
- current_post_local_id INTEGER NOT NULL DEFAULT 0,
- CONSTRAINT FK_timelines_users_owner FOREIGN KEY (owner) REFERENCES users (id) ON DELETE CASCADE
-);
-
-INSERT INTO new_timelines (id, unique_id, name, name_last_modified, description, owner, visibility, create_time, last_modified, current_post_local_id)
- SELECT id, unique_id, name, '{currentTime}', description, owner, visibility, create_time, '{currentTime}', current_post_local_id FROM timelines;
-
-DROP TABLE timelines;
-
-ALTER TABLE new_timelines
- RENAME TO timelines;
-
-CREATE INDEX IX_timelines_owner ON timelines (owner);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
- , true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs b/Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs deleted file mode 100644 index fe2329e4..00000000 --- a/Timeline/Migrations/20200808071611_UserAddUniqueId.Designer.cs +++ /dev/null @@ -1,321 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200808071611_UserAddUniqueId")]
- partial class UserAddUniqueId
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200808071611_UserAddUniqueId.cs b/Timeline/Migrations/20200808071611_UserAddUniqueId.cs deleted file mode 100644 index 651a2b05..00000000 --- a/Timeline/Migrations/20200808071611_UserAddUniqueId.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class UserAddUniqueId : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(
-@"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_users (
- id INTEGER NOT NULL
- CONSTRAINT PK_users PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- username TEXT NOT NULL,
- password TEXT NOT NULL,
- roles TEXT NOT NULL,
- version INTEGER NOT NULL
- DEFAULT 0,
- nickname TEXT
-);
-
-INSERT INTO new_users (id, username, password, roles, version, nickname)
- SELECT id, username, password, roles, version, nickname FROM users;
-
-DROP TABLE users;
-
-ALTER TABLE new_users
- RENAME TO users;
-
-CREATE UNIQUE INDEX IX_users_username ON users (
- username
-);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
- , true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "unique_id",
- table: "users");
- }
- }
-}
diff --git a/Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs b/Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs deleted file mode 100644 index 71cc54dc..00000000 --- a/Timeline/Migrations/20200810155908_AddTimesToUser.Designer.cs +++ /dev/null @@ -1,339 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200810155908_AddTimesToUser")]
- partial class AddTimesToUser
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTimeOffset>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTimeOffset>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTimeOffset>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200810155908_AddTimesToUser.cs b/Timeline/Migrations/20200810155908_AddTimesToUser.cs deleted file mode 100644 index 369f85e6..00000000 --- a/Timeline/Migrations/20200810155908_AddTimesToUser.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class AddTimesToUser : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(
-@"
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_users (
- id INTEGER NOT NULL
- CONSTRAINT PK_users PRIMARY KEY AUTOINCREMENT,
- unique_id TEXT NOT NULL DEFAULT (lower(hex(randomblob(16)))),
- username TEXT NOT NULL,
- password TEXT NOT NULL,
- roles TEXT NOT NULL,
- version INTEGER NOT NULL
- DEFAULT 0,
- nickname TEXT,
- create_time TEXT NOT NULL DEFAULT (datetime('now', 'utc')),
- last_modified TEXT NOT NULL DEFAULT (datetime('now', 'utc')),
- username_change_time TEXT NOT NULL DEFAULT (datetime('now', 'utc'))
-);
-
-INSERT INTO new_users (id, unique_id, username, password, roles, version, nickname)
- SELECT id, unique_id, username, password, roles, version, nickname FROM users;
-
-DROP TABLE users;
-
-ALTER TABLE new_users
- RENAME TO users;
-
-CREATE UNIQUE INDEX IX_users_username ON users (
- username
-);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys=ON;
-"
-, true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "create_time",
- table: "users");
-
- migrationBuilder.DropColumn(
- name: "last_modified",
- table: "users");
-
- migrationBuilder.DropColumn(
- name: "username_change_time",
- table: "users");
- }
- }
-}
diff --git a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs b/Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs deleted file mode 100644 index 80598fdf..00000000 --- a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.Designer.cs +++ /dev/null @@ -1,337 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200810170533_MakePostAuthorOptional")]
- partial class MakePostAuthorOptional
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTimeOffset>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTimeOffset>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTimeOffset>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs b/Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs deleted file mode 100644 index b0f0bca7..00000000 --- a/Timeline/Migrations/20200810170533_MakePostAuthorOptional.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class MakePostAuthorOptional : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.Sql(@"
-PRAGMA foreign_keys = 0;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE new_timeline_posts (
- id INTEGER NOT NULL
- CONSTRAINT PK_timeline_posts PRIMARY KEY AUTOINCREMENT,
- timeline INTEGER NOT NULL,
- author INTEGER,
- content TEXT,
- time TEXT NOT NULL,
- last_updated TEXT NOT NULL,
- local_id INTEGER NOT NULL
- DEFAULT 0,
- content_type TEXT NOT NULL
- DEFAULT '',
- extra_content TEXT,
- CONSTRAINT FK_timeline_posts_users_author FOREIGN KEY (
- author
- )
- REFERENCES users (id),
- CONSTRAINT FK_timeline_posts_timelines_timeline FOREIGN KEY (
- timeline
- )
- REFERENCES timelines (id) ON DELETE CASCADE
-);
-
-INSERT INTO new_timeline_posts SELECT * FROM timeline_posts;
-
-DROP TABLE timeline_posts;
-
-ALTER TABLE new_timeline_posts RENAME TO timeline_posts;
-
-CREATE INDEX IX_timeline_posts_author ON timeline_posts (author);
-
-CREATE INDEX IX_timeline_posts_timeline ON timeline_posts(timeline);
-
-PRAGMA foreign_key_check;
-
-COMMIT TRANSACTION;
-
-PRAGMA foreign_keys = 1;
- ", true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_timeline_posts_users_author",
- table: "timeline_posts");
-
- migrationBuilder.AlterColumn<long>(
- name: "author",
- table: "timeline_posts",
- type: "INTEGER",
- nullable: false,
- oldClrType: typeof(long),
- oldNullable: true);
-
- migrationBuilder.AddForeignKey(
- name: "FK_timeline_posts_users_author",
- table: "timeline_posts",
- column: "author",
- principalTable: "users",
- principalColumn: "id",
- onDelete: ReferentialAction.Cascade);
- }
- }
-}
diff --git a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs b/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs deleted file mode 100644 index 58238557..00000000 --- a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.Designer.cs +++ /dev/null @@ -1,337 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200811080808_ChangeDateTimeOffsetToDateTime")]
- partial class ChangeDateTimeOffsetToDateTime
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.5");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTime>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs b/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs deleted file mode 100644 index eb6b44f3..00000000 --- a/Timeline/Migrations/20200811080808_ChangeDateTimeOffsetToDateTime.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class ChangeDateTimeOffsetToDateTime : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
-
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
-}
diff --git a/Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs b/Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs deleted file mode 100644 index f2279f3b..00000000 --- a/Timeline/Migrations/20200826164553_TimelineAddTitle.Designer.cs +++ /dev/null @@ -1,341 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20200826164553_TimelineAddTitle")]
- partial class TimelineAddTitle
- {
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.7");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Title")
- .HasColumnName("title")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTime>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/Migrations/20200826164553_TimelineAddTitle.cs b/Timeline/Migrations/20200826164553_TimelineAddTitle.cs deleted file mode 100644 index 7e8c498b..00000000 --- a/Timeline/Migrations/20200826164553_TimelineAddTitle.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations;
-
-namespace Timeline.Migrations
-{
- public partial class TimelineAddTitle : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn<string>(
- name: "title",
- table: "timelines",
- nullable: true);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropColumn(
- name: "title",
- table: "timelines");
- }
- }
-}
diff --git a/Timeline/Migrations/DatabaseContextModelSnapshot.cs b/Timeline/Migrations/DatabaseContextModelSnapshot.cs deleted file mode 100644 index 65ae6c9a..00000000 --- a/Timeline/Migrations/DatabaseContextModelSnapshot.cs +++ /dev/null @@ -1,339 +0,0 @@ -// <auto-generated />
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Timeline.Entities;
-
-namespace Timeline.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- partial class DatabaseContextModelSnapshot : ModelSnapshot
- {
- protected override void BuildModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "3.1.7");
-
- modelBuilder.Entity("Timeline.Entities.DataEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Data")
- .IsRequired()
- .HasColumnName("data")
- .HasColumnType("BLOB");
-
- b.Property<int>("Ref")
- .HasColumnName("ref")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Tag")
- .IsRequired()
- .HasColumnName("tag")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("Tag")
- .IsUnique();
-
- b.ToTable("data");
- });
-
- modelBuilder.Entity("Timeline.Entities.JwtTokenEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<byte[]>("Key")
- .IsRequired()
- .HasColumnName("key")
- .HasColumnType("BLOB");
-
- b.HasKey("Id");
-
- b.ToTable("jwt_token");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .HasColumnName("create_time")
- .HasColumnType("TEXT");
-
- b.Property<long>("CurrentPostLocalId")
- .HasColumnName("current_post_local_id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Description")
- .HasColumnName("description")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Name")
- .HasColumnName("name")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("NameLastModified")
- .HasColumnName("name_last_modified")
- .HasColumnType("TEXT");
-
- b.Property<long>("OwnerId")
- .HasColumnName("owner")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Title")
- .HasColumnName("title")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<int>("Visibility")
- .HasColumnName("visibility")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("OwnerId");
-
- b.ToTable("timelines");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("TimelineId");
-
- b.HasIndex("UserId");
-
- b.ToTable("timeline_members");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<long?>("AuthorId")
- .HasColumnName("author")
- .HasColumnType("INTEGER");
-
- b.Property<string>("Content")
- .HasColumnName("content")
- .HasColumnType("TEXT");
-
- b.Property<string>("ContentType")
- .IsRequired()
- .HasColumnName("content_type")
- .HasColumnType("TEXT");
-
- b.Property<string>("ExtraContent")
- .HasColumnName("extra_content")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastUpdated")
- .HasColumnName("last_updated")
- .HasColumnType("TEXT");
-
- b.Property<long>("LocalId")
- .HasColumnName("local_id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("Time")
- .HasColumnName("time")
- .HasColumnType("TEXT");
-
- b.Property<long>("TimelineId")
- .HasColumnName("timeline")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("AuthorId");
-
- b.HasIndex("TimelineId");
-
- b.ToTable("timeline_posts");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<string>("DataTag")
- .HasColumnName("data_tag")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("LastModified")
- .HasColumnName("last_modified")
- .HasColumnType("TEXT");
-
- b.Property<string>("Type")
- .HasColumnName("type")
- .HasColumnType("TEXT");
-
- b.Property<long>("UserId")
- .HasColumnName("user")
- .HasColumnType("INTEGER");
-
- b.HasKey("Id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("user_avatars");
- });
-
- modelBuilder.Entity("Timeline.Entities.UserEntity", b =>
- {
- b.Property<long>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnName("id")
- .HasColumnType("INTEGER");
-
- b.Property<DateTime>("CreateTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("create_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<DateTime>("LastModified")
- .ValueGeneratedOnAdd()
- .HasColumnName("last_modified")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<string>("Nickname")
- .HasColumnName("nickname")
- .HasColumnType("TEXT");
-
- b.Property<string>("Password")
- .IsRequired()
- .HasColumnName("password")
- .HasColumnType("TEXT");
-
- b.Property<string>("Roles")
- .IsRequired()
- .HasColumnName("roles")
- .HasColumnType("TEXT");
-
- b.Property<string>("UniqueId")
- .IsRequired()
- .ValueGeneratedOnAdd()
- .HasColumnName("unique_id")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("lower(hex(randomblob(16)))");
-
- b.Property<string>("Username")
- .IsRequired()
- .HasColumnName("username")
- .HasColumnType("TEXT");
-
- b.Property<DateTime>("UsernameChangeTime")
- .ValueGeneratedOnAdd()
- .HasColumnName("username_change_time")
- .HasColumnType("TEXT")
- .HasDefaultValueSql("datetime('now', 'utc')");
-
- b.Property<long>("Version")
- .ValueGeneratedOnAdd()
- .HasColumnName("version")
- .HasColumnType("INTEGER")
- .HasDefaultValue(0L);
-
- b.HasKey("Id");
-
- b.HasIndex("Username")
- .IsUnique();
-
- b.ToTable("users");
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Owner")
- .WithMany("Timelines")
- .HasForeignKey("OwnerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelineMemberEntity", b =>
- {
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Members")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
-
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithMany("TimelinesJoined")
- .HasForeignKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.TimelinePostEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "Author")
- .WithMany("TimelinePosts")
- .HasForeignKey("AuthorId");
-
- b.HasOne("Timeline.Entities.TimelineEntity", "Timeline")
- .WithMany("Posts")
- .HasForeignKey("TimelineId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-
- modelBuilder.Entity("Timeline.Entities.UserAvatarEntity", b =>
- {
- b.HasOne("Timeline.Entities.UserEntity", "User")
- .WithOne("Avatar")
- .HasForeignKey("Timeline.Entities.UserAvatarEntity", "UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired();
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Timeline/MockClientApp/index.html b/Timeline/MockClientApp/index.html deleted file mode 100644 index 03cf371e..00000000 --- a/Timeline/MockClientApp/index.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE html>
-<html>
-<head>
- <meta charset="utf-8" />
- <title>Mock Client App</title>
-</head>
-<body>
- This is a mock client app for testing.
-</body>
-</html>
diff --git a/Timeline/Models/ByteData.cs b/Timeline/Models/ByteData.cs deleted file mode 100644 index 7b832eb5..00000000 --- a/Timeline/Models/ByteData.cs +++ /dev/null @@ -1,33 +0,0 @@ -using NSwag.Annotations;
-
-namespace Timeline.Models
-{
- /// <summary>
- /// Model for reading http body as bytes.
- /// </summary>
- [OpenApiFile]
- public class ByteData
- {
- /// <summary>
- /// </summary>
- /// <param name="data">The data.</param>
- /// <param name="contentType">The content type.</param>
- public ByteData(byte[] data, string contentType)
- {
- Data = data;
- ContentType = contentType;
- }
-
- /// <summary>
- /// Data.
- /// </summary>
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; }
-#pragma warning restore CA1819 // Properties should not return arrays
-
- /// <summary>
- /// Content type.
- /// </summary>
- public string ContentType { get; }
- }
-}
diff --git a/Timeline/Models/Converters/JsonDateTimeConverter.cs b/Timeline/Models/Converters/JsonDateTimeConverter.cs deleted file mode 100644 index 94b5cab0..00000000 --- a/Timeline/Models/Converters/JsonDateTimeConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System;
-using System.Diagnostics;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using Timeline.Helpers;
-
-namespace Timeline.Models.Converters
-{
- public class JsonDateTimeConverter : JsonConverter<DateTime>
- {
- public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- Debug.Assert(typeToConvert == typeof(DateTime));
- return DateTime.Parse(reader.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
- }
-
- public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
- {
- writer.WriteStringValue(value.MyToUtc().ToString("s", CultureInfo.InvariantCulture) + "Z");
- }
- }
-}
diff --git a/Timeline/Models/Converters/MyDateTimeConverter.cs b/Timeline/Models/Converters/MyDateTimeConverter.cs deleted file mode 100644 index f125cd5c..00000000 --- a/Timeline/Models/Converters/MyDateTimeConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System;
-using System.ComponentModel;
-using System.Globalization;
-
-namespace Timeline.Models.Converters
-{
- public class MyDateTimeConverter : TypeConverter
- {
- public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
- {
- return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
- }
-
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
- {
- return base.CanConvertTo(context, destinationType);
- }
-
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
- {
- if (value is string text)
- {
- text = text.Trim();
- if (text.Length == 0)
- {
- return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
- }
-
- return DateTime.Parse(text, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
- }
-
- return base.ConvertFrom(context, culture, value);
- }
-
- public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
- {
- if (destinationType == typeof(string) && value is DateTime)
- {
- DateTime dt = (DateTime)value;
- if (dt == DateTime.MinValue)
- {
- return string.Empty;
- }
-
- return dt.ToString("s", CultureInfo.InvariantCulture) + "Z";
- }
-
- return base.ConvertTo(context, culture, value, destinationType);
- }
- }
-}
diff --git a/Timeline/Models/Http/ActionContextAccessorExtensions.cs b/Timeline/Models/Http/ActionContextAccessorExtensions.cs deleted file mode 100644 index bcc55c5a..00000000 --- a/Timeline/Models/Http/ActionContextAccessorExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using System;
-
-namespace Timeline.Models.Http
-{
- public static class ActionContextAccessorExtensions
- {
- public static ActionContext AssertActionContextForUrlFill(this IActionContextAccessor accessor)
- {
- return accessor.ActionContext ?? throw new InvalidOperationException(Resources.Models.Http.Exception.ActionContextNull);
- }
- }
-}
diff --git a/Timeline/Models/Http/Common.cs b/Timeline/Models/Http/Common.cs deleted file mode 100644 index 5fa22c9e..00000000 --- a/Timeline/Models/Http/Common.cs +++ /dev/null @@ -1,120 +0,0 @@ -using static Timeline.Resources.Models.Http.Common;
-
-namespace Timeline.Models.Http
-{
- public class CommonResponse
- {
- public CommonResponse()
- {
-
- }
-
- public CommonResponse(int code, string message)
- {
- Code = code;
- Message = message;
- }
-
- public int Code { get; set; }
- public string? Message { get; set; }
- }
-
- public class CommonDataResponse<T> : CommonResponse
- {
- public CommonDataResponse()
- {
-
- }
-
- public CommonDataResponse(int code, string message, T data)
- : base(code, message)
- {
- Data = data;
- }
-
- public T Data { get; set; } = default!;
- }
-
- public class CommonPutResponse : CommonDataResponse<CommonPutResponse.ResponseData>
- {
- public class ResponseData
- {
- public ResponseData() { }
-
- public ResponseData(bool create)
- {
- Create = create;
- }
-
- public bool Create { get; set; }
- }
-
- public CommonPutResponse()
- {
-
- }
-
- public CommonPutResponse(int code, string message, bool create)
- : base(code, message, new ResponseData(create))
- {
-
- }
-
- internal static CommonPutResponse Create()
- {
- return new CommonPutResponse(0, MessagePutCreate, true);
- }
-
- internal static CommonPutResponse Modify()
- {
- return new CommonPutResponse(0, MessagePutModify, false);
- }
- }
-
- /// <summary>
- /// Common response for delete method.
- /// </summary>
- public class CommonDeleteResponse : CommonDataResponse<CommonDeleteResponse.ResponseData>
- {
- /// <summary></summary>
- public class ResponseData
- {
- /// <summary></summary>
- public ResponseData() { }
-
- /// <summary></summary>
- public ResponseData(bool delete)
- {
- Delete = delete;
- }
-
- /// <summary>
- /// True if the entry is deleted. False if the entry does not exist.
- /// </summary>
- public bool Delete { get; set; }
- }
-
- /// <summary></summary>
- public CommonDeleteResponse()
- {
-
- }
-
- /// <summary></summary>
- public CommonDeleteResponse(int code, string message, bool delete)
- : base(code, message, new ResponseData(delete))
- {
-
- }
-
- internal static CommonDeleteResponse Delete()
- {
- return new CommonDeleteResponse(0, MessageDeleteDelete, true);
- }
-
- internal static CommonDeleteResponse NotExist()
- {
- return new CommonDeleteResponse(0, MessageDeleteNotExist, false);
- }
- }
-}
diff --git a/Timeline/Models/Http/ErrorResponse.cs b/Timeline/Models/Http/ErrorResponse.cs deleted file mode 100644 index ac86481f..00000000 --- a/Timeline/Models/Http/ErrorResponse.cs +++ /dev/null @@ -1,261 +0,0 @@ -using static Timeline.Resources.Messages;
-
-namespace Timeline.Models.Http
-{
- public static class ErrorResponse
- {
- public static class Common
- {
- public static CommonResponse InvalidModel(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.InvalidModel, string.Format(Common_InvalidModel, formatArgs));
- }
-
- public static CommonResponse CustomMessage_InvalidModel(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.InvalidModel, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Forbid(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Forbid, string.Format(Common_Forbid, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Forbid(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Forbid, string.Format(message, formatArgs));
- }
-
- public static CommonResponse UnknownEndpoint(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.UnknownEndpoint, string.Format(Common_UnknownEndpoint, formatArgs));
- }
-
- public static CommonResponse CustomMessage_UnknownEndpoint(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.UnknownEndpoint, string.Format(message, formatArgs));
- }
-
- public static class Header
- {
- public static CommonResponse IfNonMatch_BadFormat(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Header.IfNonMatch_BadFormat, string.Format(Common_Header_IfNonMatch_BadFormat, formatArgs));
- }
-
- public static CommonResponse CustomMessage_IfNonMatch_BadFormat(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Header.IfNonMatch_BadFormat, string.Format(message, formatArgs));
- }
-
- }
-
- public static class Content
- {
- public static CommonResponse TooBig(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Content.TooBig, string.Format(Common_Content_TooBig, formatArgs));
- }
-
- public static CommonResponse CustomMessage_TooBig(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.Common.Content.TooBig, string.Format(message, formatArgs));
- }
-
- }
-
- }
-
- public static class UserCommon
- {
- public static CommonResponse NotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserCommon.NotExist, string.Format(UserCommon_NotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_NotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserCommon.NotExist, string.Format(message, formatArgs));
- }
-
- }
-
- public static class TokenController
- {
- public static CommonResponse Create_BadCredential(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Create_BadCredential, string.Format(TokenController_Create_BadCredential, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Create_BadCredential(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Create_BadCredential, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_BadFormat(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_BadFormat, string.Format(TokenController_Verify_BadFormat, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_BadFormat(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_BadFormat, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_UserNotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_UserNotExist, string.Format(TokenController_Verify_UserNotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_UserNotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_UserNotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_OldVersion(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_OldVersion, string.Format(TokenController_Verify_OldVersion, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_OldVersion(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_OldVersion, string.Format(message, formatArgs));
- }
-
- public static CommonResponse Verify_TimeExpired(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_TimeExpired, string.Format(TokenController_Verify_TimeExpired, formatArgs));
- }
-
- public static CommonResponse CustomMessage_Verify_TimeExpired(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TokenController.Verify_TimeExpired, string.Format(message, formatArgs));
- }
-
- }
-
- public static class UserController
- {
- public static CommonResponse UsernameConflict(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.UsernameConflict, string.Format(UserController_UsernameConflict, formatArgs));
- }
-
- public static CommonResponse CustomMessage_UsernameConflict(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.UsernameConflict, string.Format(message, formatArgs));
- }
-
- public static CommonResponse ChangePassword_BadOldPassword(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.ChangePassword_BadOldPassword, string.Format(UserController_ChangePassword_BadOldPassword, formatArgs));
- }
-
- public static CommonResponse CustomMessage_ChangePassword_BadOldPassword(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserController.ChangePassword_BadOldPassword, string.Format(message, formatArgs));
- }
-
- }
-
- public static class UserAvatar
- {
- public static CommonResponse BadFormat_CantDecode(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_CantDecode, string.Format(UserAvatar_BadFormat_CantDecode, formatArgs));
- }
-
- public static CommonResponse CustomMessage_BadFormat_CantDecode(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_CantDecode, string.Format(message, formatArgs));
- }
-
- public static CommonResponse BadFormat_UnmatchedFormat(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_UnmatchedFormat, string.Format(UserAvatar_BadFormat_UnmatchedFormat, formatArgs));
- }
-
- public static CommonResponse CustomMessage_BadFormat_UnmatchedFormat(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_UnmatchedFormat, string.Format(message, formatArgs));
- }
-
- public static CommonResponse BadFormat_BadSize(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_BadSize, string.Format(UserAvatar_BadFormat_BadSize, formatArgs));
- }
-
- public static CommonResponse CustomMessage_BadFormat_BadSize(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.UserAvatar.BadFormat_BadSize, string.Format(message, formatArgs));
- }
-
- }
-
- public static class TimelineController
- {
- public static CommonResponse NameConflict(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NameConflict, string.Format(TimelineController_NameConflict, formatArgs));
- }
-
- public static CommonResponse CustomMessage_NameConflict(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NameConflict, string.Format(message, formatArgs));
- }
-
- public static CommonResponse NotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NotExist, string.Format(TimelineController_NotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_NotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.NotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse MemberPut_NotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.MemberPut_NotExist, string.Format(TimelineController_MemberPut_NotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_MemberPut_NotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.MemberPut_NotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse QueryRelateNotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, string.Format(TimelineController_QueryRelateNotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_QueryRelateNotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.QueryRelateNotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse PostNotExist(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNotExist, string.Format(TimelineController_PostNotExist, formatArgs));
- }
-
- public static CommonResponse CustomMessage_PostNotExist(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNotExist, string.Format(message, formatArgs));
- }
-
- public static CommonResponse PostNoData(params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNoData, string.Format(TimelineController_PostNoData, formatArgs));
- }
-
- public static CommonResponse CustomMessage_PostNoData(string message, params object?[] formatArgs)
- {
- return new CommonResponse(ErrorCodes.TimelineController.PostNoData, string.Format(message, formatArgs));
- }
-
- }
-
- }
-
-}
diff --git a/Timeline/Models/Http/Timeline.cs b/Timeline/Models/Http/Timeline.cs deleted file mode 100644 index a81b33f5..00000000 --- a/Timeline/Models/Http/Timeline.cs +++ /dev/null @@ -1,219 +0,0 @@ -using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Routing;
-using System;
-using System.Collections.Generic;
-using Timeline.Controllers;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Info of post content.
- /// </summary>
- public class TimelinePostContentInfo
- {
- /// <summary>
- /// Type of the post content.
- /// </summary>
- public string Type { get; set; } = default!;
- /// <summary>
- /// If post is of text type. This is the text.
- /// </summary>
- public string? Text { get; set; }
- /// <summary>
- /// If post is of image type. This is the image url.
- /// </summary>
- public string? Url { get; set; }
- /// <summary>
- /// If post has data (currently it means it's a image post), this is the data etag.
- /// </summary>
- public string? ETag { get; set; }
- }
-
- /// <summary>
- /// Info of a post.
- /// </summary>
- public class TimelinePostInfo
- {
- /// <summary>
- /// Post id.
- /// </summary>
- public long Id { get; set; }
- /// <summary>
- /// Content of the post. May be null if post is deleted.
- /// </summary>
- public TimelinePostContentInfo? Content { get; set; }
- /// <summary>
- /// True if post is deleted.
- /// </summary>
- public bool Deleted { get; set; }
- /// <summary>
- /// Post time.
- /// </summary>
- public DateTime Time { get; set; }
- /// <summary>
- /// The author. May be null if the user has been deleted.
- /// </summary>
- public UserInfo? Author { get; set; } = default!;
- /// <summary>
- /// Last updated time.
- /// </summary>
- public DateTime LastUpdated { get; set; } = default!;
- }
-
- /// <summary>
- /// Info of a timeline.
- /// </summary>
- public class TimelineInfo
- {
- /// <summary>
- /// Unique id.
- /// </summary>
- public string UniqueId { get; set; } = default!;
- /// <summary>
- /// Title.
- /// </summary>
- public string Title { get; set; } = default!;
- /// <summary>
- /// Name of timeline.
- /// </summary>
- public string Name { get; set; } = default!;
- /// <summary>
- /// Last modified time of timeline name.
- /// </summary>
- public DateTime NameLastModifed { get; set; } = default!;
- /// <summary>
- /// Timeline description.
- /// </summary>
- public string Description { get; set; } = default!;
- /// <summary>
- /// Owner of the timeline.
- /// </summary>
- public UserInfo Owner { get; set; } = default!;
- /// <summary>
- /// Visibility of the timeline.
- /// </summary>
- public TimelineVisibility Visibility { get; set; }
-#pragma warning disable CA2227 // Collection properties should be read only
- /// <summary>
- /// Members of timeline.
- /// </summary>
- public List<UserInfo> Members { get; set; } = default!;
-#pragma warning restore CA2227 // Collection properties should be read only
- /// <summary>
- /// Create time of timeline.
- /// </summary>
- public DateTime CreateTime { get; set; } = default!;
- /// <summary>
- /// Last modified time of timeline.
- /// </summary>
- public DateTime LastModified { get; set; } = default!;
-
-#pragma warning disable CA1707 // Identifiers should not contain underscores
- /// <summary>
- /// Related links.
- /// </summary>
- public TimelineInfoLinks _links { get; set; } = default!;
-#pragma warning restore CA1707 // Identifiers should not contain underscores
- }
-
- /// <summary>
- /// Related links for timeline.
- /// </summary>
- public class TimelineInfoLinks
- {
- /// <summary>
- /// Self.
- /// </summary>
- public string Self { get; set; } = default!;
- /// <summary>
- /// Posts url.
- /// </summary>
- public string Posts { get; set; } = default!;
- }
-
- public class TimelineInfoLinksValueResolver : IValueResolver<Timeline, TimelineInfo, TimelineInfoLinks>
- {
- private readonly IActionContextAccessor _actionContextAccessor;
- private readonly IUrlHelperFactory _urlHelperFactory;
-
- public TimelineInfoLinksValueResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
- {
- _actionContextAccessor = actionContextAccessor;
- _urlHelperFactory = urlHelperFactory;
- }
-
- public TimelineInfoLinks Resolve(Timeline source, TimelineInfo destination, TimelineInfoLinks destMember, ResolutionContext context)
- {
- var actionContext = _actionContextAccessor.AssertActionContextForUrlFill();
- var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext);
-
- return new TimelineInfoLinks
- {
- Self = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { source.Name }),
- Posts = urlHelper.ActionLink(nameof(TimelineController.PostListGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { source.Name })
- };
- }
- }
-
- public class TimelinePostContentResolver : IValueResolver<TimelinePost, TimelinePostInfo, TimelinePostContentInfo?>
- {
- private readonly IActionContextAccessor _actionContextAccessor;
- private readonly IUrlHelperFactory _urlHelperFactory;
-
- public TimelinePostContentResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
- {
- _actionContextAccessor = actionContextAccessor;
- _urlHelperFactory = urlHelperFactory;
- }
-
- public TimelinePostContentInfo? Resolve(TimelinePost source, TimelinePostInfo destination, TimelinePostContentInfo? destMember, ResolutionContext context)
- {
- var actionContext = _actionContextAccessor.AssertActionContextForUrlFill();
- var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext);
-
- var sourceContent = source.Content;
-
- if (sourceContent == null)
- {
- return null;
- }
-
- if (sourceContent is TextTimelinePostContent textContent)
- {
- return new TimelinePostContentInfo
- {
- Type = TimelinePostContentTypes.Text,
- Text = textContent.Text
- };
- }
- else if (sourceContent is ImageTimelinePostContent imageContent)
- {
- return new TimelinePostContentInfo
- {
- Type = TimelinePostContentTypes.Image,
- Url = urlHelper.ActionLink(
- action: nameof(TimelineController.PostDataGet),
- controller: nameof(TimelineController)[0..^nameof(Controller).Length],
- values: new { Name = source.TimelineName, Id = source.Id }),
- ETag = $"\"{imageContent.DataTag}\""
- };
- }
- else
- {
- throw new InvalidOperationException(Resources.Models.Http.Exception.UnknownPostContentType);
- }
- }
- }
-
- public class TimelineInfoAutoMapperProfile : Profile
- {
- public TimelineInfoAutoMapperProfile()
- {
- CreateMap<Timeline, TimelineInfo>().ForMember(u => u._links, opt => opt.MapFrom<TimelineInfoLinksValueResolver>());
- CreateMap<TimelinePost, TimelinePostInfo>().ForMember(p => p.Content, opt => opt.MapFrom<TimelinePostContentResolver>());
- CreateMap<TimelinePatchRequest, TimelineChangePropertyRequest>();
- }
- }
-}
diff --git a/Timeline/Models/Http/TimelineController.cs b/Timeline/Models/Http/TimelineController.cs deleted file mode 100644 index 7bd141ed..00000000 --- a/Timeline/Models/Http/TimelineController.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System;
-using System.ComponentModel.DataAnnotations;
-using Timeline.Models.Validation;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Content of post create request.
- /// </summary>
- public class TimelinePostCreateRequestContent
- {
- /// <summary>
- /// Type of post content.
- /// </summary>
- [Required]
- public string Type { get; set; } = default!;
- /// <summary>
- /// If post is of text type, this is the text.
- /// </summary>
- public string? Text { get; set; }
- /// <summary>
- /// If post is of image type, this is base64 of image data.
- /// </summary>
- public string? Data { get; set; }
- }
-
- public class TimelinePostCreateRequest
- {
- /// <summary>
- /// Content of the new post.
- /// </summary>
- [Required]
- public TimelinePostCreateRequestContent Content { get; set; } = default!;
-
- /// <summary>
- /// Time of the post. If not set, current time will be used.
- /// </summary>
- public DateTime? Time { get; set; }
- }
-
- /// <summary>
- /// Create timeline request model.
- /// </summary>
- public class TimelineCreateRequest
- {
- /// <summary>
- /// Name of the new timeline. Must be a valid name.
- /// </summary>
- [Required]
- [TimelineName]
- public string Name { get; set; } = default!;
- }
-
- /// <summary>
- /// Patch timeline request model.
- /// </summary>
- public class TimelinePatchRequest
- {
- /// <summary>
- /// New title. Null for not change.
- /// </summary>
- public string? Title { get; set; }
-
- /// <summary>
- /// New description. Null for not change.
- /// </summary>
- public string? Description { get; set; }
-
- /// <summary>
- /// New visibility. Null for not change.
- /// </summary>
- public TimelineVisibility? Visibility { get; set; }
- }
-
- /// <summary>
- /// Change timeline name request model.
- /// </summary>
- public class TimelineChangeNameRequest
- {
- /// <summary>
- /// Old name of timeline.
- /// </summary>
- [Required]
- [TimelineName]
- public string OldName { get; set; } = default!;
- /// <summary>
- /// New name of timeline.
- /// </summary>
- [Required]
- [TimelineName]
- public string NewName { get; set; } = default!;
- }
-}
diff --git a/Timeline/Models/Http/TokenController.cs b/Timeline/Models/Http/TokenController.cs deleted file mode 100644 index a42c44e5..00000000 --- a/Timeline/Models/Http/TokenController.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.ComponentModel.DataAnnotations;
-using Timeline.Controllers;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Request model for <see cref="TokenController.Create(CreateTokenRequest)"/>.
- /// </summary>
- public class CreateTokenRequest
- {
- /// <summary>
- /// The username.
- /// </summary>
- public string Username { get; set; } = default!;
- /// <summary>
- /// The password.
- /// </summary>
- public string Password { get; set; } = default!;
- /// <summary>
- /// Optional token validation period. In days. If not specified, server will use a default one.
- /// </summary>
- [Range(1, 365)]
- public int? Expire { get; set; }
- }
-
- /// <summary>
- /// Response model for <see cref="TokenController.Create(CreateTokenRequest)"/>.
- /// </summary>
- public class CreateTokenResponse
- {
- /// <summary>
- /// The token created.
- /// </summary>
- public string Token { get; set; } = default!;
- /// <summary>
- /// The user owning the token.
- /// </summary>
- public UserInfo User { get; set; } = default!;
- }
-
- /// <summary>
- /// Request model for <see cref="TokenController.Verify(VerifyTokenRequest)"/>.
- /// </summary>
- public class VerifyTokenRequest
- {
- /// <summary>
- /// The token to verify.
- /// </summary>
- public string Token { get; set; } = default!;
- }
-
- /// <summary>
- /// Response model for <see cref="TokenController.Verify(VerifyTokenRequest)"/>.
- /// </summary>
- public class VerifyTokenResponse
- {
- /// <summary>
- /// The user owning the token.
- /// </summary>
- public UserInfo User { get; set; } = default!;
- }
-}
diff --git a/Timeline/Models/Http/UserController.cs b/Timeline/Models/Http/UserController.cs deleted file mode 100644 index 6bc5a66e..00000000 --- a/Timeline/Models/Http/UserController.cs +++ /dev/null @@ -1,93 +0,0 @@ -using AutoMapper;
-using System.ComponentModel.DataAnnotations;
-using Timeline.Controllers;
-using Timeline.Models.Validation;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Request model for <see cref="UserController.Patch(UserPatchRequest, string)"/>.
- /// </summary>
- public class UserPatchRequest
- {
- /// <summary>
- /// New username. Null if not change. Need to be administrator.
- /// </summary>
- [Username]
- public string? Username { get; set; }
-
- /// <summary>
- /// New password. Null if not change. Need to be administrator.
- /// </summary>
- [MinLength(1)]
- public string? Password { get; set; }
-
- /// <summary>
- /// New nickname. Null if not change. Need to be administrator to change other's.
- /// </summary>
- [Nickname]
- public string? Nickname { get; set; }
-
- /// <summary>
- /// Whether to be administrator. Null if not change. Need to be administrator.
- /// </summary>
- public bool? Administrator { get; set; }
- }
-
- /// <summary>
- /// Request model for <see cref="UserController.CreateUser(CreateUserRequest)"/>.
- /// </summary>
- public class CreateUserRequest
- {
- /// <summary>
- /// Username of the new user.
- /// </summary>
- [Required, Username]
- public string Username { get; set; } = default!;
-
- /// <summary>
- /// Password of the new user.
- /// </summary>
- [Required, MinLength(1)]
- public string Password { get; set; } = default!;
-
- /// <summary>
- /// Whether the new user is administrator.
- /// </summary>
- [Required]
- public bool? Administrator { get; set; }
-
- /// <summary>
- /// Nickname of the new user.
- /// </summary>
- [Nickname]
- public string? Nickname { get; set; }
- }
-
- /// <summary>
- /// Request model for <see cref="UserController.ChangePassword(ChangePasswordRequest)"/>.
- /// </summary>
- public class ChangePasswordRequest
- {
- /// <summary>
- /// Old password.
- /// </summary>
- [Required(AllowEmptyStrings = false)]
- public string OldPassword { get; set; } = default!;
-
- /// <summary>
- /// New password.
- /// </summary>
- [Required(AllowEmptyStrings = false)]
- public string NewPassword { get; set; } = default!;
- }
-
- public class UserControllerAutoMapperProfile : Profile
- {
- public UserControllerAutoMapperProfile()
- {
- CreateMap<UserPatchRequest, User>(MemberList.Source);
- CreateMap<CreateUserRequest, User>(MemberList.Source);
- }
- }
-}
diff --git a/Timeline/Models/Http/UserInfo.cs b/Timeline/Models/Http/UserInfo.cs deleted file mode 100644 index d92a12c4..00000000 --- a/Timeline/Models/Http/UserInfo.cs +++ /dev/null @@ -1,90 +0,0 @@ -using AutoMapper;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Routing;
-using Timeline.Controllers;
-
-namespace Timeline.Models.Http
-{
- /// <summary>
- /// Info of a user.
- /// </summary>
- public class UserInfo
- {
- /// <summary>
- /// Unique id.
- /// </summary>
- public string UniqueId { get; set; } = default!;
- /// <summary>
- /// Username.
- /// </summary>
- public string Username { get; set; } = default!;
- /// <summary>
- /// Nickname.
- /// </summary>
- public string Nickname { get; set; } = default!;
- /// <summary>
- /// True if the user is a administrator.
- /// </summary>
- public bool? Administrator { get; set; } = default!;
-#pragma warning disable CA1707 // Identifiers should not contain underscores
- /// <summary>
- /// Related links.
- /// </summary>
- public UserInfoLinks _links { get; set; } = default!;
-#pragma warning restore CA1707 // Identifiers should not contain underscores
- }
-
- /// <summary>
- /// Related links for user.
- /// </summary>
- public class UserInfoLinks
- {
- /// <summary>
- /// Self.
- /// </summary>
- public string Self { get; set; } = default!;
- /// <summary>
- /// Avatar url.
- /// </summary>
- public string Avatar { get; set; } = default!;
- /// <summary>
- /// Personal timeline url.
- /// </summary>
- public string Timeline { get; set; } = default!;
- }
-
- public class UserInfoLinksValueResolver : IValueResolver<User, UserInfo, UserInfoLinks>
- {
- private readonly IActionContextAccessor _actionContextAccessor;
- private readonly IUrlHelperFactory _urlHelperFactory;
-
- public UserInfoLinksValueResolver(IActionContextAccessor actionContextAccessor, IUrlHelperFactory urlHelperFactory)
- {
- _actionContextAccessor = actionContextAccessor;
- _urlHelperFactory = urlHelperFactory;
- }
-
- public UserInfoLinks Resolve(User source, UserInfo destination, UserInfoLinks destMember, ResolutionContext context)
- {
- var actionContext = _actionContextAccessor.AssertActionContextForUrlFill();
- var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext);
-
- var result = new UserInfoLinks
- {
- Self = urlHelper.ActionLink(nameof(UserController.Get), nameof(UserController)[0..^nameof(Controller).Length], new { destination.Username }),
- Avatar = urlHelper.ActionLink(nameof(UserAvatarController.Get), nameof(UserAvatarController)[0..^nameof(Controller).Length], new { destination.Username }),
- Timeline = urlHelper.ActionLink(nameof(TimelineController.TimelineGet), nameof(TimelineController)[0..^nameof(Controller).Length], new { Name = "@" + destination.Username })
- };
- return result;
- }
- }
-
- public class UserInfoAutoMapperProfile : Profile
- {
- public UserInfoAutoMapperProfile()
- {
- CreateMap<User, UserInfo>().ForMember(u => u._links, opt => opt.MapFrom<UserInfoLinksValueResolver>());
- }
- }
-}
diff --git a/Timeline/Models/Timeline.cs b/Timeline/Models/Timeline.cs deleted file mode 100644 index a5987577..00000000 --- a/Timeline/Models/Timeline.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System;
-using System.Collections.Generic;
-
-namespace Timeline.Models
-{
- public enum TimelineVisibility
- {
- /// <summary>
- /// All people including those without accounts.
- /// </summary>
- Public,
- /// <summary>
- /// Only people signed in.
- /// </summary>
- Register,
- /// <summary>
- /// Only member.
- /// </summary>
- Private
- }
-
- public static class TimelinePostContentTypes
- {
- public const string Text = "text";
- public const string Image = "image";
- }
-
- public interface ITimelinePostContent
- {
- public string Type { get; }
- }
-
- public class TextTimelinePostContent : ITimelinePostContent
- {
- public TextTimelinePostContent(string text) { Text = text; }
-
- public string Type { get; } = TimelinePostContentTypes.Text;
- public string Text { get; set; }
- }
-
- public class ImageTimelinePostContent : ITimelinePostContent
- {
- public ImageTimelinePostContent(string dataTag) { DataTag = dataTag; }
-
- public string Type { get; } = TimelinePostContentTypes.Image;
-
- /// <summary>
- /// The tag of the data. The tag of the entry in DataManager. Also the etag (not quoted).
- /// </summary>
- public string DataTag { get; set; }
- }
-
- public class TimelinePost
- {
- public TimelinePost(long id, ITimelinePostContent? content, DateTime time, User? author, DateTime lastUpdated, string timelineName)
- {
- Id = id;
- Content = content;
- Time = time;
- Author = author;
- LastUpdated = lastUpdated;
- TimelineName = timelineName;
- }
-
- public long Id { get; set; }
- public ITimelinePostContent? Content { get; set; }
- public bool Deleted => Content == null;
- public DateTime Time { get; set; }
- public User? Author { get; set; }
- public DateTime LastUpdated { get; set; }
- public string TimelineName { get; set; }
- }
-
-#pragma warning disable CA1724 // Type names should not match namespaces
- public class Timeline
-#pragma warning restore CA1724 // Type names should not match namespaces
- {
- public string UniqueID { get; set; } = default!;
- public string Name { get; set; } = default!;
- public DateTime NameLastModified { get; set; } = default!;
- public string Title { get; set; } = default!;
- public string Description { get; set; } = default!;
- public User Owner { get; set; } = default!;
- public TimelineVisibility Visibility { get; set; }
-#pragma warning disable CA2227 // Collection properties should be read only
- public List<User> Members { get; set; } = default!;
-#pragma warning restore CA2227 // Collection properties should be read only
- public DateTime CreateTime { get; set; } = default!;
- public DateTime LastModified { get; set; } = default!;
- }
-
- public class TimelineChangePropertyRequest
- {
- public string? Title { get; set; }
- public string? Description { get; set; }
- public TimelineVisibility? Visibility { get; set; }
- }
-}
diff --git a/Timeline/Models/User.cs b/Timeline/Models/User.cs deleted file mode 100644 index f08a62db..00000000 --- a/Timeline/Models/User.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System;
-
-namespace Timeline.Models
-{
- public class User
- {
- public string? UniqueId { get; set; }
- public string? Username { get; set; }
- public string? Nickname { get; set; }
- public bool? Administrator { get; set; }
-
- #region secret
- public long? Id { get; set; }
- public string? Password { get; set; }
- public long? Version { get; set; }
- public DateTime? UsernameChangeTime { get; set; }
- public DateTime? CreateTime { get; set; }
- public DateTime? LastModified { get; set; }
- #endregion secret
- }
-}
diff --git a/Timeline/Models/Validation/GeneralTimelineNameValidator.cs b/Timeline/Models/Validation/GeneralTimelineNameValidator.cs deleted file mode 100644 index e1c96fbd..00000000 --- a/Timeline/Models/Validation/GeneralTimelineNameValidator.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System;
-
-namespace Timeline.Models.Validation
-{
- public class GeneralTimelineNameValidator : Validator<string>
- {
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
- private readonly TimelineNameValidator _timelineNameValidator = new TimelineNameValidator();
-
- protected override (bool, string) DoValidate(string value)
- {
- if (value.StartsWith('@'))
- {
- return _usernameValidator.Validate(value.Substring(1));
- }
- else
- {
- return _timelineNameValidator.Validate(value);
- }
- }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class GeneralTimelineNameAttribute : ValidateWithAttribute
- {
- public GeneralTimelineNameAttribute()
- : base(typeof(GeneralTimelineNameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/NameValidator.cs b/Timeline/Models/Validation/NameValidator.cs deleted file mode 100644 index b74c40b7..00000000 --- a/Timeline/Models/Validation/NameValidator.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Linq;
-using System.Text.RegularExpressions;
-using static Timeline.Resources.Models.Validation.NameValidator;
-
-namespace Timeline.Models.Validation
-{
- public class NameValidator : Validator<string>
- {
- private static Regex UniqueIdRegex { get; } = new Regex(@"^[a-zA-Z0-9]{32}$");
-
- public const int MaxLength = 26;
-
- protected override (bool, string) DoValidate(string value)
- {
- if (value.Length == 0)
- {
- return (false, MessageEmptyString);
- }
-
- if (value.Length > MaxLength)
- {
- return (false, MessageTooLong);
- }
-
- foreach ((char c, int i) in value.Select((c, i) => (c, i)))
- {
- if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_'))
- {
- return (false, MessageInvalidChar);
- }
- }
-
- // Currently name can't be longer than 26. So this is not needed. But reserve it for future use.
- if (UniqueIdRegex.IsMatch(value))
- {
- return (false, MessageUnqiueId);
- }
-
- return (true, GetSuccessMessage());
- }
- }
-}
diff --git a/Timeline/Models/Validation/NicknameValidator.cs b/Timeline/Models/Validation/NicknameValidator.cs deleted file mode 100644 index 1d6ab163..00000000 --- a/Timeline/Models/Validation/NicknameValidator.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System;
-using static Timeline.Resources.Models.Validation.NicknameValidator;
-
-namespace Timeline.Models.Validation
-{
- public class NicknameValidator : Validator<string>
- {
- protected override (bool, string) DoValidate(string value)
- {
- if (value.Length > 25)
- return (false, MessageTooLong);
-
- return (true, GetSuccessMessage());
- }
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
- public class NicknameAttribute : ValidateWithAttribute
- {
- public NicknameAttribute() : base(typeof(NicknameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/TimelineNameValidator.cs b/Timeline/Models/Validation/TimelineNameValidator.cs deleted file mode 100644 index f1ab54e8..00000000 --- a/Timeline/Models/Validation/TimelineNameValidator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System;
-
-namespace Timeline.Models.Validation
-{
- public class TimelineNameValidator : NameValidator
- {
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class TimelineNameAttribute : ValidateWithAttribute
- {
- public TimelineNameAttribute()
- : base(typeof(TimelineNameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/UsernameValidator.cs b/Timeline/Models/Validation/UsernameValidator.cs deleted file mode 100644 index 87bbf85f..00000000 --- a/Timeline/Models/Validation/UsernameValidator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System;
-
-namespace Timeline.Models.Validation
-{
- public class UsernameValidator : NameValidator
- {
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class UsernameAttribute : ValidateWithAttribute
- {
- public UsernameAttribute()
- : base(typeof(UsernameValidator))
- {
-
- }
- }
-}
diff --git a/Timeline/Models/Validation/Validator.cs b/Timeline/Models/Validation/Validator.cs deleted file mode 100644 index aef7891c..00000000 --- a/Timeline/Models/Validation/Validator.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System;
-using System.ComponentModel.DataAnnotations;
-using static Timeline.Resources.Models.Validation.Validator;
-
-namespace Timeline.Models.Validation
-{
- /// <summary>
- /// A validator to validate value.
- /// </summary>
- public interface IValidator
- {
- /// <summary>
- /// Validate given value.
- /// </summary>
- /// <param name="value">The value to validate.</param>
- /// <returns>Validation success or not and message.</returns>
- (bool, string) Validate(object? value);
- }
-
- public static class ValidatorExtensions
- {
- public static bool Validate(this IValidator validator, object? value, out string message)
- {
- if (validator == null)
- throw new ArgumentNullException(nameof(validator));
-
- var (r, m) = validator.Validate(value);
- message = m;
- return r;
- }
- }
-
- /// <summary>
- /// Convenient base class for validator.
- /// </summary>
- /// <typeparam name="T">The type of accepted value.</typeparam>
- /// <remarks>
- /// Subclass should override <see cref="DoValidate(T)"/> to do the real validation.
- /// This class will check the nullity and type of value.
- /// If value is null, it will pass or fail depending on <see cref="PermitNull"/>.
- /// If value is not null and not of type <typeparamref name="T"/>
- /// it will fail and not call <see cref="DoValidate(T)"/>.
- ///
- /// <see cref="PermitNull"/> is true by default.
- ///
- /// If you want some other behaviours, write the validator from scratch.
- /// </remarks>
- public abstract class Validator<T> : IValidator
- {
- protected bool PermitNull { get; set; } = true;
-
- public (bool, string) Validate(object? value)
- {
- if (value == null)
- {
- if (PermitNull)
- return (true, GetSuccessMessage());
- else
- return (false, ValidatorMessageNull);
- }
-
- if (value is T v)
- {
- return DoValidate(v);
- }
- else
- {
- return (false, ValidatorMessageBadType);
- }
- }
-
- protected static string GetSuccessMessage() => ValidatorMessageSuccess;
-
- protected abstract (bool, string) DoValidate(T value);
- }
-
- [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
- AllowMultiple = false)]
- public class ValidateWithAttribute : ValidationAttribute
- {
- private readonly IValidator _validator;
-
- /// <summary>
- /// Create with a given validator.
- /// </summary>
- /// <param name="validator">The validator used to validate.</param>
- public ValidateWithAttribute(IValidator validator)
- {
- _validator = validator ?? throw new ArgumentNullException(nameof(validator));
- }
-
- /// <summary>
- /// Create the validator with default constructor.
- /// </summary>
- /// <param name="validatorType">The type of the validator.</param>
- public ValidateWithAttribute(Type validatorType)
- {
- if (validatorType == null)
- throw new ArgumentNullException(nameof(validatorType));
-
- if (!typeof(IValidator).IsAssignableFrom(validatorType))
- throw new ArgumentException(ValidateWithAttributeExceptionNotValidator, nameof(validatorType));
-
- try
- {
- _validator = (Activator.CreateInstance(validatorType) as IValidator)!;
- }
- catch (Exception e)
- {
- throw new ArgumentException(ValidateWithAttributeExceptionCreateFail, e);
- }
- }
-
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- var (result, message) = _validator.Validate(value);
- if (result)
- {
- return ValidationResult.Success;
- }
- else
- {
- return new ValidationResult(message);
- }
- }
- }
-}
diff --git a/Timeline/Program.cs b/Timeline/Program.cs deleted file mode 100644 index 87e330a2..00000000 --- a/Timeline/Program.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Hosting;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using System.Resources;
-using Timeline.Entities;
-using Timeline.Services;
-
-[assembly: NeutralResourcesLanguage("en")]
-
-namespace Timeline
-{
- public static class Program
- {
- public static void Main(string[] args)
- {
- var host = CreateWebHostBuilder(args).Build();
-
- var env = host.Services.GetRequiredService<IWebHostEnvironment>();
-
- var databaseBackupService = host.Services.GetRequiredService<IDatabaseBackupService>();
- databaseBackupService.BackupNow();
-
- if (env.IsProduction())
- {
- using (var scope = host.Services.CreateScope())
- {
- var databaseContext = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
- databaseContext.Database.Migrate();
- }
- }
-
- host.Run();
- }
-
- public static IHostBuilder CreateWebHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup<Startup>();
- });
- }
-}
diff --git a/Timeline/Properties/launchSettings.json b/Timeline/Properties/launchSettings.json deleted file mode 100644 index de8186db..00000000 --- a/Timeline/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{
- "profiles": {
- "Development": {
- "commandName": "Project",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "ASPNETCORE_USEPROXYFRONTEND": "true",
- "ASPNETCORE_WORKDIR": "D:\\timeline-development"
- }
- },
- "Development-Mock": {
- "commandName": "Project",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "ASPNETCORE_USEMOCKFRONTEND": "true",
- "ASPNETCORE_WORKDIR": "D:\\timeline-development"
- }
- },
- "Staging": {
- "commandName": "Project",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Staging",
- "ASPNETCORE_WORKDIR": "D:\\timeline-development"
- }
- }
- }
-}
diff --git a/Timeline/Resources/Authentication/AuthHandler.Designer.cs b/Timeline/Resources/Authentication/AuthHandler.Designer.cs deleted file mode 100644 index fd4540ea..00000000 --- a/Timeline/Resources/Authentication/AuthHandler.Designer.cs +++ /dev/null @@ -1,99 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Authentication {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class AuthHandler {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal AuthHandler() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Authentication.AuthHandler", typeof(AuthHandler).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token is found in authorization header. Token is {0} ..
- /// </summary>
- internal static string LogTokenFoundInHeader {
- get {
- return ResourceManager.GetString("LogTokenFoundInHeader", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token is found in query param with key "{0}". Token is {1} ..
- /// </summary>
- internal static string LogTokenFoundInQuery {
- get {
- return ResourceManager.GetString("LogTokenFoundInQuery", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to No jwt token is found..
- /// </summary>
- internal static string LogTokenNotFound {
- get {
- return ResourceManager.GetString("LogTokenNotFound", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A jwt token validation failed..
- /// </summary>
- internal static string LogTokenValidationFail {
- get {
- return ResourceManager.GetString("LogTokenValidationFail", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Authentication/AuthHandler.resx b/Timeline/Resources/Authentication/AuthHandler.resx deleted file mode 100644 index 4cddc8ce..00000000 --- a/Timeline/Resources/Authentication/AuthHandler.resx +++ /dev/null @@ -1,132 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogTokenFoundInHeader" xml:space="preserve">
- <value>Token is found in authorization header. Token is {0} .</value>
- </data>
- <data name="LogTokenFoundInQuery" xml:space="preserve">
- <value>Token is found in query param with key "{0}". Token is {1} .</value>
- </data>
- <data name="LogTokenNotFound" xml:space="preserve">
- <value>No jwt token is found.</value>
- </data>
- <data name="LogTokenValidationFail" xml:space="preserve">
- <value>A jwt token validation failed.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs b/Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs deleted file mode 100644 index 70a1d605..00000000 --- a/Timeline/Resources/Controllers/ControllerAuthExtensions.Designer.cs +++ /dev/null @@ -1,81 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class ControllerAuthExtensions {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal ControllerAuthExtensions() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.ControllerAuthExtensions", typeof(ControllerAuthExtensions).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Failed to get user id because User has no NameIdentifier claim..
- /// </summary>
- internal static string ExceptionNoUserIdentifierClaim {
- get {
- return ResourceManager.GetString("ExceptionNoUserIdentifierClaim", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Failed to get user id because NameIdentifier claim is not a number..
- /// </summary>
- internal static string ExceptionUserIdentifierClaimBadFormat {
- get {
- return ResourceManager.GetString("ExceptionUserIdentifierClaimBadFormat", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/ControllerAuthExtensions.resx b/Timeline/Resources/Controllers/ControllerAuthExtensions.resx deleted file mode 100644 index 03e6d95a..00000000 --- a/Timeline/Resources/Controllers/ControllerAuthExtensions.resx +++ /dev/null @@ -1,126 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionNoUserIdentifierClaim" xml:space="preserve">
- <value>Failed to get user id because User has no NameIdentifier claim.</value>
- </data>
- <data name="ExceptionUserIdentifierClaimBadFormat" xml:space="preserve">
- <value>Failed to get user id because NameIdentifier claim is not a number.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Controllers/TimelineController.Designer.cs b/Timeline/Resources/Controllers/TimelineController.Designer.cs deleted file mode 100644 index ae6414e6..00000000 --- a/Timeline/Resources/Controllers/TimelineController.Designer.cs +++ /dev/null @@ -1,81 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class TimelineController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal TimelineController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.TimelineController", typeof(TimelineController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An unknown timeline visibility value. Can't convert it..
- /// </summary>
- internal static string ExceptionStringToVisibility {
- get {
- return ResourceManager.GetString("ExceptionStringToVisibility", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An unknown TimelineMemberOperationUserException is thrown. Can't recognize its inner exception. It is rethrown..
- /// </summary>
- internal static string LogUnknownTimelineMemberOperationUserException {
- get {
- return ResourceManager.GetString("LogUnknownTimelineMemberOperationUserException", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/TimelineController.resx b/Timeline/Resources/Controllers/TimelineController.resx deleted file mode 100644 index 4cf3d6fb..00000000 --- a/Timeline/Resources/Controllers/TimelineController.resx +++ /dev/null @@ -1,126 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionStringToVisibility" xml:space="preserve">
- <value>An unknown timeline visibility value. Can't convert it.</value>
- </data>
- <data name="LogUnknownTimelineMemberOperationUserException" xml:space="preserve">
- <value>An unknown TimelineMemberOperationUserException is thrown. Can't recognize its inner exception. It is rethrown.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Controllers/TokenController.Designer.cs b/Timeline/Resources/Controllers/TokenController.Designer.cs deleted file mode 100644 index a7c2864b..00000000 --- a/Timeline/Resources/Controllers/TokenController.Designer.cs +++ /dev/null @@ -1,153 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class TokenController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal TokenController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.TokenController", typeof(TokenController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The password is wrong..
- /// </summary>
- internal static string LogBadPassword {
- get {
- return ResourceManager.GetString("LogBadPassword", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user failed to create a token..
- /// </summary>
- internal static string LogCreateFailure {
- get {
- return ResourceManager.GetString("LogCreateFailure", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user succeeded to create a token..
- /// </summary>
- internal static string LogCreateSuccess {
- get {
- return ResourceManager.GetString("LogCreateSuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user does not exist..
- /// </summary>
- internal static string LogUserNotExist {
- get {
- return ResourceManager.GetString("LogUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format. It might not be created by the server..
- /// </summary>
- internal static string LogVerifyBadFormat {
- get {
- return ResourceManager.GetString("LogVerifyBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired..
- /// </summary>
- internal static string LogVerifyExpire {
- get {
- return ResourceManager.GetString("LogVerifyExpire", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token failed to be verified..
- /// </summary>
- internal static string LogVerifyFailure {
- get {
- return ResourceManager.GetString("LogVerifyFailure", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token has an old version. User might have update some info..
- /// </summary>
- internal static string LogVerifyOldVersion {
- get {
- return ResourceManager.GetString("LogVerifyOldVersion", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A token succeeded to be verified..
- /// </summary>
- internal static string LogVerifySuccess {
- get {
- return ResourceManager.GetString("LogVerifySuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to User does not exist. Administrator might have deleted this user..
- /// </summary>
- internal static string LogVerifyUserNotExist {
- get {
- return ResourceManager.GetString("LogVerifyUserNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/TokenController.resx b/Timeline/Resources/Controllers/TokenController.resx deleted file mode 100644 index 683d6cc9..00000000 --- a/Timeline/Resources/Controllers/TokenController.resx +++ /dev/null @@ -1,150 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogBadPassword" xml:space="preserve">
- <value>The password is wrong.</value>
- </data>
- <data name="LogCreateFailure" xml:space="preserve">
- <value>A user failed to create a token.</value>
- </data>
- <data name="LogCreateSuccess" xml:space="preserve">
- <value>A user succeeded to create a token.</value>
- </data>
- <data name="LogUserNotExist" xml:space="preserve">
- <value>The user does not exist.</value>
- </data>
- <data name="LogVerifyBadFormat" xml:space="preserve">
- <value>The token is of bad format. It might not be created by the server.</value>
- </data>
- <data name="LogVerifyExpire" xml:space="preserve">
- <value>The token is expired.</value>
- </data>
- <data name="LogVerifyFailure" xml:space="preserve">
- <value>A token failed to be verified.</value>
- </data>
- <data name="LogVerifyOldVersion" xml:space="preserve">
- <value>Token has an old version. User might have update some info.</value>
- </data>
- <data name="LogVerifySuccess" xml:space="preserve">
- <value>A token succeeded to be verified.</value>
- </data>
- <data name="LogVerifyUserNotExist" xml:space="preserve">
- <value>User does not exist. Administrator might have deleted this user.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Controllers/UserAvatarController.Designer.cs b/Timeline/Resources/Controllers/UserAvatarController.Designer.cs deleted file mode 100644 index b0c35ff9..00000000 --- a/Timeline/Resources/Controllers/UserAvatarController.Designer.cs +++ /dev/null @@ -1,144 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserAvatarController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserAvatarController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.UserAvatarController", typeof(UserAvatarController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown AvatarDataException.ErrorReason value..
- /// </summary>
- internal static string ExceptionUnknownAvatarFormatError {
- get {
- return ResourceManager.GetString("ExceptionUnknownAvatarFormatError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to delete a avatar of other user as a non-admin failed..
- /// </summary>
- internal static string LogDeleteForbid {
- get {
- return ResourceManager.GetString("LogDeleteForbid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to delete a avatar of a non-existent user failed..
- /// </summary>
- internal static string LogDeleteNotExist {
- get {
- return ResourceManager.GetString("LogDeleteNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Succeed to delete a avatar of a user..
- /// </summary>
- internal static string LogDeleteSuccess {
- get {
- return ResourceManager.GetString("LogDeleteSuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to get a avatar of a non-existent user failed..
- /// </summary>
- internal static string LogGetUserNotExist {
- get {
- return ResourceManager.GetString("LogGetUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to put a avatar of other user as a non-admin failed..
- /// </summary>
- internal static string LogPutForbid {
- get {
- return ResourceManager.GetString("LogPutForbid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Succeed to put a avatar of a user..
- /// </summary>
- internal static string LogPutSuccess {
- get {
- return ResourceManager.GetString("LogPutSuccess", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to put a avatar of a bad format failed..
- /// </summary>
- internal static string LogPutUserBadFormat {
- get {
- return ResourceManager.GetString("LogPutUserBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to put a avatar of a non-existent user failed..
- /// </summary>
- internal static string LogPutUserNotExist {
- get {
- return ResourceManager.GetString("LogPutUserNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/UserAvatarController.resx b/Timeline/Resources/Controllers/UserAvatarController.resx deleted file mode 100644 index 864d96c0..00000000 --- a/Timeline/Resources/Controllers/UserAvatarController.resx +++ /dev/null @@ -1,147 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionUnknownAvatarFormatError" xml:space="preserve">
- <value>Unknown AvatarDataException.ErrorReason value.</value>
- </data>
- <data name="LogDeleteForbid" xml:space="preserve">
- <value>Attempt to delete a avatar of other user as a non-admin failed.</value>
- </data>
- <data name="LogDeleteNotExist" xml:space="preserve">
- <value>Attempt to delete a avatar of a non-existent user failed.</value>
- </data>
- <data name="LogDeleteSuccess" xml:space="preserve">
- <value>Succeed to delete a avatar of a user.</value>
- </data>
- <data name="LogGetUserNotExist" xml:space="preserve">
- <value>Attempt to get a avatar of a non-existent user failed.</value>
- </data>
- <data name="LogPutForbid" xml:space="preserve">
- <value>Attempt to put a avatar of other user as a non-admin failed.</value>
- </data>
- <data name="LogPutSuccess" xml:space="preserve">
- <value>Succeed to put a avatar of a user.</value>
- </data>
- <data name="LogPutUserBadFormat" xml:space="preserve">
- <value>Attempt to put a avatar of a bad format failed.</value>
- </data>
- <data name="LogPutUserNotExist" xml:space="preserve">
- <value>Attempt to put a avatar of a non-existent user failed.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Controllers/UserController.Designer.cs b/Timeline/Resources/Controllers/UserController.Designer.cs deleted file mode 100644 index c8067614..00000000 --- a/Timeline/Resources/Controllers/UserController.Designer.cs +++ /dev/null @@ -1,117 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Controllers {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserController {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserController() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Controllers.UserController", typeof(UserController).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown PutResult..
- /// </summary>
- internal static string ExceptionUnknownPutResult {
- get {
- return ResourceManager.GetString("ExceptionUnknownPutResult", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to change password with wrong old password failed..
- /// </summary>
- internal static string LogChangePasswordBadPassword {
- get {
- return ResourceManager.GetString("LogChangePasswordBadPassword", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to change a user's username to a existent one failed..
- /// </summary>
- internal static string LogChangeUsernameConflict {
- get {
- return ResourceManager.GetString("LogChangeUsernameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to change a username of a user that does not exist failed..
- /// </summary>
- internal static string LogChangeUsernameNotExist {
- get {
- return ResourceManager.GetString("LogChangeUsernameNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to retrieve info of a user that does not exist failed..
- /// </summary>
- internal static string LogGetUserNotExist {
- get {
- return ResourceManager.GetString("LogGetUserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Attempt to patch a user that does not exist failed..
- /// </summary>
- internal static string LogPatchUserNotExist {
- get {
- return ResourceManager.GetString("LogPatchUserNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Controllers/UserController.resx b/Timeline/Resources/Controllers/UserController.resx deleted file mode 100644 index 0bdf4845..00000000 --- a/Timeline/Resources/Controllers/UserController.resx +++ /dev/null @@ -1,138 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionUnknownPutResult" xml:space="preserve">
- <value>Unknown PutResult.</value>
- </data>
- <data name="LogChangePasswordBadPassword" xml:space="preserve">
- <value>Attempt to change password with wrong old password failed.</value>
- </data>
- <data name="LogChangeUsernameConflict" xml:space="preserve">
- <value>Attempt to change a user's username to a existent one failed.</value>
- </data>
- <data name="LogChangeUsernameNotExist" xml:space="preserve">
- <value>Attempt to change a username of a user that does not exist failed.</value>
- </data>
- <data name="LogGetUserNotExist" xml:space="preserve">
- <value>Attempt to retrieve info of a user that does not exist failed.</value>
- </data>
- <data name="LogPatchUserNotExist" xml:space="preserve">
- <value>Attempt to patch a user that does not exist failed.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Entities.Designer.cs b/Timeline/Resources/Entities.Designer.cs deleted file mode 100644 index 5f286f23..00000000 --- a/Timeline/Resources/Entities.Designer.cs +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Entities {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Entities() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Entities", typeof(Entities).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Only sqlite is supported..
- /// </summary>
- internal static string ExceptionOnlySqliteSupported {
- get {
- return ResourceManager.GetString("ExceptionOnlySqliteSupported", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Entities.resx b/Timeline/Resources/Entities.resx deleted file mode 100644 index 1538b533..00000000 --- a/Timeline/Resources/Entities.resx +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionOnlySqliteSupported" xml:space="preserve">
- <value>Only sqlite is supported.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Filters.Designer.cs b/Timeline/Resources/Filters.Designer.cs deleted file mode 100644 index dedfe498..00000000 --- a/Timeline/Resources/Filters.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Filters {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Filters() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Filters", typeof(Filters).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You apply a SelfOrAdminAttribute on an action, but there is no user. Try add AuthorizeAttribute..
- /// </summary>
- internal static string LogSelfOrAdminNoUser {
- get {
- return ResourceManager.GetString("LogSelfOrAdminNoUser", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You apply a SelfOrAdminAttribute on an action, but it does not have a model named username..
- /// </summary>
- internal static string LogSelfOrAdminNoUsername {
- get {
- return ResourceManager.GetString("LogSelfOrAdminNoUsername", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You apply a SelfOrAdminAttribute on an action, found a model named username, but it is not string..
- /// </summary>
- internal static string LogSelfOrAdminUsernameNotString {
- get {
- return ResourceManager.GetString("LogSelfOrAdminUsernameNotString", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Filters.resx b/Timeline/Resources/Filters.resx deleted file mode 100644 index 22620889..00000000 --- a/Timeline/Resources/Filters.resx +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogSelfOrAdminNoUser" xml:space="preserve">
- <value>You apply a SelfOrAdminAttribute on an action, but there is no user. Try add AuthorizeAttribute.</value>
- </data>
- <data name="LogSelfOrAdminNoUsername" xml:space="preserve">
- <value>You apply a SelfOrAdminAttribute on an action, but it does not have a model named username.</value>
- </data>
- <data name="LogSelfOrAdminUsernameNotString" xml:space="preserve">
- <value>You apply a SelfOrAdminAttribute on an action, found a model named username, but it is not string.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Helper/DataCacheHelper.Designer.cs b/Timeline/Resources/Helper/DataCacheHelper.Designer.cs deleted file mode 100644 index acf56d13..00000000 --- a/Timeline/Resources/Helper/DataCacheHelper.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Helper {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class DataCacheHelper {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal DataCacheHelper() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Helper.DataCacheHelper", typeof(DataCacheHelper).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header If-None-Match is of bad format..
- /// </summary>
- internal static string LogBadIfNoneMatch {
- get {
- return ResourceManager.GetString("LogBadIfNoneMatch", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Cache is invalid and data is returned..
- /// </summary>
- internal static string LogResultData {
- get {
- return ResourceManager.GetString("LogResultData", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Cache is valid and 304 Not Modified is returned..
- /// </summary>
- internal static string LogResultNotModified {
- get {
- return ResourceManager.GetString("LogResultNotModified", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Helper/DataCacheHelper.resx b/Timeline/Resources/Helper/DataCacheHelper.resx deleted file mode 100644 index 515cfa9b..00000000 --- a/Timeline/Resources/Helper/DataCacheHelper.resx +++ /dev/null @@ -1,129 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="LogBadIfNoneMatch" xml:space="preserve">
- <value>Header If-None-Match is of bad format.</value>
- </data>
- <data name="LogResultData" xml:space="preserve">
- <value>Cache is invalid and data is returned.</value>
- </data>
- <data name="LogResultNotModified" xml:space="preserve">
- <value>Cache is valid and 304 Not Modified is returned.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Messages.Designer.cs b/Timeline/Resources/Messages.Designer.cs deleted file mode 100644 index bb654ce6..00000000 --- a/Timeline/Resources/Messages.Designer.cs +++ /dev/null @@ -1,396 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Messages {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Messages() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Messages", typeof(Messages).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Body is too big. It can't be bigger than {0}..
- /// </summary>
- internal static string Common_Content_TooBig {
- get {
- return ResourceManager.GetString("Common_Content_TooBig", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Actual body length is bigger than it in header..
- /// </summary>
- internal static string Common_Content_UnmatchedLength_Bigger {
- get {
- return ResourceManager.GetString("Common_Content_UnmatchedLength_Bigger", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Actual body length is smaller than it in header..
- /// </summary>
- internal static string Common_Content_UnmatchedLength_Smaller {
- get {
- return ResourceManager.GetString("Common_Content_UnmatchedLength_Smaller", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You have no permission to do the operation..
- /// </summary>
- internal static string Common_Forbid {
- get {
- return ResourceManager.GetString("Common_Forbid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You are not the resource owner..
- /// </summary>
- internal static string Common_Forbid_NotSelf {
- get {
- return ResourceManager.GetString("Common_Forbid_NotSelf", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header Content-Length is missing or of bad format..
- /// </summary>
- internal static string Common_Header_ContentLength_Missing {
- get {
- return ResourceManager.GetString("Common_Header_ContentLength_Missing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header Content-Length must not be 0..
- /// </summary>
- internal static string Common_Header_ContentLength_Zero {
- get {
- return ResourceManager.GetString("Common_Header_ContentLength_Zero", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header Content-Type is missing..
- /// </summary>
- internal static string Common_Header_ContentType_Missing {
- get {
- return ResourceManager.GetString("Common_Header_ContentType_Missing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Header If-Non-Match is of bad format..
- /// </summary>
- internal static string Common_Header_IfNonMatch_BadFormat {
- get {
- return ResourceManager.GetString("Common_Header_IfNonMatch_BadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Model is of bad format..
- /// </summary>
- internal static string Common_InvalidModel {
- get {
- return ResourceManager.GetString("Common_InvalidModel", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The api endpoint you request is unknown. You might get the wrong api entry..
- /// </summary>
- internal static string Common_UnknownEndpoint {
- get {
- return ResourceManager.GetString("Common_UnknownEndpoint", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown type of post content..
- /// </summary>
- internal static string TimelineController_ContentUnknownType {
- get {
- return ResourceManager.GetString("TimelineController_ContentUnknownType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data field is not a valid base64 string in image content..
- /// </summary>
- internal static string TimelineController_ImageContentDataNotBase64 {
- get {
- return ResourceManager.GetString("TimelineController_ImageContentDataNotBase64", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data field is not a valid image after base64 decoding in image content..
- /// </summary>
- internal static string TimelineController_ImageContentDataNotImage {
- get {
- return ResourceManager.GetString("TimelineController_ImageContentDataNotImage", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data field is required for image content..
- /// </summary>
- internal static string TimelineController_ImageContentDataRequired {
- get {
- return ResourceManager.GetString("TimelineController_ImageContentDataRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user to set as member does not exist..
- /// </summary>
- internal static string TimelineController_MemberPut_NotExist {
- get {
- return ResourceManager.GetString("TimelineController_MemberPut_NotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A timeline with given name already exists..
- /// </summary>
- internal static string TimelineController_NameConflict {
- get {
- return ResourceManager.GetString("TimelineController_NameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline with given name does not exist..
- /// </summary>
- internal static string TimelineController_NotExist {
- get {
- return ResourceManager.GetString("TimelineController_NotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The post of that type has no data..
- /// </summary>
- internal static string TimelineController_PostNoData {
- get {
- return ResourceManager.GetString("TimelineController_PostNoData", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The post to operate on does not exist..
- /// </summary>
- internal static string TimelineController_PostNotExist {
- get {
- return ResourceManager.GetString("TimelineController_PostNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user specified by query param "relate" does not exist..
- /// </summary>
- internal static string TimelineController_QueryRelateNotExist {
- get {
- return ResourceManager.GetString("TimelineController_QueryRelateNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to '{0}' is an unkown visibility in the query parameter 'visibility'. .
- /// </summary>
- internal static string TimelineController_QueryVisibilityUnknown {
- get {
- return ResourceManager.GetString("TimelineController_QueryVisibilityUnknown", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Text field is required for text content..
- /// </summary>
- internal static string TimelineController_TextContentTextRequired {
- get {
- return ResourceManager.GetString("TimelineController_TextContentTextRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Username or password is invalid..
- /// </summary>
- internal static string TokenController_Create_BadCredential {
- get {
- return ResourceManager.GetString("TokenController_Create_BadCredential", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format. It might not be created by the server..
- /// </summary>
- internal static string TokenController_Verify_BadFormat {
- get {
- return ResourceManager.GetString("TokenController_Verify_BadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Token has an old version. User might have update some info..
- /// </summary>
- internal static string TokenController_Verify_OldVersion {
- get {
- return ResourceManager.GetString("TokenController_Verify_OldVersion", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired..
- /// </summary>
- internal static string TokenController_Verify_TimeExpired {
- get {
- return ResourceManager.GetString("TokenController_Verify_TimeExpired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to User does not exist. Administrator might have deleted this user..
- /// </summary>
- internal static string TokenController_Verify_UserNotExist {
- get {
- return ResourceManager.GetString("TokenController_Verify_UserNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image is not a square..
- /// </summary>
- internal static string UserAvatar_BadFormat_BadSize {
- get {
- return ResourceManager.GetString("UserAvatar_BadFormat_BadSize", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image decode failed..
- /// </summary>
- internal static string UserAvatar_BadFormat_CantDecode {
- get {
- return ResourceManager.GetString("UserAvatar_BadFormat_CantDecode", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image format does not match the one in header..
- /// </summary>
- internal static string UserAvatar_BadFormat_UnmatchedFormat {
- get {
- return ResourceManager.GetString("UserAvatar_BadFormat_UnmatchedFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The user to operate on does not exist..
- /// </summary>
- internal static string UserCommon_NotExist {
- get {
- return ResourceManager.GetString("UserCommon_NotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Old password is wrong..
- /// </summary>
- internal static string UserController_ChangePassword_BadOldPassword {
- get {
- return ResourceManager.GetString("UserController_ChangePassword_BadOldPassword", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You can't set permission unless you are administrator..
- /// </summary>
- internal static string UserController_Patch_Forbid_Administrator {
- get {
- return ResourceManager.GetString("UserController_Patch_Forbid_Administrator", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You can't set password unless you are administrator. If you want to change password, use /userop/changepassword ..
- /// </summary>
- internal static string UserController_Patch_Forbid_Password {
- get {
- return ResourceManager.GetString("UserController_Patch_Forbid_Password", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to You can't set username unless you are administrator..
- /// </summary>
- internal static string UserController_Patch_Forbid_Username {
- get {
- return ResourceManager.GetString("UserController_Patch_Forbid_Username", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user with given username already exists..
- /// </summary>
- internal static string UserController_UsernameConflict {
- get {
- return ResourceManager.GetString("UserController_UsernameConflict", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Messages.resx b/Timeline/Resources/Messages.resx deleted file mode 100644 index 2bbf494e..00000000 --- a/Timeline/Resources/Messages.resx +++ /dev/null @@ -1,231 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="Common_Content_TooBig" xml:space="preserve">
- <value>Body is too big. It can't be bigger than {0}.</value>
- </data>
- <data name="Common_Content_UnmatchedLength_Bigger" xml:space="preserve">
- <value>Actual body length is bigger than it in header.</value>
- </data>
- <data name="Common_Content_UnmatchedLength_Smaller" xml:space="preserve">
- <value>Actual body length is smaller than it in header.</value>
- </data>
- <data name="Common_Forbid" xml:space="preserve">
- <value>You have no permission to do the operation.</value>
- </data>
- <data name="Common_Forbid_NotSelf" xml:space="preserve">
- <value>You are not the resource owner.</value>
- </data>
- <data name="Common_Header_ContentLength_Missing" xml:space="preserve">
- <value>Header Content-Length is missing or of bad format.</value>
- </data>
- <data name="Common_Header_ContentLength_Zero" xml:space="preserve">
- <value>Header Content-Length must not be 0.</value>
- </data>
- <data name="Common_Header_ContentType_Missing" xml:space="preserve">
- <value>Header Content-Type is missing.</value>
- </data>
- <data name="Common_Header_IfNonMatch_BadFormat" xml:space="preserve">
- <value>Header If-Non-Match is of bad format.</value>
- </data>
- <data name="Common_InvalidModel" xml:space="preserve">
- <value>Model is of bad format.</value>
- </data>
- <data name="Common_UnknownEndpoint" xml:space="preserve">
- <value>The api endpoint you request is unknown. You might get the wrong api entry.</value>
- </data>
- <data name="TimelineController_ContentUnknownType" xml:space="preserve">
- <value>Unknown type of post content.</value>
- </data>
- <data name="TimelineController_ImageContentDataNotBase64" xml:space="preserve">
- <value>Data field is not a valid base64 string in image content.</value>
- </data>
- <data name="TimelineController_ImageContentDataNotImage" xml:space="preserve">
- <value>Data field is not a valid image after base64 decoding in image content.</value>
- </data>
- <data name="TimelineController_ImageContentDataRequired" xml:space="preserve">
- <value>Data field is required for image content.</value>
- </data>
- <data name="TimelineController_MemberPut_NotExist" xml:space="preserve">
- <value>The user to set as member does not exist.</value>
- </data>
- <data name="TimelineController_NameConflict" xml:space="preserve">
- <value>A timeline with given name already exists.</value>
- </data>
- <data name="TimelineController_NotExist" xml:space="preserve">
- <value>The timeline with given name does not exist.</value>
- </data>
- <data name="TimelineController_PostNoData" xml:space="preserve">
- <value>The post of that type has no data.</value>
- </data>
- <data name="TimelineController_PostNotExist" xml:space="preserve">
- <value>The post to operate on does not exist.</value>
- </data>
- <data name="TimelineController_QueryRelateNotExist" xml:space="preserve">
- <value>The user specified by query param "relate" does not exist.</value>
- </data>
- <data name="TimelineController_QueryVisibilityUnknown" xml:space="preserve">
- <value>'{0}' is an unkown visibility in the query parameter 'visibility'. </value>
- </data>
- <data name="TimelineController_TextContentTextRequired" xml:space="preserve">
- <value>Text field is required for text content.</value>
- </data>
- <data name="TokenController_Create_BadCredential" xml:space="preserve">
- <value>Username or password is invalid.</value>
- </data>
- <data name="TokenController_Verify_BadFormat" xml:space="preserve">
- <value>The token is of bad format. It might not be created by the server.</value>
- </data>
- <data name="TokenController_Verify_OldVersion" xml:space="preserve">
- <value>Token has an old version. User might have update some info.</value>
- </data>
- <data name="TokenController_Verify_TimeExpired" xml:space="preserve">
- <value>The token is expired.</value>
- </data>
- <data name="TokenController_Verify_UserNotExist" xml:space="preserve">
- <value>User does not exist. Administrator might have deleted this user.</value>
- </data>
- <data name="UserAvatar_BadFormat_BadSize" xml:space="preserve">
- <value>Image is not a square.</value>
- </data>
- <data name="UserAvatar_BadFormat_CantDecode" xml:space="preserve">
- <value>Image decode failed.</value>
- </data>
- <data name="UserAvatar_BadFormat_UnmatchedFormat" xml:space="preserve">
- <value>Image format does not match the one in header.</value>
- </data>
- <data name="UserCommon_NotExist" xml:space="preserve">
- <value>The user to operate on does not exist.</value>
- </data>
- <data name="UserController_ChangePassword_BadOldPassword" xml:space="preserve">
- <value>Old password is wrong.</value>
- </data>
- <data name="UserController_Patch_Forbid_Administrator" xml:space="preserve">
- <value>You can't set permission unless you are administrator.</value>
- </data>
- <data name="UserController_Patch_Forbid_Password" xml:space="preserve">
- <value>You can't set password unless you are administrator. If you want to change password, use /userop/changepassword .</value>
- </data>
- <data name="UserController_Patch_Forbid_Username" xml:space="preserve">
- <value>You can't set username unless you are administrator.</value>
- </data>
- <data name="UserController_UsernameConflict" xml:space="preserve">
- <value>A user with given username already exists.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Models/Http/Common.Designer.cs b/Timeline/Resources/Models/Http/Common.Designer.cs deleted file mode 100644 index 5165463e..00000000 --- a/Timeline/Resources/Models/Http/Common.Designer.cs +++ /dev/null @@ -1,99 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Http {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Common {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Common() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Http.Common", typeof(Common).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An existent item is deleted..
- /// </summary>
- internal static string MessageDeleteDelete {
- get {
- return ResourceManager.GetString("MessageDeleteDelete", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The item does not exist, so nothing is changed..
- /// </summary>
- internal static string MessageDeleteNotExist {
- get {
- return ResourceManager.GetString("MessageDeleteNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A new item is created..
- /// </summary>
- internal static string MessagePutCreate {
- get {
- return ResourceManager.GetString("MessagePutCreate", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An existent item is modified..
- /// </summary>
- internal static string MessagePutModify {
- get {
- return ResourceManager.GetString("MessagePutModify", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Http/Common.resx b/Timeline/Resources/Models/Http/Common.resx deleted file mode 100644 index 85ec4d32..00000000 --- a/Timeline/Resources/Models/Http/Common.resx +++ /dev/null @@ -1,132 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageDeleteDelete" xml:space="preserve">
- <value>An existent item is deleted.</value>
- </data>
- <data name="MessageDeleteNotExist" xml:space="preserve">
- <value>The item does not exist, so nothing is changed.</value>
- </data>
- <data name="MessagePutCreate" xml:space="preserve">
- <value>A new item is created.</value>
- </data>
- <data name="MessagePutModify" xml:space="preserve">
- <value>An existent item is modified.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Models/Http/Exception.Designer.cs b/Timeline/Resources/Models/Http/Exception.Designer.cs deleted file mode 100644 index 19f42793..00000000 --- a/Timeline/Resources/Models/Http/Exception.Designer.cs +++ /dev/null @@ -1,81 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Http {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Exception {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Exception() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Http.Exception", typeof(Exception).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to No action context currently, can't fill urls in value resolver..
- /// </summary>
- internal static string ActionContextNull {
- get {
- return ResourceManager.GetString("ActionContextNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown post content type..
- /// </summary>
- internal static string UnknownPostContentType {
- get {
- return ResourceManager.GetString("UnknownPostContentType", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Http/Exception.resx b/Timeline/Resources/Models/Http/Exception.resx deleted file mode 100644 index 3f7bddb6..00000000 --- a/Timeline/Resources/Models/Http/Exception.resx +++ /dev/null @@ -1,126 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ActionContextNull" xml:space="preserve">
- <value>No action context currently, can't fill urls in value resolver.</value>
- </data>
- <data name="UnknownPostContentType" xml:space="preserve">
- <value>Unknown post content type.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Models/Validation/NameValidator.Designer.cs b/Timeline/Resources/Models/Validation/NameValidator.Designer.cs deleted file mode 100644 index 3050049e..00000000 --- a/Timeline/Resources/Models/Validation/NameValidator.Designer.cs +++ /dev/null @@ -1,99 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Validation {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class NameValidator {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal NameValidator() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.NameValidator", typeof(NameValidator).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An empty string is not allowed..
- /// </summary>
- internal static string MessageEmptyString {
- get {
- return ResourceManager.GetString("MessageEmptyString", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Invalid character, only alphabet, digit, underscore and hyphen are allowed..
- /// </summary>
- internal static string MessageInvalidChar {
- get {
- return ResourceManager.GetString("MessageInvalidChar", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Too long, more than 26 characters is not premitted..
- /// </summary>
- internal static string MessageTooLong {
- get {
- return ResourceManager.GetString("MessageTooLong", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Name can't be of the same format of unique id..
- /// </summary>
- internal static string MessageUnqiueId {
- get {
- return ResourceManager.GetString("MessageUnqiueId", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Validation/NameValidator.resx b/Timeline/Resources/Models/Validation/NameValidator.resx deleted file mode 100644 index 5e7e1745..00000000 --- a/Timeline/Resources/Models/Validation/NameValidator.resx +++ /dev/null @@ -1,132 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageEmptyString" xml:space="preserve">
- <value>An empty string is not allowed.</value>
- </data>
- <data name="MessageInvalidChar" xml:space="preserve">
- <value>Invalid character, only alphabet, digit, underscore and hyphen are allowed.</value>
- </data>
- <data name="MessageTooLong" xml:space="preserve">
- <value>Too long, more than 26 characters is not premitted.</value>
- </data>
- <data name="MessageUnqiueId" xml:space="preserve">
- <value>Name can't be of the same format of unique id.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs b/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs deleted file mode 100644 index 522f305a..00000000 --- a/Timeline/Resources/Models/Validation/NicknameValidator.Designer.cs +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Validation {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class NicknameValidator {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal NicknameValidator() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.NicknameValidator", typeof(NicknameValidator).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Nickname is too long..
- /// </summary>
- internal static string MessageTooLong {
- get {
- return ResourceManager.GetString("MessageTooLong", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Validation/NicknameValidator.resx b/Timeline/Resources/Models/Validation/NicknameValidator.resx deleted file mode 100644 index b191b505..00000000 --- a/Timeline/Resources/Models/Validation/NicknameValidator.resx +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="MessageTooLong" xml:space="preserve">
- <value>Nickname is too long.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Models/Validation/Validator.Designer.cs b/Timeline/Resources/Models/Validation/Validator.Designer.cs deleted file mode 100644 index 74d4c169..00000000 --- a/Timeline/Resources/Models/Validation/Validator.Designer.cs +++ /dev/null @@ -1,108 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Models.Validation {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Validator {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Validator() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Models.Validation.Validator", typeof(Validator).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Failed to create a validator instance from default constructor. See inner exception..
- /// </summary>
- internal static string ValidateWithAttributeExceptionCreateFail {
- get {
- return ResourceManager.GetString("ValidateWithAttributeExceptionCreateFail", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Given type is not assignable to IValidator..
- /// </summary>
- internal static string ValidateWithAttributeExceptionNotValidator {
- get {
- return ResourceManager.GetString("ValidateWithAttributeExceptionNotValidator", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Value is not of type {0}..
- /// </summary>
- internal static string ValidatorMessageBadType {
- get {
- return ResourceManager.GetString("ValidatorMessageBadType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Value can't be null..
- /// </summary>
- internal static string ValidatorMessageNull {
- get {
- return ResourceManager.GetString("ValidatorMessageNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Validation succeeded..
- /// </summary>
- internal static string ValidatorMessageSuccess {
- get {
- return ResourceManager.GetString("ValidatorMessageSuccess", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Models/Validation/Validator.resx b/Timeline/Resources/Models/Validation/Validator.resx deleted file mode 100644 index 8317e3eb..00000000 --- a/Timeline/Resources/Models/Validation/Validator.resx +++ /dev/null @@ -1,135 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ValidateWithAttributeExceptionCreateFail" xml:space="preserve">
- <value>Failed to create a validator instance from default constructor. See inner exception.</value>
- </data>
- <data name="ValidateWithAttributeExceptionNotValidator" xml:space="preserve">
- <value>Given type is not assignable to IValidator.</value>
- </data>
- <data name="ValidatorMessageBadType" xml:space="preserve">
- <value>Value is not of type {0}.</value>
- </data>
- <data name="ValidatorMessageNull" xml:space="preserve">
- <value>Value can't be null.</value>
- </data>
- <data name="ValidatorMessageSuccess" xml:space="preserve">
- <value>Validation succeeded.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/DataManager.Designer.cs b/Timeline/Resources/Services/DataManager.Designer.cs deleted file mode 100644 index 0872059a..00000000 --- a/Timeline/Resources/Services/DataManager.Designer.cs +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class DataManager {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal DataManager() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.DataManager", typeof(DataManager).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Entry with given tag does not exist..
- /// </summary>
- internal static string ExceptionEntryNotExist {
- get {
- return ResourceManager.GetString("ExceptionEntryNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/DataManager.resx b/Timeline/Resources/Services/DataManager.resx deleted file mode 100644 index 688e0e96..00000000 --- a/Timeline/Resources/Services/DataManager.resx +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionEntryNotExist" xml:space="preserve">
- <value>Entry with given tag does not exist.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/Exception.Designer.cs b/Timeline/Resources/Services/Exception.Designer.cs deleted file mode 100644 index 21ca7b86..00000000 --- a/Timeline/Resources/Services/Exception.Designer.cs +++ /dev/null @@ -1,234 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Exception {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Exception() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.Exception", typeof(Exception).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The password is wrong..
- /// </summary>
- internal static string BadPasswordException {
- get {
- return ResourceManager.GetString("BadPasswordException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The hashes password is of bad format. It might not be created by server..
- /// </summary>
- internal static string HashedPasswordBadFromatException {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Not of valid base64 format. See inner exception..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotBase64 {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotBase64", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Decoded hashed password is of length 0..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotLength0 {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotLength0", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to See inner exception..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotOthers {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotOthers", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Salt length < 128 bits..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotSaltTooShort {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotSaltTooShort", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Subkey length < 128 bits..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotSubkeyTooShort {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotSubkeyTooShort", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown format marker..
- /// </summary>
- internal static string HashedPasswordBadFromatExceptionNotUnknownMarker {
- get {
- return ResourceManager.GetString("HashedPasswordBadFromatExceptionNotUnknownMarker", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token didn't pass verification because {0}..
- /// </summary>
- internal static string JwtUserTokenBadFormatException {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to id claim is not a number.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionIdBadFormat {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionIdBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to id claim does not exist.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionIdMissing {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionIdMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to other error, see inner exception for information.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionOthers {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionOthers", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to unknown error.
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionUnknown {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionUnknown", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to version claim is not a number..
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionVersionBadFormat {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionVersionBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to version claim does not exist..
- /// </summary>
- internal static string JwtUserTokenBadFormatExceptionVersionMissing {
- get {
- return ResourceManager.GetString("JwtUserTokenBadFormatExceptionVersionMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Password is of bad format..
- /// </summary>
- internal static string PasswordBadFormatException {
- get {
- return ResourceManager.GetString("PasswordBadFormatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad format, which means it may not be created by the server..
- /// </summary>
- internal static string UserTokenBadFormatException {
- get {
- return ResourceManager.GetString("UserTokenBadFormatException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is of bad version..
- /// </summary>
- internal static string UserTokenBadVersionException {
- get {
- return ResourceManager.GetString("UserTokenBadVersionException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The token is expired because its expiration time has passed..
- /// </summary>
- internal static string UserTokenTimeExpireException {
- get {
- return ResourceManager.GetString("UserTokenTimeExpireException", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/Exception.resx b/Timeline/Resources/Services/Exception.resx deleted file mode 100644 index c31ed7c7..00000000 --- a/Timeline/Resources/Services/Exception.resx +++ /dev/null @@ -1,177 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="BadPasswordException" xml:space="preserve">
- <value>The password is wrong.</value>
- </data>
- <data name="HashedPasswordBadFromatException" xml:space="preserve">
- <value>The hashes password is of bad format. It might not be created by server.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotBase64" xml:space="preserve">
- <value>Not of valid base64 format. See inner exception.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotLength0" xml:space="preserve">
- <value>Decoded hashed password is of length 0.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotOthers" xml:space="preserve">
- <value>See inner exception.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotSaltTooShort" xml:space="preserve">
- <value>Salt length < 128 bits.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotSubkeyTooShort" xml:space="preserve">
- <value>Subkey length < 128 bits.</value>
- </data>
- <data name="HashedPasswordBadFromatExceptionNotUnknownMarker" xml:space="preserve">
- <value>Unknown format marker.</value>
- </data>
- <data name="JwtUserTokenBadFormatException" xml:space="preserve">
- <value>The token didn't pass verification because {0}.</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionIdBadFormat" xml:space="preserve">
- <value>id claim is not a number</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionIdMissing" xml:space="preserve">
- <value>id claim does not exist</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionOthers" xml:space="preserve">
- <value>other error, see inner exception for information</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionUnknown" xml:space="preserve">
- <value>unknown error</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionVersionBadFormat" xml:space="preserve">
- <value>version claim is not a number.</value>
- </data>
- <data name="JwtUserTokenBadFormatExceptionVersionMissing" xml:space="preserve">
- <value>version claim does not exist.</value>
- </data>
- <data name="PasswordBadFormatException" xml:space="preserve">
- <value>Password is of bad format.</value>
- </data>
- <data name="UserTokenBadFormatException" xml:space="preserve">
- <value>The token is of bad format, which means it may not be created by the server.</value>
- </data>
- <data name="UserTokenBadVersionException" xml:space="preserve">
- <value>The token is of bad version.</value>
- </data>
- <data name="UserTokenTimeExpireException" xml:space="preserve">
- <value>The token is expired because its expiration time has passed.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/Exceptions.Designer.cs b/Timeline/Resources/Services/Exceptions.Designer.cs deleted file mode 100644 index 1dbe11c9..00000000 --- a/Timeline/Resources/Services/Exceptions.Designer.cs +++ /dev/null @@ -1,189 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Exceptions {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Exceptions() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.Exceptions", typeof(Exceptions).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A entity of type "{0}" already exists..
- /// </summary>
- internal static string EntityAlreadyExistError {
- get {
- return ResourceManager.GetString("EntityAlreadyExistError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The entity already exists..
- /// </summary>
- internal static string EntityAlreadyExistErrorDefault {
- get {
- return ResourceManager.GetString("EntityAlreadyExistErrorDefault", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The required entity of type "{0}" does not exist..
- /// </summary>
- internal static string EntityNotExistError {
- get {
- return ResourceManager.GetString("EntityNotExistError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The required entity does not exist..
- /// </summary>
- internal static string EntityNotExistErrorDefault {
- get {
- return ResourceManager.GetString("EntityNotExistErrorDefault", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image is in valid because {0}..
- /// </summary>
- internal static string ImageException {
- get {
- return ResourceManager.GetString("ImageException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to image is not of required size.
- /// </summary>
- internal static string ImageExceptionBadSize {
- get {
- return ResourceManager.GetString("ImageExceptionBadSize", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to failed to decode image, see inner exception.
- /// </summary>
- internal static string ImageExceptionCantDecode {
- get {
- return ResourceManager.GetString("ImageExceptionCantDecode", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to unknown error.
- /// </summary>
- internal static string ImageExceptionUnknownError {
- get {
- return ResourceManager.GetString("ImageExceptionUnknownError", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to image's actual mime type is not the specified one.
- /// </summary>
- internal static string ImageExceptionUnmatchedFormat {
- get {
- return ResourceManager.GetString("ImageExceptionUnmatchedFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline has no data..
- /// </summary>
- internal static string TimelineNoDataException {
- get {
- return ResourceManager.GetString("TimelineNoDataException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request timeline name is "{0}". If this is a personal timeline whose name starts with '@', it means the user does not exist and inner exception should be a UserNotExistException..
- /// </summary>
- internal static string TimelineNotExistException {
- get {
- return ResourceManager.GetString("TimelineNotExistException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request timeline name is "{0}". Request timeline post id is "{1}"..
- /// </summary>
- internal static string TimelinePostNotExistException {
- get {
- return ResourceManager.GetString("TimelinePostNotExistException", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request timeline name is "{0}". Request timeline post id is "{1}". The post does not exist because it is deleted..
- /// </summary>
- internal static string TimelinePostNotExistExceptionDeleted {
- get {
- return ResourceManager.GetString("TimelinePostNotExistExceptionDeleted", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Request username is "{0}". Request id is "{1}"..
- /// </summary>
- internal static string UserNotExistException {
- get {
- return ResourceManager.GetString("UserNotExistException", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/Exceptions.resx b/Timeline/Resources/Services/Exceptions.resx deleted file mode 100644 index e9595caa..00000000 --- a/Timeline/Resources/Services/Exceptions.resx +++ /dev/null @@ -1,142 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 1.3
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">1.3</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1">this is my long string</data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- [base64 mime encoded serialized .NET Framework object]
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- [base64 mime encoded string representing a byte array form of the .NET Framework object]
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>1.3</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="EntityNotExistError" xml:space="preserve">
- <value>The required entity of type "{0}" does not exist.</value>
- </data>
- <data name="EntityAlreadyExistErrorDefault" xml:space="preserve">
- <value>The entity already exists.</value>
- </data>
- <data name="EntityAlreadyExistError" xml:space="preserve">
- <value>A entity of type "{0}" already exists.</value>
- </data>
- <data name="EntityNotExistErrorDefault" xml:space="preserve">
- <value>The required entity does not exist.</value>
- </data>
- <data name="TimelineNotExistException" xml:space="preserve">
- <value>Request timeline name is "{0}". If this is a personal timeline whose name starts with '@', it means the user does not exist and inner exception should be a UserNotExistException.</value>
- </data>
- <data name="TimelinePostNotExistException" xml:space="preserve">
- <value>Request timeline name is "{0}". Request timeline post id is "{1}".</value>
- </data>
- <data name="UserNotExistException" xml:space="preserve">
- <value>Request username is "{0}". Request id is "{1}".</value>
- </data>
- <data name="TimelineNoDataException" xml:space="preserve">
- <value>The timeline has no data.</value>
- </data>
- <data name="ImageException" xml:space="preserve">
- <value>Image is in valid because {0}.</value>
- </data>
- <data name="ImageExceptionBadSize" xml:space="preserve">
- <value>image is not of required size</value>
- </data>
- <data name="ImageExceptionCantDecode" xml:space="preserve">
- <value>failed to decode image, see inner exception</value>
- </data>
- <data name="ImageExceptionUnknownError" xml:space="preserve">
- <value>unknown error</value>
- </data>
- <data name="ImageExceptionUnmatchedFormat" xml:space="preserve">
- <value>image's actual mime type is not the specified one</value>
- </data>
- <data name="TimelinePostNotExistExceptionDeleted" xml:space="preserve">
- <value>Request timeline name is "{0}". Request timeline post id is "{1}". The post does not exist because it is deleted.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/TimelineService.Designer.cs b/Timeline/Resources/Services/TimelineService.Designer.cs deleted file mode 100644 index e16c1337..00000000 --- a/Timeline/Resources/Services/TimelineService.Designer.cs +++ /dev/null @@ -1,144 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class TimelineService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal TimelineService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.TimelineService", typeof(TimelineService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The number {0} username is invalid..
- /// </summary>
- internal static string ExceptionChangeMemberUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionChangeMemberUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unknown post content type "{0}" is saved in database..
- /// </summary>
- internal static string ExceptionDatabaseUnknownContentType {
- get {
- return ResourceManager.GetString("ExceptionDatabaseUnknownContentType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The owner username of personal timeline is of bad format..
- /// </summary>
- internal static string ExceptionFindTimelineUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionFindTimelineUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The data entry of the tag of the image post does not exist..
- /// </summary>
- internal static string ExceptionGetDataDataEntryNotExist {
- get {
- return ResourceManager.GetString("ExceptionGetDataDataEntryNotExist", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Can't get data of a non-image post..
- /// </summary>
- internal static string ExceptionGetDataNonImagePost {
- get {
- return ResourceManager.GetString("ExceptionGetDataNonImagePost", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The post has been deleted because content of entity is null..
- /// </summary>
- internal static string ExceptionPostDeleted {
- get {
- return ResourceManager.GetString("ExceptionPostDeleted", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline name is of bad format..
- /// </summary>
- internal static string ExceptionTimelineNameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionTimelineNameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The timeline with given name already exists..
- /// </summary>
- internal static string ExceptionTimelineNameConflict {
- get {
- return ResourceManager.GetString("ExceptionTimelineNameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Image format type of the post does not exist in column "extra_content". Normally this couldn't be possible because it should be saved when post was created. However, we now re-detect the format and save it..
- /// </summary>
- internal static string LogGetDataNoFormat {
- get {
- return ResourceManager.GetString("LogGetDataNoFormat", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/TimelineService.resx b/Timeline/Resources/Services/TimelineService.resx deleted file mode 100644 index 9314f51b..00000000 --- a/Timeline/Resources/Services/TimelineService.resx +++ /dev/null @@ -1,147 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionChangeMemberUsernameBadFormat" xml:space="preserve">
- <value>The number {0} username is invalid.</value>
- </data>
- <data name="ExceptionDatabaseUnknownContentType" xml:space="preserve">
- <value>Unknown post content type "{0}" is saved in database.</value>
- </data>
- <data name="ExceptionFindTimelineUsernameBadFormat" xml:space="preserve">
- <value>The owner username of personal timeline is of bad format.</value>
- </data>
- <data name="ExceptionGetDataDataEntryNotExist" xml:space="preserve">
- <value>The data entry of the tag of the image post does not exist.</value>
- </data>
- <data name="ExceptionGetDataNonImagePost" xml:space="preserve">
- <value>Can't get data of a non-image post.</value>
- </data>
- <data name="ExceptionTimelineNameBadFormat" xml:space="preserve">
- <value>The timeline name is of bad format.</value>
- </data>
- <data name="ExceptionTimelineNameConflict" xml:space="preserve">
- <value>The timeline with given name already exists.</value>
- </data>
- <data name="LogGetDataNoFormat" xml:space="preserve">
- <value>Image format type of the post does not exist in column "extra_content". Normally this couldn't be possible because it should be saved when post was created. However, we now re-detect the format and save it.</value>
- </data>
- <data name="ExceptionPostDeleted" xml:space="preserve">
- <value>The post has been deleted because content of entity is null.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/UserAvatarService.Designer.cs b/Timeline/Resources/Services/UserAvatarService.Designer.cs deleted file mode 100644 index c72d4215..00000000 --- a/Timeline/Resources/Services/UserAvatarService.Designer.cs +++ /dev/null @@ -1,108 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserAvatarService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserAvatarService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserAvatarService", typeof(UserAvatarService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Data of avatar is null..
- /// </summary>
- internal static string ExceptionAvatarDataNull {
- get {
- return ResourceManager.GetString("ExceptionAvatarDataNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Type of avatar is null or empty..
- /// </summary>
- internal static string ExceptionAvatarTypeNullOrEmpty {
- get {
- return ResourceManager.GetString("ExceptionAvatarTypeNullOrEmpty", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Database corupted! One of type and data of a avatar is null but the other is not..
- /// </summary>
- internal static string ExceptionDatabaseCorruptedDataAndTypeNotSame {
- get {
- return ResourceManager.GetString("ExceptionDatabaseCorruptedDataAndTypeNotSame", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Created an entry in user_avatars..
- /// </summary>
- internal static string LogCreateEntity {
- get {
- return ResourceManager.GetString("LogCreateEntity", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Updated an entry in user_avatars..
- /// </summary>
- internal static string LogUpdateEntity {
- get {
- return ResourceManager.GetString("LogUpdateEntity", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserAvatarService.resx b/Timeline/Resources/Services/UserAvatarService.resx deleted file mode 100644 index da9d7203..00000000 --- a/Timeline/Resources/Services/UserAvatarService.resx +++ /dev/null @@ -1,135 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionAvatarDataNull" xml:space="preserve">
- <value>Data of avatar is null.</value>
- </data>
- <data name="ExceptionAvatarTypeNullOrEmpty" xml:space="preserve">
- <value>Type of avatar is null or empty.</value>
- </data>
- <data name="ExceptionDatabaseCorruptedDataAndTypeNotSame" xml:space="preserve">
- <value>Database corupted! One of type and data of a avatar is null but the other is not.</value>
- </data>
- <data name="LogCreateEntity" xml:space="preserve">
- <value>Created an entry in user_avatars.</value>
- </data>
- <data name="LogUpdateEntity" xml:space="preserve">
- <value>Updated an entry in user_avatars.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/UserService.Designer.cs b/Timeline/Resources/Services/UserService.Designer.cs deleted file mode 100644 index cdf7f390..00000000 --- a/Timeline/Resources/Services/UserService.Designer.cs +++ /dev/null @@ -1,162 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserService", typeof(UserService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to New username is of bad format..
- /// </summary>
- internal static string ExceptionNewUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionNewUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Nickname is of bad format, because {}..
- /// </summary>
- internal static string ExceptionNicknameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionNicknameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Old username is of bad format..
- /// </summary>
- internal static string ExceptionOldUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionOldUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Password can't be empty..
- /// </summary>
- internal static string ExceptionPasswordEmpty {
- get {
- return ResourceManager.GetString("ExceptionPasswordEmpty", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Password can't be null..
- /// </summary>
- internal static string ExceptionPasswordNull {
- get {
- return ResourceManager.GetString("ExceptionPasswordNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Username is of bad format, because {}..
- /// </summary>
- internal static string ExceptionUsernameBadFormat {
- get {
- return ResourceManager.GetString("ExceptionUsernameBadFormat", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user with given username already exists..
- /// </summary>
- internal static string ExceptionUsernameConflict {
- get {
- return ResourceManager.GetString("ExceptionUsernameConflict", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Username can't be null..
- /// </summary>
- internal static string ExceptionUsernameNull {
- get {
- return ResourceManager.GetString("ExceptionUsernameNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A new user entry is added to the database..
- /// </summary>
- internal static string LogDatabaseCreate {
- get {
- return ResourceManager.GetString("LogDatabaseCreate", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user entry is removed from the database..
- /// </summary>
- internal static string LogDatabaseRemove {
- get {
- return ResourceManager.GetString("LogDatabaseRemove", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A user entry is updated to the database..
- /// </summary>
- internal static string LogDatabaseUpdate {
- get {
- return ResourceManager.GetString("LogDatabaseUpdate", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserService.resx b/Timeline/Resources/Services/UserService.resx deleted file mode 100644 index 09bd4abb..00000000 --- a/Timeline/Resources/Services/UserService.resx +++ /dev/null @@ -1,153 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ExceptionNewUsernameBadFormat" xml:space="preserve">
- <value>New username is of bad format.</value>
- </data>
- <data name="ExceptionNicknameBadFormat" xml:space="preserve">
- <value>Nickname is of bad format, because {}.</value>
- </data>
- <data name="ExceptionOldUsernameBadFormat" xml:space="preserve">
- <value>Old username is of bad format.</value>
- </data>
- <data name="ExceptionPasswordEmpty" xml:space="preserve">
- <value>Password can't be empty.</value>
- </data>
- <data name="ExceptionPasswordNull" xml:space="preserve">
- <value>Password can't be null.</value>
- </data>
- <data name="ExceptionUsernameBadFormat" xml:space="preserve">
- <value>Username is of bad format, because {}.</value>
- </data>
- <data name="ExceptionUsernameConflict" xml:space="preserve">
- <value>A user with given username already exists.</value>
- </data>
- <data name="ExceptionUsernameNull" xml:space="preserve">
- <value>Username can't be null.</value>
- </data>
- <data name="LogDatabaseCreate" xml:space="preserve">
- <value>A new user entry is added to the database.</value>
- </data>
- <data name="LogDatabaseRemove" xml:space="preserve">
- <value>A user entry is removed from the database.</value>
- </data>
- <data name="LogDatabaseUpdate" xml:space="preserve">
- <value>A user entry is updated to the database.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Resources/Services/UserTokenService.Designer.cs b/Timeline/Resources/Services/UserTokenService.Designer.cs deleted file mode 100644 index 3c3c7e41..00000000 --- a/Timeline/Resources/Services/UserTokenService.Designer.cs +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Timeline.Resources.Services {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class UserTokenService {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal UserTokenService() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Timeline.Resources.Services.UserTokenService", typeof(UserTokenService).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Jwt token key is not set in database. Maybe you forget to migrate the database..
- /// </summary>
- internal static string JwtKeyNotExist {
- get {
- return ResourceManager.GetString("JwtKeyNotExist", resourceCulture);
- }
- }
- }
-}
diff --git a/Timeline/Resources/Services/UserTokenService.resx b/Timeline/Resources/Services/UserTokenService.resx deleted file mode 100644 index 1ce78427..00000000 --- a/Timeline/Resources/Services/UserTokenService.resx +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="JwtKeyNotExist" xml:space="preserve">
- <value>Jwt token key is not set in database. Maybe you forget to migrate the database.</value>
- </data>
-</root>
\ No newline at end of file diff --git a/Timeline/Routes/ApiRoutePrefixConvention.cs b/Timeline/Routes/ApiRoutePrefixConvention.cs deleted file mode 100644 index ca38a0d9..00000000 --- a/Timeline/Routes/ApiRoutePrefixConvention.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.AspNetCore.Mvc.Routing;
-using System.Linq;
-
-namespace Timeline.Routes
-{
- public static class MvcOptionsExtensions
- {
- public static void UseApiRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
- {
- opts.Conventions.Add(new ApiRoutePrefixConvention(routeAttribute));
- }
-
- public static void UseApiRoutePrefix(this MvcOptions opts, string prefix)
- {
- opts.UseApiRoutePrefix(new RouteAttribute(prefix));
- }
- }
-
- public class ApiRoutePrefixConvention : IApplicationModelConvention
- {
- private readonly AttributeRouteModel _routePrefix;
-
- public ApiRoutePrefixConvention(IRouteTemplateProvider route)
- {
- _routePrefix = new AttributeRouteModel(route);
- }
-
- public void Apply(ApplicationModel application)
- {
- foreach (var selector in application.Controllers.Where(c => c.Filters.Any(f => f is IApiBehaviorMetadata)).SelectMany(c => c.Selectors))
- {
- if (selector.AttributeRouteModel != null)
- {
- selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_routePrefix, selector.AttributeRouteModel);
- }
- else
- {
- selector.AttributeRouteModel = _routePrefix;
- }
- }
- }
- }
-}
diff --git a/Timeline/Routes/UnknownEndpointMiddleware.cs b/Timeline/Routes/UnknownEndpointMiddleware.cs deleted file mode 100644 index 25ec563c..00000000 --- a/Timeline/Routes/UnknownEndpointMiddleware.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using System;
-using System.Net.Mime;
-using System.Text.Json;
-using Timeline.Models.Http;
-
-namespace Timeline.Routes
-{
- public static class UnknownEndpointMiddleware
- {
- public static void Attach(IApplicationBuilder app)
- {
- app.Use(async (context, next) =>
- {
- if (context.GetEndpoint() != null)
- {
- await next();
- return;
- }
-
- if (context.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase))
- {
- context.Response.StatusCode = StatusCodes.Status400BadRequest;
- context.Response.ContentType = MediaTypeNames.Application.Json;
-
- var body = JsonSerializer.SerializeToUtf8Bytes(ErrorResponse.Common.UnknownEndpoint(), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
-
- context.Response.ContentLength = body.Length;
- await context.Response.Body.WriteAsync(body);
- await context.Response.CompleteAsync();
- return;
- }
-
- await next();
- });
- }
- }
-}
diff --git a/Timeline/Services/BadPasswordException.cs b/Timeline/Services/BadPasswordException.cs deleted file mode 100644 index f609371d..00000000 --- a/Timeline/Services/BadPasswordException.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System;
-using Timeline.Helpers;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class BadPasswordException : Exception
- {
- public BadPasswordException() : base(Resources.Services.Exception.BadPasswordException) { }
- public BadPasswordException(string message, Exception inner) : base(message, inner) { }
-
- public BadPasswordException(string badPassword)
- : base(Log.Format(Resources.Services.Exception.BadPasswordException, ("Bad Password", badPassword)))
- {
- Password = badPassword;
- }
-
- protected BadPasswordException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- /// <summary>
- /// The wrong password.
- /// </summary>
- public string? Password { get; set; }
- }
-}
diff --git a/Timeline/Services/Clock.cs b/Timeline/Services/Clock.cs deleted file mode 100644 index 4395edcd..00000000 --- a/Timeline/Services/Clock.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// Convenient for unit test.
- /// </summary>
- public interface IClock
- {
- /// <summary>
- /// Get current time.
- /// </summary>
- /// <returns>Current time.</returns>
- DateTime GetCurrentTime();
- }
-
- public class Clock : IClock
- {
- public Clock()
- {
-
- }
-
- public DateTime GetCurrentTime()
- {
- return DateTime.UtcNow;
- }
- }
-}
diff --git a/Timeline/Services/DataManager.cs b/Timeline/Services/DataManager.cs deleted file mode 100644 index d447b0d5..00000000 --- a/Timeline/Services/DataManager.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Microsoft.EntityFrameworkCore;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// A data manager controlling data.
- /// </summary>
- /// <remarks>
- /// Identical data will be saved as one copy and return the same tag.
- /// Every data has a ref count. When data is retained, ref count increase.
- /// When data is freed, ref count decease. If ref count is decreased
- /// to 0, the data entry will be destroyed and no longer occupy space.
- /// </remarks>
- public interface IDataManager
- {
- /// <summary>
- /// Saves the data to a new entry if it does not exist,
- /// increases its ref count and returns a tag to the entry.
- /// </summary>
- /// <param name="data">The data. Can't be null.</param>
- /// <returns>The tag of the created entry.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="data"/> is null.</exception>
- public Task<string> RetainEntry(byte[] data);
-
- /// <summary>
- /// Decrease the the ref count of the entry.
- /// Remove it if ref count is zero.
- /// </summary>
- /// <param name="tag">The tag of the entry.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tag"/> is null.</exception>
- /// <remarks>
- /// It's no-op if entry with tag does not exist.
- /// </remarks>
- public Task FreeEntry(string tag);
-
- /// <summary>
- /// Retrieve the entry with given tag.
- /// </summary>
- /// <param name="tag">The tag of the entry.</param>
- /// <returns>The data of the entry.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tag"/> is null.</exception>
- /// <exception cref="InvalidOperationException">Thrown when entry with given tag does not exist.</exception>
- public Task<byte[]> GetEntry(string tag);
- }
-
- public class DataManager : IDataManager
- {
- private readonly DatabaseContext _database;
- private readonly IETagGenerator _eTagGenerator;
-
- public DataManager(DatabaseContext database, IETagGenerator eTagGenerator)
- {
- _database = database;
- _eTagGenerator = eTagGenerator;
- }
-
- public async Task<string> RetainEntry(byte[] data)
- {
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- var tag = await _eTagGenerator.Generate(data);
-
- var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync();
-
- if (entity == null)
- {
- entity = new DataEntity
- {
- Tag = tag,
- Data = data,
- Ref = 1
- };
- _database.Data.Add(entity);
- }
- else
- {
- entity.Ref += 1;
- }
- await _database.SaveChangesAsync();
- return tag;
- }
-
- public async Task FreeEntry(string tag)
- {
- if (tag == null)
- throw new ArgumentNullException(nameof(tag));
-
- var entity = await _database.Data.Where(d => d.Tag == tag).SingleOrDefaultAsync();
-
- if (entity != null)
- {
- if (entity.Ref == 1)
- {
- _database.Data.Remove(entity);
- }
- else
- {
- entity.Ref -= 1;
- }
- await _database.SaveChangesAsync();
- }
- }
-
- public async Task<byte[]> GetEntry(string tag)
- {
- if (tag == null)
- throw new ArgumentNullException(nameof(tag));
-
- var entity = await _database.Data.Where(d => d.Tag == tag).Select(d => new { d.Data }).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new InvalidOperationException(Resources.Services.DataManager.ExceptionEntryNotExist);
-
- return entity.Data;
- }
- }
-}
diff --git a/Timeline/Services/DatabaseBackupService.cs b/Timeline/Services/DatabaseBackupService.cs deleted file mode 100644 index a76b2a0d..00000000 --- a/Timeline/Services/DatabaseBackupService.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Globalization;
-using System.IO;
-
-namespace Timeline.Services
-{
- public interface IDatabaseBackupService
- {
- void BackupNow();
- }
-
- public class DatabaseBackupService : IDatabaseBackupService
- {
- private readonly IPathProvider _pathProvider;
- private readonly IClock _clock;
-
- public DatabaseBackupService(IPathProvider pathProvider, IClock clock)
- {
- _pathProvider = pathProvider;
- _clock = clock;
- }
-
- public void BackupNow()
- {
- var databasePath = _pathProvider.GetDatabaseFilePath();
- if (File.Exists(databasePath))
- {
- var backupDirPath = _pathProvider.GetDatabaseBackupDirectory();
- Directory.CreateDirectory(backupDirPath);
- var fileName = _clock.GetCurrentTime().ToString("yyyy-MM-ddTHH-mm-ss", CultureInfo.InvariantCulture);
- var path = Path.Combine(backupDirPath, fileName);
- File.Copy(databasePath, path);
- }
- }
- }
-}
diff --git a/Timeline/Services/DatabaseCorruptedException.cs b/Timeline/Services/DatabaseCorruptedException.cs deleted file mode 100644 index 9988e0ad..00000000 --- a/Timeline/Services/DatabaseCorruptedException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class DatabaseCorruptedException : Exception
- {
- public DatabaseCorruptedException() { }
- public DatabaseCorruptedException(string message) : base(message) { }
- public DatabaseCorruptedException(string message, Exception inner) : base(message, inner) { }
- protected DatabaseCorruptedException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/Timeline/Services/ETagGenerator.cs b/Timeline/Services/ETagGenerator.cs deleted file mode 100644 index 4493e903..00000000 --- a/Timeline/Services/ETagGenerator.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System;
-using System.Security.Cryptography;
-using System.Threading.Tasks;
-
-namespace Timeline.Services
-{
- public interface IETagGenerator
- {
- /// <summary>
- /// Generate a etag for given source.
- /// </summary>
- /// <param name="source">The source data.</param>
- /// <returns>The generated etag.</returns>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="source"/> is null.</exception>
- Task<string> Generate(byte[] source);
- }
-
- public sealed class ETagGenerator : IETagGenerator, IDisposable
- {
- private readonly SHA1 _sha1;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA5350:Do Not Use Weak Cryptographic Algorithms", Justification = "Sha1 is enough ??? I don't know.")]
- public ETagGenerator()
- {
- _sha1 = SHA1.Create();
- }
-
- public Task<string> Generate(byte[] source)
- {
- if (source == null)
- throw new ArgumentNullException(nameof(source));
-
- return Task.Run(() => Convert.ToBase64String(_sha1.ComputeHash(source)));
- }
-
- private bool _disposed; // To detect redundant calls
-
- public void Dispose()
- {
- if (_disposed) return;
- _sha1.Dispose();
- _disposed = true;
- }
- }
-}
diff --git a/Timeline/Services/EntityNames.cs b/Timeline/Services/EntityNames.cs deleted file mode 100644 index 0ce1de3b..00000000 --- a/Timeline/Services/EntityNames.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace Timeline.Services
-{
- public static class EntityNames
- {
- public const string User = "User";
- public const string Timeline = "Timeline";
- public const string TimelinePost = "TimelinePost";
- }
-}
diff --git a/Timeline/Services/Exceptions/EntityAlreadyExistError.cs b/Timeline/Services/Exceptions/EntityAlreadyExistError.cs deleted file mode 100644 index 7db2e860..00000000 --- a/Timeline/Services/Exceptions/EntityAlreadyExistError.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System;
-using System.Globalization;
-using System.Text;
-
-namespace Timeline.Services.Exceptions
-{
- /// <summary>
- /// Thrown when an entity is already exists.
- /// </summary>
- /// <remarks>
- /// For example, want to create a timeline but a timeline with the same name already exists.
- /// </remarks>
- [Serializable]
- public class EntityAlreadyExistException : Exception
- {
- private readonly string? _entityName;
-
- public EntityAlreadyExistException() : this(null, null, null, null) { }
-
- public EntityAlreadyExistException(string? entityName) : this(entityName, null) { }
-
- public EntityAlreadyExistException(string? entityName, Exception? inner) : this(entityName, null, null, null, inner) { }
-
- public EntityAlreadyExistException(string? entityName, object? entity = null) : this(entityName, null, entity, null, null) { }
- public EntityAlreadyExistException(Type? entityType, object? entity = null) : this(null, entityType, entity, null, null) { }
- public EntityAlreadyExistException(string? entityName, Type? entityType, object? entity = null, string? message = null, Exception? inner = null) : base(MakeMessage(entityName, entityType, message), inner)
- {
- _entityName = entityName;
- EntityType = entityType;
- Entity = entity;
- }
-
- private static string MakeMessage(string? entityName, Type? entityType, string? message)
- {
- string? name = entityName ?? (entityType?.Name);
-
- var result = new StringBuilder();
-
- if (name == null)
- result.Append(Resources.Services.Exceptions.EntityAlreadyExistErrorDefault);
- else
- result.AppendFormat(CultureInfo.InvariantCulture, Resources.Services.Exceptions.EntityAlreadyExistError, name);
-
- if (message != null)
- {
- result.Append(' ');
- result.Append(message);
- }
-
- return result.ToString();
- }
-
- protected EntityAlreadyExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? EntityName => _entityName ?? (EntityType?.Name);
-
- public Type? EntityType { get; }
-
- public object? Entity { get; }
- }
-}
diff --git a/Timeline/Services/Exceptions/EntityNotExistError.cs b/Timeline/Services/Exceptions/EntityNotExistError.cs deleted file mode 100644 index e79496d3..00000000 --- a/Timeline/Services/Exceptions/EntityNotExistError.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System;
-using System.Globalization;
-using System.Text;
-
-namespace Timeline.Services.Exceptions
-{
- /// <summary>
- /// Thrown when you want to get an entity that does not exist.
- /// </summary>
- /// <example>
- /// For example, you want to get a timeline with given name but it does not exist.
- /// </example>
- [Serializable]
- public class EntityNotExistException : Exception
- {
- public EntityNotExistException() : this(null, null, null, null) { }
- public EntityNotExistException(string? entityName) : this(entityName, null, null, null) { }
- public EntityNotExistException(Type? entityType) : this(null, entityType, null, null) { }
- public EntityNotExistException(string? entityName, Exception? inner) : this(entityName, null, null, inner) { }
- public EntityNotExistException(Type? entityType, Exception? inner) : this(null, entityType, null, inner) { }
- public EntityNotExistException(string? entityName, Type? entityType, string? message = null, Exception? inner = null) : base(MakeMessage(entityName, entityType, message), inner)
- {
- EntityName = entityName;
- EntityType = entityType;
- }
-
- private static string MakeMessage(string? entityName, Type? entityType, string? message)
- {
- string? name = entityName ?? (entityType?.Name);
-
- var result = new StringBuilder();
-
- if (name == null)
- result.Append(Resources.Services.Exceptions.EntityNotExistErrorDefault);
- else
- result.AppendFormat(CultureInfo.InvariantCulture, Resources.Services.Exceptions.EntityNotExistError, name);
-
- if (message != null)
- {
- result.Append(' ');
- result.Append(message);
- }
-
- return result.ToString();
- }
-
- protected EntityNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? EntityName { get; }
-
- public Type? EntityType { get; }
- }
-}
diff --git a/Timeline/Services/Exceptions/ExceptionMessageHelper.cs b/Timeline/Services/Exceptions/ExceptionMessageHelper.cs deleted file mode 100644 index be3c42a4..00000000 --- a/Timeline/Services/Exceptions/ExceptionMessageHelper.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Timeline.Services.Exceptions
-{
- public static class ExceptionMessageHelper
- {
- public static string AppendAdditionalMessage(this string origin, string? message)
- {
- if (message == null)
- return origin;
- else
- return origin + " " + message;
- }
- }
-}
diff --git a/Timeline/Services/Exceptions/ImageException.cs b/Timeline/Services/Exceptions/ImageException.cs deleted file mode 100644 index 20dd48ae..00000000 --- a/Timeline/Services/Exceptions/ImageException.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class ImageException : Exception
- {
- public enum ErrorReason
- {
- /// <summary>
- /// Decoding image failed.
- /// </summary>
- CantDecode,
- /// <summary>
- /// Decoding succeeded but the real type is not the specified type.
- /// </summary>
- UnmatchedFormat,
- /// <summary>
- /// Image is not of required size.
- /// </summary>
- NotSquare,
- /// <summary>
- /// Other unknown errer.
- /// </summary>
- Unknown
- }
-
- public ImageException() : this(null) { }
- public ImageException(string? message) : this(message, null) { }
- public ImageException(string? message, Exception? inner) : this(ErrorReason.Unknown, null, null, null, message, inner) { }
-
- public ImageException(ErrorReason error, byte[]? data, string? requestType = null, string? realType = null, string? message = null, Exception? inner = null) : base(MakeMessage(error).AppendAdditionalMessage(message), inner) { Error = error; ImageData = data; RequestType = requestType; RealType = realType; }
-
- protected ImageException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- private static string MakeMessage(ErrorReason? reason) =>
- string.Format(CultureInfo.InvariantCulture, Resources.Services.Exceptions.ImageException, reason switch
- {
- ErrorReason.CantDecode => Resources.Services.Exceptions.ImageExceptionCantDecode,
- ErrorReason.UnmatchedFormat => Resources.Services.Exceptions.ImageExceptionUnmatchedFormat,
- ErrorReason.NotSquare => Resources.Services.Exceptions.ImageExceptionBadSize,
- _ => Resources.Services.Exceptions.ImageExceptionUnknownError
- });
-
- public ErrorReason Error { get; }
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[]? ImageData { get; }
-#pragma warning restore CA1819 // Properties should not return arrays
- public string? RequestType { get; }
-
- // This field will be null if decoding failed.
- public string? RealType { get; }
- }
-}
diff --git a/Timeline/Services/Exceptions/TimelineNotExistException.cs b/Timeline/Services/Exceptions/TimelineNotExistException.cs deleted file mode 100644 index 70970b24..00000000 --- a/Timeline/Services/Exceptions/TimelineNotExistException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelineNotExistException : EntityNotExistException
- {
- public TimelineNotExistException() : this(null, null) { }
- public TimelineNotExistException(string? timelineName) : this(timelineName, null) { }
- public TimelineNotExistException(string? timelineName, Exception? inner) : this(timelineName, null, inner) { }
- public TimelineNotExistException(string? timelineName, string? message, Exception? inner = null)
- : base(EntityNames.Timeline, null, string.Format(CultureInfo.InvariantCulture, Resources.Services.Exceptions.TimelineNotExistException, timelineName ?? "").AppendAdditionalMessage(message), inner) { TimelineName = timelineName; }
-
- protected TimelineNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? TimelineName { get; set; }
- }
-}
diff --git a/Timeline/Services/Exceptions/TimelinePostNoDataException.cs b/Timeline/Services/Exceptions/TimelinePostNoDataException.cs deleted file mode 100644 index c4b6bf62..00000000 --- a/Timeline/Services/Exceptions/TimelinePostNoDataException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelinePostNoDataException : Exception
- {
- public TimelinePostNoDataException() : this(null, null) { }
- public TimelinePostNoDataException(string? message) : this(message, null) { }
- public TimelinePostNoDataException(string? message, Exception? inner) : base(Resources.Services.Exceptions.TimelineNoDataException.AppendAdditionalMessage(message), inner) { }
- protected TimelinePostNoDataException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/Timeline/Services/Exceptions/TimelinePostNotExistException.cs b/Timeline/Services/Exceptions/TimelinePostNotExistException.cs deleted file mode 100644 index f95dd410..00000000 --- a/Timeline/Services/Exceptions/TimelinePostNotExistException.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- [Serializable]
- public class TimelinePostNotExistException : EntityNotExistException
- {
- public TimelinePostNotExistException() : this(null, null, false, null, null) { }
- [Obsolete("This has no meaning.")]
- public TimelinePostNotExistException(string? message) : this(message, null) { }
- [Obsolete("This has no meaning.")]
- public TimelinePostNotExistException(string? message, Exception? inner) : this(null, null, false, message, inner) { }
- protected TimelinePostNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public TimelinePostNotExistException(string? timelineName, long? id, bool isDelete, string? message = null, Exception? inner = null) : base(EntityNames.TimelinePost, null, MakeMessage(timelineName, id, isDelete).AppendAdditionalMessage(message), inner) { TimelineName = timelineName; Id = id; IsDelete = isDelete; }
-
- private static string MakeMessage(string? timelineName, long? id, bool isDelete)
- {
- return string.Format(CultureInfo.InvariantCulture, isDelete ? Resources.Services.Exceptions.TimelinePostNotExistExceptionDeleted : Resources.Services.Exceptions.TimelinePostNotExistException, timelineName ?? "", id);
- }
-
- public string? TimelineName { get; set; }
- public long? Id { get; set; }
-
- /// <summary>
- /// True if the post is deleted. False if the post does not exist at all.
- /// </summary>
- public bool IsDelete { get; set; }
- }
-}
diff --git a/Timeline/Services/Exceptions/UserNotExistException.cs b/Timeline/Services/Exceptions/UserNotExistException.cs deleted file mode 100644 index 7ef714df..00000000 --- a/Timeline/Services/Exceptions/UserNotExistException.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System;
-using System.Globalization;
-
-namespace Timeline.Services.Exceptions
-{
- /// <summary>
- /// The user requested does not exist.
- /// </summary>
- [Serializable]
- public class UserNotExistException : EntityNotExistException
- {
- public UserNotExistException() : this(null, null, null, null) { }
- public UserNotExistException(string? username, Exception? inner) : this(username, null, null, inner) { }
-
- public UserNotExistException(string? username) : this(username, null, null, null) { }
-
- public UserNotExistException(long id) : this(null, id, null, null) { }
-
- public UserNotExistException(string? username, long? id, string? message, Exception? inner) : base(EntityNames.User, null,
- string.Format(CultureInfo.InvariantCulture, Resources.Services.Exceptions.UserNotExistException, username ?? "", id).AppendAdditionalMessage(message), inner)
- {
- Username = username;
- Id = id;
- }
-
- protected UserNotExistException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- /// <summary>
- /// The username of the user that does not exist.
- /// </summary>
- public string? Username { get; set; }
-
- /// <summary>
- /// The id of the user that does not exist.
- /// </summary>
- public long? Id { get; set; }
- }
-}
diff --git a/Timeline/Services/ImageValidator.cs b/Timeline/Services/ImageValidator.cs deleted file mode 100644 index 59424a7c..00000000 --- a/Timeline/Services/ImageValidator.cs +++ /dev/null @@ -1,54 +0,0 @@ -using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Services
-{
- public interface IImageValidator
- {
- /// <summary>
- /// Validate a image data.
- /// </summary>
- /// <param name="data">The data of the image. Can't be null.</param>
- /// <param name="requestType">If not null, the real image format will be check against the requested format and throw if not match. If null, then do not check.</param>
- /// <param name="square">If true, image must be square.</param>
- /// <returns>The format.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="data"/> is null.</exception>
- /// <exception cref="ImageException">Thrown when image data can't be decoded or real type does not match request type or image is not square when required.</exception>
- Task<IImageFormat> Validate(byte[] data, string? requestType = null, bool square = false);
- }
-
- public class ImageValidator : IImageValidator
- {
- public ImageValidator()
- {
- }
-
- public async Task<IImageFormat> Validate(byte[] data, string? requestType = null, bool square = false)
- {
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- var format = await Task.Run(() =>
- {
- try
- {
- using var image = Image.Load(data, out IImageFormat format);
- if (requestType != null && !format.MimeTypes.Contains(requestType))
- throw new ImageException(ImageException.ErrorReason.UnmatchedFormat, data, requestType, format.DefaultMimeType);
- if (square && image.Width != image.Height)
- throw new ImageException(ImageException.ErrorReason.NotSquare, data, requestType, format.DefaultMimeType);
- return format;
- }
- catch (UnknownImageFormatException e)
- {
- throw new ImageException(ImageException.ErrorReason.CantDecode, data, requestType, null, null, e);
- }
- });
- return format;
- }
- }
-}
diff --git a/Timeline/Services/JwtUserTokenBadFormatException.cs b/Timeline/Services/JwtUserTokenBadFormatException.cs deleted file mode 100644 index c528c3e3..00000000 --- a/Timeline/Services/JwtUserTokenBadFormatException.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System;
-using System.Globalization;
-using static Timeline.Resources.Services.Exception;
-
-namespace Timeline.Services
-{
- [Serializable]
- public class JwtUserTokenBadFormatException : UserTokenBadFormatException
- {
- public enum ErrorKind
- {
- NoIdClaim,
- IdClaimBadFormat,
- NoVersionClaim,
- VersionClaimBadFormat,
- Other
- }
-
- public JwtUserTokenBadFormatException() : this("", ErrorKind.Other) { }
- public JwtUserTokenBadFormatException(string message) : base(message) { }
- public JwtUserTokenBadFormatException(string message, Exception inner) : base(message, inner) { }
-
- public JwtUserTokenBadFormatException(string token, ErrorKind type) : base(token, GetErrorMessage(type)) { ErrorType = type; }
- public JwtUserTokenBadFormatException(string token, ErrorKind type, Exception inner) : base(token, GetErrorMessage(type), inner) { ErrorType = type; }
- public JwtUserTokenBadFormatException(string token, ErrorKind type, string message, Exception inner) : base(token, message, inner) { ErrorType = type; }
- protected JwtUserTokenBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public ErrorKind ErrorType { get; set; }
-
- private static string GetErrorMessage(ErrorKind type)
- {
- var reason = type switch
- {
- ErrorKind.NoIdClaim => JwtUserTokenBadFormatExceptionIdMissing,
- ErrorKind.IdClaimBadFormat => JwtUserTokenBadFormatExceptionIdBadFormat,
- ErrorKind.NoVersionClaim => JwtUserTokenBadFormatExceptionVersionMissing,
- ErrorKind.VersionClaimBadFormat => JwtUserTokenBadFormatExceptionVersionBadFormat,
- ErrorKind.Other => JwtUserTokenBadFormatExceptionOthers,
- _ => JwtUserTokenBadFormatExceptionUnknown
- };
-
- return string.Format(CultureInfo.CurrentCulture,
- Resources.Services.Exception.JwtUserTokenBadFormatException, reason);
- }
- }
-}
diff --git a/Timeline/Services/PasswordBadFormatException.cs b/Timeline/Services/PasswordBadFormatException.cs deleted file mode 100644 index 2029ebb4..00000000 --- a/Timeline/Services/PasswordBadFormatException.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System;
-
-namespace Timeline.Services
-{
-
- [Serializable]
- public class PasswordBadFormatException : Exception
- {
- public PasswordBadFormatException() : base(Resources.Services.Exception.PasswordBadFormatException) { }
- public PasswordBadFormatException(string message) : base(message) { }
- public PasswordBadFormatException(string message, Exception inner) : base(message, inner) { }
-
- public PasswordBadFormatException(string password, string validationMessage) : this()
- {
- Password = password;
- ValidationMessage = validationMessage;
- }
-
- protected PasswordBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string Password { get; set; } = "";
-
- public string ValidationMessage { get; set; } = "";
- }
-}
diff --git a/Timeline/Services/PasswordService.cs b/Timeline/Services/PasswordService.cs deleted file mode 100644 index 8114a520..00000000 --- a/Timeline/Services/PasswordService.cs +++ /dev/null @@ -1,224 +0,0 @@ -using Microsoft.AspNetCore.Cryptography.KeyDerivation;
-using System;
-using System.Runtime.CompilerServices;
-using System.Security.Cryptography;
-
-namespace Timeline.Services
-{
- /// <summary>
- /// Hashed password is of bad format.
- /// </summary>
- /// <seealso cref="IPasswordService.VerifyPassword(string, string)"/>
- [Serializable]
- public class HashedPasswordBadFromatException : Exception
- {
- private static string MakeMessage(string reason)
- {
- return Resources.Services.Exception.HashedPasswordBadFromatException + " Reason: " + reason;
- }
-
- public HashedPasswordBadFromatException() : base(Resources.Services.Exception.HashedPasswordBadFromatException) { }
-
- public HashedPasswordBadFromatException(string message) : base(message) { }
- public HashedPasswordBadFromatException(string message, Exception inner) : base(message, inner) { }
-
- public HashedPasswordBadFromatException(string hashedPassword, string reason) : base(MakeMessage(reason)) { HashedPassword = hashedPassword; }
- public HashedPasswordBadFromatException(string hashedPassword, string reason, Exception inner) : base(MakeMessage(reason), inner) { HashedPassword = hashedPassword; }
- protected HashedPasswordBadFromatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string? HashedPassword { get; set; }
- }
-
- public interface IPasswordService
- {
- /// <summary>
- /// Hash a password.
- /// </summary>
- /// <param name="password">The password to hash.</param>
- /// <returns>A hashed representation of the supplied <paramref name="password"/>.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="password"/> is null.</exception>
- string HashPassword(string password);
-
- /// <summary>
- /// Verify whether the password fits into the hashed one.
- ///
- /// Usually you only need to check the returned bool value.
- /// Catching <see cref="HashedPasswordBadFromatException"/> usually is not necessary.
- /// Because if your program logic is right and always call <see cref="HashPassword(string)"/>
- /// and <see cref="VerifyPassword(string, string)"/> in pair, this exception will never be thrown.
- /// A thrown one usually means the data you saved is corupted, which is a critical problem.
- /// </summary>
- /// <param name="hashedPassword">The hashed password.</param>
- /// <param name="providedPassword">The password supplied for comparison.</param>
- /// <returns>True indicating password is right. Otherwise false.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="hashedPassword"/> or <paramref name="providedPassword"/> is null.</exception>
- /// <exception cref="HashedPasswordBadFromatException">Thrown when the hashed password is of bad format.</exception>
- bool VerifyPassword(string hashedPassword, string providedPassword);
- }
-
- /// <summary>
- /// Copied from https://github.com/aspnet/AspNetCore/blob/master/src/Identity/Extensions.Core/src/PasswordHasher.cs
- /// Remove V2 format and unnecessary format version check.
- /// Remove configuration options.
- /// Remove user related parts.
- /// Change the exceptions.
- /// </summary>
- public class PasswordService : IPasswordService
- {
- /* =======================
- * HASHED PASSWORD FORMATS
- * =======================
- *
- * Version 3:
- * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
- * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
- * (All UInt32s are stored big-endian.)
- */
-
- private readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
-
- public PasswordService()
- {
- }
-
- // Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- private static bool ByteArraysEqual(byte[] a, byte[] b)
- {
- if (a == null && b == null)
- {
- return true;
- }
- if (a == null || b == null || a.Length != b.Length)
- {
- return false;
- }
- var areSame = true;
- for (var i = 0; i < a.Length; i++)
- {
- areSame &= (a[i] == b[i]);
- }
- return areSame;
- }
-
- public string HashPassword(string password)
- {
- if (password == null)
- throw new ArgumentNullException(nameof(password));
- return Convert.ToBase64String(HashPasswordV3(password, _rng));
- }
-
- private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng)
- {
- return HashPasswordV3(password, rng,
- prf: KeyDerivationPrf.HMACSHA256,
- iterCount: 10000,
- saltSize: 128 / 8,
- numBytesRequested: 256 / 8);
- }
-
- private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
- {
- // Produce a version 3 (see comment above) text hash.
- byte[] salt = new byte[saltSize];
- rng.GetBytes(salt);
- byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
-
- var outputBytes = new byte[13 + salt.Length + subkey.Length];
- outputBytes[0] = 0x01; // format marker
- WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
- WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
- WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
- Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
- Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
- return outputBytes;
- }
-
- public bool VerifyPassword(string hashedPassword, string providedPassword)
- {
- if (hashedPassword == null)
- throw new ArgumentNullException(nameof(hashedPassword));
- if (providedPassword == null)
- throw new ArgumentNullException(nameof(providedPassword));
-
- byte[] decodedHashedPassword;
- try
- {
- decodedHashedPassword = Convert.FromBase64String(hashedPassword);
- }
- catch (FormatException e)
- {
- throw new HashedPasswordBadFromatException(hashedPassword, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotBase64, e);
- }
-
- // read the format marker from the hashed password
- if (decodedHashedPassword.Length == 0)
- {
- throw new HashedPasswordBadFromatException(hashedPassword, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotLength0);
- }
-
- return (decodedHashedPassword[0]) switch
- {
- 0x01 => VerifyHashedPasswordV3(decodedHashedPassword, providedPassword, hashedPassword),
- _ => throw new HashedPasswordBadFromatException(hashedPassword, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotUnknownMarker),
- };
- }
-
- private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string password, string hashedPasswordString)
- {
- try
- {
- // Read header information
- KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1);
- int iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5);
- int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9);
-
- // Read the salt: must be >= 128 bits
- if (saltLength < 128 / 8)
- {
- throw new HashedPasswordBadFromatException(hashedPasswordString, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotSaltTooShort);
- }
- byte[] salt = new byte[saltLength];
- Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length);
-
- // Read the subkey (the rest of the payload): must be >= 128 bits
- int subkeyLength = hashedPassword.Length - 13 - salt.Length;
- if (subkeyLength < 128 / 8)
- {
- throw new HashedPasswordBadFromatException(hashedPasswordString, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotSubkeyTooShort);
- }
- byte[] expectedSubkey = new byte[subkeyLength];
- Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);
-
- // Hash the incoming password and verify it
- byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength);
- return ByteArraysEqual(actualSubkey, expectedSubkey);
- }
- catch (Exception e)
- {
- // This should never occur except in the case of a malformed payload, where
- // we might go off the end of the array. Regardless, a malformed payload
- // implies verification failed.
- throw new HashedPasswordBadFromatException(hashedPasswordString, Resources.Services.Exception.HashedPasswordBadFromatExceptionNotOthers, e);
- }
- }
-
- private static uint ReadNetworkByteOrder(byte[] buffer, int offset)
- {
- return ((uint)(buffer[offset + 0]) << 24)
- | ((uint)(buffer[offset + 1]) << 16)
- | ((uint)(buffer[offset + 2]) << 8)
- | ((uint)(buffer[offset + 3]));
- }
-
- private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value)
- {
- buffer[offset + 0] = (byte)(value >> 24);
- buffer[offset + 1] = (byte)(value >> 16);
- buffer[offset + 2] = (byte)(value >> 8);
- buffer[offset + 3] = (byte)(value >> 0);
- }
- }
-}
diff --git a/Timeline/Services/PathProvider.cs b/Timeline/Services/PathProvider.cs deleted file mode 100644 index 1baba5c0..00000000 --- a/Timeline/Services/PathProvider.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.Extensions.Configuration;
-using System.IO;
-using Timeline.Configs;
-
-namespace Timeline.Services
-{
- public interface IPathProvider
- {
- public string GetWorkingDirectory();
- public string GetDatabaseFilePath();
- public string GetDatabaseBackupDirectory();
- }
-
- public class PathProvider : IPathProvider
- {
- private readonly IConfiguration _configuration;
-
- private readonly string _workingDirectory;
-
-
- public PathProvider(IConfiguration configuration)
- {
- _configuration = configuration;
- _workingDirectory = configuration.GetValue<string?>(ApplicationConfiguration.WorkDirKey) ?? ApplicationConfiguration.DefaultWorkDir;
- }
-
- public string GetWorkingDirectory()
- {
- return _workingDirectory;
- }
-
- public string GetDatabaseFilePath()
- {
- return Path.Combine(_workingDirectory, ApplicationConfiguration.DatabaseFileName);
- }
-
- public string GetDatabaseBackupDirectory()
- {
- return Path.Combine(_workingDirectory, ApplicationConfiguration.DatabaseBackupDirectoryName);
- }
- }
-}
diff --git a/Timeline/Services/TimelineService.cs b/Timeline/Services/TimelineService.cs deleted file mode 100644 index 4bcae596..00000000 --- a/Timeline/Services/TimelineService.cs +++ /dev/null @@ -1,1166 +0,0 @@ -using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using SixLabors.ImageSharp;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Validation;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Services.TimelineService;
-
-namespace Timeline.Services
-{
- public static class TimelineHelper
- {
- public static string ExtractTimelineName(string name, out bool isPersonal)
- {
- if (name.StartsWith("@", StringComparison.OrdinalIgnoreCase))
- {
- isPersonal = true;
- return name.Substring(1);
- }
- else
- {
- isPersonal = false;
- return name;
- }
- }
- }
-
- public enum TimelineUserRelationshipType
- {
- Own = 0b1,
- Join = 0b10,
- Default = Own | Join
- }
-
- public class TimelineUserRelationship
- {
- public TimelineUserRelationship(TimelineUserRelationshipType type, long userId)
- {
- Type = type;
- UserId = userId;
- }
-
- public TimelineUserRelationshipType Type { get; set; }
- public long UserId { get; set; }
- }
-
- public class PostData : ICacheableData
- {
-#pragma warning disable CA1819 // Properties should not return arrays
- public byte[] Data { get; set; } = default!;
-#pragma warning restore CA1819 // Properties should not return arrays
- public string Type { get; set; } = default!;
- public string ETag { get; set; } = default!;
- public DateTime? LastModified { get; set; } // TODO: Why nullable?
- }
-
- /// <summary>
- /// This define the interface of both personal timeline and ordinary timeline.
- /// </summary>
- public interface ITimelineService
- {
- /// <summary>
- /// Get the timeline last modified time (not include name change).
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<DateTime> GetTimelineLastModifiedTime(string timelineName);
-
- /// <summary>
- /// Get the timeline unique id.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<string> GetTimelineUniqueId(string timelineName);
-
- /// <summary>
- /// Get the timeline info.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <returns>The timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<Models.Timeline> GetTimeline(string timelineName);
-
- /// <summary>
- /// Set the properties of a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="newProperties">The new properties. Null member means not to change.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> or <paramref name="newProperties"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task ChangeProperty(string timelineName, TimelineChangePropertyRequest newProperties);
-
- /// <summary>
- /// Get all the posts in the timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="modifiedSince">The time that posts have been modified since.</param>
- /// <param name="includeDeleted">Whether include deleted posts.</param>
- /// <returns>A list of all posts.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- Task<List<TimelinePost>> GetPosts(string timelineName, DateTime? modifiedSince = null, bool includeDeleted = false);
-
- /// <summary>
- /// Get the etag of data of a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline of the post.</param>
- /// <param name="postId">The id of the post.</param>
- /// <returns>The etag of the data.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- /// <exception cref="TimelinePostNoDataException">Thrown when post has no data.</exception>
- /// <seealso cref="GetPostData(string, long)"/>
- Task<string> GetPostDataETag(string timelineName, long postId);
-
- /// <summary>
- /// Get the data of a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline of the post.</param>
- /// <param name="postId">The id of the post.</param>
- /// <returns>The etag of the data.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when post of <paramref name="postId"/> does not exist or has been deleted.</exception>
- /// <exception cref="TimelinePostNoDataException">Thrown when post has no data.</exception>
- /// <seealso cref="GetPostDataETag(string, long)"/>
- Task<PostData> GetPostData(string timelineName, long postId);
-
- /// <summary>
- /// Create a new text post in timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to create post against.</param>
- /// <param name="authorId">The author's user id.</param>
- /// <param name="text">The content text.</param>
- /// <param name="time">The time of the post. If null, then current time is used.</param>
- /// <returns>The info of the created post.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> or <paramref name="text"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="UserNotExistException">Thrown if user of <paramref name="authorId"/> does not exist.</exception>
- Task<TimelinePost> CreateTextPost(string timelineName, long authorId, string text, DateTime? time);
-
- /// <summary>
- /// Create a new image post in timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to create post against.</param>
- /// <param name="authorId">The author's user id.</param>
- /// <param name="imageData">The image data.</param>
- /// <param name="time">The time of the post. If null, then use current time.</param>
- /// <returns>The info of the created post.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> or <paramref name="imageData"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="UserNotExistException">Thrown if user of <paramref name="authorId"/> does not exist.</exception>
- /// <exception cref="ImageException">Thrown if data is not a image. Validated by <see cref="ImageValidator"/>.</exception>
- Task<TimelinePost> CreateImagePost(string timelineName, long authorId, byte[] imageData, DateTime? time);
-
- /// <summary>
- /// Delete a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to delete post against.</param>
- /// <param name="postId">The id of the post to delete.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already.</exception>
- /// <remarks>
- /// First use <see cref="HasPostModifyPermission(string, long, long, bool)"/> to check the permission.
- /// </remarks>
- Task DeletePost(string timelineName, long postId);
-
- /// <summary>
- /// Delete all posts of the given user. Used when delete a user.
- /// </summary>
- /// <param name="userId">The id of the user.</param>
- Task DeleteAllPostsOfUser(long userId);
-
- /// <summary>
- /// Change member of timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="membersToAdd">A list of usernames of members to add. May be null.</param>
- /// <param name="membersToRemove">A list of usernames of members to remove. May be null.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="ArgumentException">Thrown when names in <paramref name="membersToAdd"/> or <paramref name="membersToRemove"/> is not a valid username.</exception>
- /// <exception cref="UserNotExistException">Thrown when one of the user to change does not exist.</exception>
- /// <remarks>
- /// Operating on a username that is of bad format or does not exist always throws.
- /// Add a user that already is a member has no effects.
- /// Remove a user that is not a member also has not effects.
- /// Add and remove an identical user results in no effects.
- /// More than one same usernames are regarded as one.
- /// </remarks>
- Task ChangeMember(string timelineName, IList<string>? membersToAdd, IList<string>? membersToRemove);
-
- /// <summary>
- /// Check whether a user can manage(change timeline info, member, ...) a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="userId">The id of the user to check on.</param>
- /// <returns>True if the user can manage the timeline, otherwise false.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// This method does not check whether visitor is administrator.
- /// Return false if user with user id does not exist.
- /// </remarks>
- Task<bool> HasManagePermission(string timelineName, long userId);
-
- /// <summary>
- /// Verify whether a visitor has the permission to read a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="visitorId">The id of the user to check on. Null means visitor without account.</param>
- /// <returns>True if can read, false if can't read.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// This method does not check whether visitor is administrator.
- /// Return false if user with visitor id does not exist.
- /// </remarks>
- Task<bool> HasReadPermission(string timelineName, long? visitorId);
-
- /// <summary>
- /// Verify whether a user has the permission to modify a post.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="postId">The id of the post.</param>
- /// <param name="modifierId">The id of the user to check on.</param>
- /// <param name="throwOnPostNotExist">True if you want it to throw <see cref="TimelinePostNotExistException"/>. Default false.</param>
- /// <returns>True if can modify, false if can't modify.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <exception cref="TimelinePostNotExistException">Thrown when the post with given id does not exist or is deleted already and <paramref name="throwOnPostNotExist"/> is true.</exception>
- /// <remarks>
- /// Unless <paramref name="throwOnPostNotExist"/> is true, this method should return true if the post does not exist.
- /// If the post is deleted, its author info still exists, so it is checked as the post is not deleted unless <paramref name="throwOnPostNotExist"/> is true.
- /// This method does not check whether the user is administrator.
- /// It only checks whether he is the author of the post or the owner of the timeline.
- /// Return false when user with modifier id does not exist.
- /// </remarks>
- Task<bool> HasPostModifyPermission(string timelineName, long postId, long modifierId, bool throwOnPostNotExist = false);
-
- /// <summary>
- /// Verify whether a user is member of a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="userId">The id of user to check on.</param>
- /// <returns>True if it is a member, false if not.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Throw when <paramref name="timelineName"/> is of bad format.</exception>
- /// <exception cref="TimelineNotExistException">
- /// Thrown when timeline with name <paramref name="timelineName"/> does not exist.
- /// If it is a personal timeline, then inner exception is <see cref="UserNotExistException"/>.
- /// </exception>
- /// <remarks>
- /// Timeline owner is also considered as a member.
- /// Return false when user with user id does not exist.
- /// </remarks>
- Task<bool> IsMemberOf(string timelineName, long userId);
-
- /// <summary>
- /// Get all timelines including personal and ordinary timelines.
- /// </summary>
- /// <param name="relate">Filter timelines related (own or is a member) to specific user.</param>
- /// <param name="visibility">Filter timelines with given visibility. If null or empty, all visibilities are returned. Duplicate value are ignored.</param>
- /// <returns>The list of timelines.</returns>
- /// <remarks>
- /// If user with related user id does not exist, empty list will be returned.
- /// </remarks>
- Task<List<Models.Timeline>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null);
-
- /// <summary>
- /// Create a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline.</param>
- /// <param name="ownerId">The id of owner of the timeline.</param>
- /// <returns>The info of the new timeline.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when the timeline already exists.</exception>
- /// <exception cref="UserNotExistException">Thrown when the owner user does not exist.</exception>
- Task<Models.Timeline> CreateTimeline(string timelineName, long ownerId);
-
- /// <summary>
- /// Delete a timeline.
- /// </summary>
- /// <param name="timelineName">The name of the timeline to delete.</param>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="timelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when timeline name is invalid.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when the timeline does not exist.</exception>
- Task DeleteTimeline(string timelineName);
-
- /// <summary>
- /// Change name of a timeline.
- /// </summary>
- /// <param name="oldTimelineName">The old timeline name.</param>
- /// <param name="newTimelineName">The new timeline name.</param>
- /// <returns>The new timeline info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="oldTimelineName"/> or <paramref name="newTimelineName"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="oldTimelineName"/> or <paramref name="newTimelineName"/> is of invalid format.</exception>
- /// <exception cref="TimelineNotExistException">Thrown when timeline does not exist.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when a timeline with new name already exists.</exception>
- /// <remarks>
- /// You can only change name of general timeline.
- /// </remarks>
- Task<Models.Timeline> ChangeTimelineName(string oldTimelineName, string newTimelineName);
- }
-
- public class TimelineService : ITimelineService
- {
- public TimelineService(ILogger<TimelineService> logger, DatabaseContext database, IDataManager dataManager, IUserService userService, IImageValidator imageValidator, IClock clock)
- {
- _logger = logger;
- _database = database;
- _dataManager = dataManager;
- _userService = userService;
- _imageValidator = imageValidator;
- _clock = clock;
- }
-
- private readonly ILogger<TimelineService> _logger;
-
- private readonly DatabaseContext _database;
-
- private readonly IDataManager _dataManager;
-
- private readonly IUserService _userService;
-
- private readonly IImageValidator _imageValidator;
-
- private readonly IClock _clock;
-
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
-
- private readonly TimelineNameValidator _timelineNameValidator = new TimelineNameValidator();
-
- private void ValidateTimelineName(string name, string paramName)
- {
- if (!_timelineNameValidator.Validate(name, out var message))
- {
- throw new ArgumentException(ExceptionTimelineNameBadFormat.AppendAdditionalMessage(message), paramName);
- }
- }
-
- /// Remember to include Members when query.
- private async Task<Models.Timeline> MapTimelineFromEntity(TimelineEntity entity)
- {
- var owner = await _userService.GetUserById(entity.OwnerId);
-
- var members = new List<User>();
- foreach (var memberEntity in entity.Members)
- {
- members.Add(await _userService.GetUserById(memberEntity.UserId));
- }
-
- var name = entity.Name ?? ("@" + owner.Username);
-
- return new Models.Timeline
- {
- UniqueID = entity.UniqueId,
- Name = name,
- NameLastModified = entity.NameLastModified,
- Title = string.IsNullOrEmpty(entity.Title) ? name : entity.Title,
- Description = entity.Description ?? "",
- Owner = owner,
- Visibility = entity.Visibility,
- Members = members,
- CreateTime = entity.CreateTime,
- LastModified = entity.LastModified
- };
- }
-
- private async Task<TimelinePost> MapTimelinePostFromEntity(TimelinePostEntity entity, string timelineName)
- {
- User? author = entity.AuthorId.HasValue ? await _userService.GetUserById(entity.AuthorId.Value) : null;
-
- ITimelinePostContent? content = null;
-
- if (entity.Content != null)
- {
- var type = entity.ContentType;
-
- content = type switch
- {
- TimelinePostContentTypes.Text => new TextTimelinePostContent(entity.Content),
- TimelinePostContentTypes.Image => new ImageTimelinePostContent(entity.Content),
- _ => throw new DatabaseCorruptedException(string.Format(CultureInfo.InvariantCulture, ExceptionDatabaseUnknownContentType, type))
- };
- }
-
- return new TimelinePost(
- id: entity.LocalId,
- author: author,
- content: content,
- time: entity.Time,
- lastUpdated: entity.LastUpdated,
- timelineName: timelineName
- );
- }
-
- private TimelineEntity CreateNewTimelineEntity(string? name, long ownerId)
- {
- var currentTime = _clock.GetCurrentTime();
-
- return new TimelineEntity
- {
- Name = name,
- NameLastModified = currentTime,
- OwnerId = ownerId,
- Visibility = TimelineVisibility.Register,
- CreateTime = currentTime,
- LastModified = currentTime,
- CurrentPostLocalId = 0,
- Members = new List<TimelineMemberEntity>()
- };
- }
-
-
-
- // Get timeline id by name. If it is a personal timeline and it does not exist, it will be created.
- //
- // This method will check the name format and if it is invalid, ArgumentException is thrown.
- //
- // For personal timeline, if the user does not exist, TimelineNotExistException will be thrown with UserNotExistException as inner exception.
- // For ordinary timeline, if the timeline does not exist, TimelineNotExistException will be thrown.
- //
- // It follows all timeline-related function common interface contracts.
- private async Task<long> FindTimelineId(string timelineName)
- {
- timelineName = TimelineHelper.ExtractTimelineName(timelineName, out var isPersonal);
-
- if (isPersonal)
- {
- long userId;
- try
- {
- userId = await _userService.GetUserIdByUsername(timelineName);
- }
- catch (ArgumentException e)
- {
- throw new ArgumentException(ExceptionFindTimelineUsernameBadFormat, nameof(timelineName), e);
- }
- catch (UserNotExistException e)
- {
- throw new TimelineNotExistException(timelineName, e);
- }
-
- var timelineEntity = await _database.Timelines.Where(t => t.OwnerId == userId && t.Name == null).Select(t => new { t.Id }).SingleOrDefaultAsync();
-
- if (timelineEntity != null)
- {
- return timelineEntity.Id;
- }
- else
- {
- var newTimelineEntity = CreateNewTimelineEntity(null, userId);
- _database.Timelines.Add(newTimelineEntity);
- await _database.SaveChangesAsync();
-
- return newTimelineEntity.Id;
- }
- }
- else
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- ValidateTimelineName(timelineName, nameof(timelineName));
-
- var timelineEntity = await _database.Timelines.Where(t => t.Name == timelineName).Select(t => new { t.Id }).SingleOrDefaultAsync();
-
- if (timelineEntity == null)
- {
- throw new TimelineNotExistException(timelineName);
- }
- else
- {
- return timelineEntity.Id;
- }
- }
- }
-
- public async Task<DateTime> GetTimelineLastModifiedTime(string timelineName)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.LastModified }).SingleAsync();
-
- return timelineEntity.LastModified;
- }
-
- public async Task<string> GetTimelineUniqueId(string timelineName)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.UniqueId }).SingleAsync();
-
- return timelineEntity.UniqueId;
- }
-
- public async Task<Models.Timeline> GetTimeline(string timelineName)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Include(t => t.Members).SingleAsync();
-
- return await MapTimelineFromEntity(timelineEntity);
- }
-
- public async Task<List<TimelinePost>> GetPosts(string timelineName, DateTime? modifiedSince = null, bool includeDeleted = false)
- {
- modifiedSince = modifiedSince?.MyToUtc();
-
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- IQueryable<TimelinePostEntity> query = _database.TimelinePosts.Where(p => p.TimelineId == timelineId);
-
- if (!includeDeleted)
- {
- query = query.Where(p => p.Content != null);
- }
-
- if (modifiedSince.HasValue)
- {
- query = query.Include(p => p.Author).Where(p => p.LastUpdated >= modifiedSince || (p.Author != null && p.Author.UsernameChangeTime >= modifiedSince));
- }
-
- query = query.OrderBy(p => p.Time);
-
- var postEntities = await query.ToListAsync();
-
- var posts = new List<TimelinePost>();
- foreach (var entity in postEntities)
- {
- posts.Add(await MapTimelinePostFromEntity(entity, timelineName));
- }
- return posts;
- }
-
- public async Task<string> GetPostDataETag(string timelineName, long postId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
-
- if (postEntity == null)
- throw new TimelinePostNotExistException(timelineName, postId, false);
-
- if (postEntity.Content == null)
- throw new TimelinePostNotExistException(timelineName, postId, true);
-
- if (postEntity.ContentType != TimelinePostContentTypes.Image)
- throw new TimelinePostNoDataException(ExceptionGetDataNonImagePost);
-
- var tag = postEntity.Content;
-
- return tag;
- }
-
- public async Task<PostData> GetPostData(string timelineName, long postId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- var postEntity = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == postId).SingleOrDefaultAsync();
-
- if (postEntity == null)
- throw new TimelinePostNotExistException(timelineName, postId, false);
-
- if (postEntity.Content == null)
- throw new TimelinePostNotExistException(timelineName, postId, true);
-
- if (postEntity.ContentType != TimelinePostContentTypes.Image)
- throw new TimelinePostNoDataException(ExceptionGetDataNonImagePost);
-
- var tag = postEntity.Content;
-
- byte[] data;
-
- try
- {
- data = await _dataManager.GetEntry(tag);
- }
- catch (InvalidOperationException e)
- {
- throw new DatabaseCorruptedException(ExceptionGetDataDataEntryNotExist, e);
- }
-
- if (postEntity.ExtraContent == null)
- {
- _logger.LogWarning(LogGetDataNoFormat);
- var format = Image.DetectFormat(data);
- postEntity.ExtraContent = format.DefaultMimeType;
- await _database.SaveChangesAsync();
- }
-
- return new PostData
- {
- Data = data,
- Type = postEntity.ExtraContent,
- ETag = tag,
- LastModified = postEntity.LastUpdated
- };
- }
-
- public async Task<TimelinePost> CreateTextPost(string timelineName, long authorId, string text, DateTime? time)
- {
- time = time?.MyToUtc();
-
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
- if (text == null)
- throw new ArgumentNullException(nameof(text));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var author = await _userService.GetUserById(authorId);
-
- var currentTime = _clock.GetCurrentTime();
- var finalTime = time ?? currentTime;
-
- timelineEntity.CurrentPostLocalId += 1;
-
- var postEntity = new TimelinePostEntity
- {
- LocalId = timelineEntity.CurrentPostLocalId,
- ContentType = TimelinePostContentTypes.Text,
- Content = text,
- AuthorId = authorId,
- TimelineId = timelineId,
- Time = finalTime,
- LastUpdated = currentTime
- };
- _database.TimelinePosts.Add(postEntity);
- await _database.SaveChangesAsync();
-
-
- return new TimelinePost(
- id: postEntity.LocalId,
- content: new TextTimelinePostContent(text),
- time: finalTime,
- author: author,
- lastUpdated: currentTime,
- timelineName: timelineName
- );
- }
-
- public async Task<TimelinePost> CreateImagePost(string timelineName, long authorId, byte[] data, DateTime? time)
- {
- time = time?.MyToUtc();
-
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var author = await _userService.GetUserById(authorId);
-
- var imageFormat = await _imageValidator.Validate(data);
-
- var imageFormatText = imageFormat.DefaultMimeType;
-
- var tag = await _dataManager.RetainEntry(data);
-
- var currentTime = _clock.GetCurrentTime();
- var finalTime = time ?? currentTime;
-
- timelineEntity.CurrentPostLocalId += 1;
-
- var postEntity = new TimelinePostEntity
- {
- LocalId = timelineEntity.CurrentPostLocalId,
- ContentType = TimelinePostContentTypes.Image,
- Content = tag,
- ExtraContent = imageFormatText,
- AuthorId = authorId,
- TimelineId = timelineId,
- Time = finalTime,
- LastUpdated = currentTime
- };
- _database.TimelinePosts.Add(postEntity);
- await _database.SaveChangesAsync();
-
- return new TimelinePost(
- id: postEntity.LocalId,
- content: new ImageTimelinePostContent(tag),
- time: finalTime,
- author: author,
- lastUpdated: currentTime,
- timelineName: timelineName
- );
- }
-
- public async Task DeletePost(string timelineName, long id)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var post = await _database.TimelinePosts.Where(p => p.TimelineId == timelineId && p.LocalId == id).SingleOrDefaultAsync();
-
- if (post == null)
- throw new TimelinePostNotExistException(timelineName, id, false);
-
- if (post.Content == null)
- throw new TimelinePostNotExistException(timelineName, id, true);
-
- string? dataTag = null;
-
- if (post.ContentType == TimelinePostContentTypes.Image)
- {
- dataTag = post.Content;
- }
-
- post.Content = null;
- post.LastUpdated = _clock.GetCurrentTime();
-
- await _database.SaveChangesAsync();
-
- if (dataTag != null)
- {
- await _dataManager.FreeEntry(dataTag);
- }
- }
-
- public async Task DeleteAllPostsOfUser(long userId)
- {
- var posts = await _database.TimelinePosts.Where(p => p.AuthorId == userId).ToListAsync();
-
- var now = _clock.GetCurrentTime();
-
- var dataTags = new List<string>();
-
- foreach (var post in posts)
- {
- if (post.Content != null)
- {
- if (post.ContentType == TimelinePostContentTypes.Image)
- {
- dataTags.Add(post.Content);
- }
- post.Content = null;
- }
- post.LastUpdated = now;
- }
-
- await _database.SaveChangesAsync();
-
- foreach (var dataTag in dataTags)
- {
- await _dataManager.FreeEntry(dataTag);
- }
- }
-
- public async Task ChangeProperty(string timelineName, TimelineChangePropertyRequest newProperties)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
- if (newProperties == null)
- throw new ArgumentNullException(nameof(newProperties));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
-
- var changed = false;
-
- if (newProperties.Title != null)
- {
- changed = true;
- timelineEntity.Title = newProperties.Title;
- }
-
- if (newProperties.Description != null)
- {
- changed = true;
- timelineEntity.Description = newProperties.Description;
- }
-
- if (newProperties.Visibility.HasValue)
- {
- changed = true;
- timelineEntity.Visibility = newProperties.Visibility.Value;
- }
-
- if (changed)
- {
- var currentTime = _clock.GetCurrentTime();
- timelineEntity.LastModified = currentTime;
- }
-
- await _database.SaveChangesAsync();
- }
-
- public async Task ChangeMember(string timelineName, IList<string>? add, IList<string>? remove)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- List<string>? RemoveDuplicateAndCheckFormat(IList<string>? list, string paramName)
- {
- if (list != null)
- {
- List<string> result = new List<string>();
- var count = list.Count;
- for (var index = 0; index < count; index++)
- {
- var username = list[index];
- if (result.Contains(username))
- {
- continue;
- }
- var (validationResult, message) = _usernameValidator.Validate(username);
- if (!validationResult)
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionChangeMemberUsernameBadFormat, index), nameof(paramName));
- result.Add(username);
- }
- return result;
- }
- else
- {
- return null;
- }
- }
- var simplifiedAdd = RemoveDuplicateAndCheckFormat(add, nameof(add));
- var simplifiedRemove = RemoveDuplicateAndCheckFormat(remove, nameof(remove));
-
- // remove those both in add and remove
- if (simplifiedAdd != null && simplifiedRemove != null)
- {
- var usersToClean = simplifiedRemove.Where(u => simplifiedAdd.Contains(u)).ToList();
- foreach (var u in usersToClean)
- {
- simplifiedAdd.Remove(u);
- simplifiedRemove.Remove(u);
- }
-
- if (simplifiedAdd.Count == 0)
- simplifiedAdd = null;
-
- if (simplifiedRemove.Count == 0)
- simplifiedRemove = null;
- }
-
- if (simplifiedAdd == null && simplifiedRemove == null)
- return;
-
- var timelineId = await FindTimelineId(timelineName);
-
- async Task<List<long>?> CheckExistenceAndGetId(List<string>? list)
- {
- if (list == null)
- return null;
-
- List<long> result = new List<long>();
- foreach (var username in list)
- {
- result.Add(await _userService.GetUserIdByUsername(username));
- }
- return result;
- }
- var userIdsAdd = await CheckExistenceAndGetId(simplifiedAdd);
- var userIdsRemove = await CheckExistenceAndGetId(simplifiedRemove);
-
- if (userIdsAdd != null)
- {
- var membersToAdd = userIdsAdd.Select(id => new TimelineMemberEntity { UserId = id, TimelineId = timelineId }).ToList();
- _database.TimelineMembers.AddRange(membersToAdd);
- }
-
- if (userIdsRemove != null)
- {
- var membersToRemove = await _database.TimelineMembers.Where(m => m.TimelineId == timelineId && userIdsRemove.Contains(m.UserId)).ToListAsync();
- _database.TimelineMembers.RemoveRange(membersToRemove);
- }
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).SingleAsync();
- timelineEntity.LastModified = _clock.GetCurrentTime();
-
- await _database.SaveChangesAsync();
- }
-
- public async Task<bool> HasManagePermission(string timelineName, long userId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
-
- return userId == timelineEntity.OwnerId;
- }
-
- public async Task<bool> HasReadPermission(string timelineName, long? visitorId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.Visibility }).SingleAsync();
-
- if (timelineEntity.Visibility == TimelineVisibility.Public)
- return true;
-
- if (timelineEntity.Visibility == TimelineVisibility.Register && visitorId != null)
- return true;
-
- if (visitorId == null)
- {
- return false;
- }
- else
- {
- var memberEntity = await _database.TimelineMembers.Where(m => m.UserId == visitorId && m.TimelineId == timelineId).SingleOrDefaultAsync();
- return memberEntity != null;
- }
- }
-
- public async Task<bool> HasPostModifyPermission(string timelineName, long postId, long modifierId, bool throwOnPostNotExist = false)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
-
- var postEntity = await _database.TimelinePosts.Where(p => p.Id == postId).Select(p => new { p.Content, p.AuthorId }).SingleOrDefaultAsync();
-
- if (postEntity == null)
- {
- if (throwOnPostNotExist)
- throw new TimelinePostNotExistException(timelineName, postId, false);
- else
- return true;
- }
-
- if (postEntity.Content == null && throwOnPostNotExist)
- {
- throw new TimelinePostNotExistException(timelineName, postId, true);
- }
-
- return timelineEntity.OwnerId == modifierId || postEntity.AuthorId == modifierId;
- }
-
- public async Task<bool> IsMemberOf(string timelineName, long userId)
- {
- if (timelineName == null)
- throw new ArgumentNullException(nameof(timelineName));
-
- var timelineId = await FindTimelineId(timelineName);
-
- var timelineEntity = await _database.Timelines.Where(t => t.Id == timelineId).Select(t => new { t.OwnerId }).SingleAsync();
-
- if (userId == timelineEntity.OwnerId)
- return true;
-
- return await _database.TimelineMembers.AnyAsync(m => m.TimelineId == timelineId && m.UserId == userId);
- }
-
- public async Task<List<Models.Timeline>> GetTimelines(TimelineUserRelationship? relate = null, List<TimelineVisibility>? visibility = null)
- {
- List<TimelineEntity> entities;
-
- IQueryable<TimelineEntity> ApplyTimelineVisibilityFilter(IQueryable<TimelineEntity> query)
- {
- if (visibility != null && visibility.Count != 0)
- {
- return query.Where(t => visibility.Contains(t.Visibility));
- }
- return query;
- }
-
- bool allVisibilities = visibility == null || visibility.Count == 0;
-
- if (relate == null)
- {
- entities = await ApplyTimelineVisibilityFilter(_database.Timelines).Include(t => t.Members).ToListAsync();
- }
- else
- {
- entities = new List<TimelineEntity>();
-
- if ((relate.Type & TimelineUserRelationshipType.Own) != 0)
- {
- entities.AddRange(await ApplyTimelineVisibilityFilter(_database.Timelines.Where(t => t.OwnerId == relate.UserId)).Include(t => t.Members).ToListAsync());
- }
-
- if ((relate.Type & TimelineUserRelationshipType.Join) != 0)
- {
- entities.AddRange(await ApplyTimelineVisibilityFilter(_database.TimelineMembers.Where(m => m.UserId == relate.UserId).Include(m => m.Timeline).ThenInclude(t => t.Members).Select(m => m.Timeline)).ToListAsync());
- }
- }
-
- var result = new List<Models.Timeline>();
-
- foreach (var entity in entities)
- {
- result.Add(await MapTimelineFromEntity(entity));
- }
-
- return result;
- }
-
- public async Task<Models.Timeline> CreateTimeline(string name, long owner)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- ValidateTimelineName(name, nameof(name));
-
- var user = await _userService.GetUserById(owner);
-
- var conflict = await _database.Timelines.AnyAsync(t => t.Name == name);
-
- if (conflict)
- throw new EntityAlreadyExistException(EntityNames.Timeline, null, ExceptionTimelineNameConflict);
-
- var newEntity = CreateNewTimelineEntity(name, user.Id!.Value);
-
- _database.Timelines.Add(newEntity);
- await _database.SaveChangesAsync();
-
- return await MapTimelineFromEntity(newEntity);
- }
-
- public async Task DeleteTimeline(string name)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- ValidateTimelineName(name, nameof(name));
-
- var entity = await _database.Timelines.Where(t => t.Name == name).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new TimelineNotExistException(name);
-
- _database.Timelines.Remove(entity);
- await _database.SaveChangesAsync();
- }
-
- public async Task<Models.Timeline> ChangeTimelineName(string oldTimelineName, string newTimelineName)
- {
- if (oldTimelineName == null)
- throw new ArgumentNullException(nameof(oldTimelineName));
- if (newTimelineName == null)
- throw new ArgumentNullException(nameof(newTimelineName));
-
- ValidateTimelineName(oldTimelineName, nameof(oldTimelineName));
- ValidateTimelineName(newTimelineName, nameof(newTimelineName));
-
- var entity = await _database.Timelines.Include(t => t.Members).Where(t => t.Name == oldTimelineName).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new TimelineNotExistException(oldTimelineName);
-
- if (oldTimelineName == newTimelineName)
- return await MapTimelineFromEntity(entity);
-
- var conflict = await _database.Timelines.AnyAsync(t => t.Name == newTimelineName);
-
- if (conflict)
- throw new EntityAlreadyExistException(EntityNames.Timeline, null, ExceptionTimelineNameConflict);
-
- var now = _clock.GetCurrentTime();
-
- entity.Name = newTimelineName;
- entity.NameLastModified = now;
- entity.LastModified = now;
-
- await _database.SaveChangesAsync();
-
- return await MapTimelineFromEntity(entity);
- }
- }
-}
diff --git a/Timeline/Services/UserAvatarService.cs b/Timeline/Services/UserAvatarService.cs deleted file mode 100644 index b41c45fd..00000000 --- a/Timeline/Services/UserAvatarService.cs +++ /dev/null @@ -1,265 +0,0 @@ -using Microsoft.AspNetCore.Hosting;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Services
-{
- public class Avatar
- {
- public string Type { get; set; } = default!;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "DTO Object")]
- public byte[] Data { get; set; } = default!;
- }
-
- public class AvatarInfo
- {
- public Avatar Avatar { get; set; } = default!;
- public DateTime LastModified { get; set; }
-
- public CacheableData ToCacheableData()
- {
- return new CacheableData(Avatar.Type, Avatar.Data, LastModified);
- }
- }
-
- /// <summary>
- /// Provider for default user avatar.
- /// </summary>
- /// <remarks>
- /// Mainly for unit tests.
- /// </remarks>
- public interface IDefaultUserAvatarProvider
- {
- /// <summary>
- /// Get the etag of default avatar.
- /// </summary>
- /// <returns></returns>
- Task<string> GetDefaultAvatarETag();
-
- /// <summary>
- /// Get the default avatar.
- /// </summary>
- Task<AvatarInfo> GetDefaultAvatar();
- }
-
- public interface IUserAvatarService
- {
- /// <summary>
- /// Get the etag of a user's avatar. Warning: This method does not check the user existence.
- /// </summary>
- /// <param name="id">The id of the user to get avatar etag of.</param>
- /// <returns>The etag.</returns>
- Task<string> GetAvatarETag(long id);
-
- /// <summary>
- /// Get avatar of a user. If the user has no avatar set, a default one is returned. Warning: This method does not check the user existence.
- /// </summary>
- /// <param name="id">The id of the user to get avatar of.</param>
- /// <returns>The avatar info.</returns>
- Task<AvatarInfo> GetAvatar(long id);
-
- /// <summary>
- /// Set avatar for a user. Warning: This method does not check the user existence.
- /// </summary>
- /// <param name="id">The id of the user to set avatar for.</param>
- /// <param name="avatar">The avatar. Can be null to delete the saved avatar.</param>
- /// <returns>The etag of the avatar.</returns>
- /// <exception cref="ArgumentException">Thrown if any field in <paramref name="avatar"/> is null when <paramref name="avatar"/> is not null.</exception>
- /// <exception cref="ImageException">Thrown if avatar is of bad format.</exception>
- Task<string> SetAvatar(long id, Avatar? avatar);
- }
-
- // TODO! : Make this configurable.
- public class DefaultUserAvatarProvider : IDefaultUserAvatarProvider
- {
- private readonly IETagGenerator _eTagGenerator;
-
- private readonly string _avatarPath;
-
- private byte[] _cacheData = default!;
- private DateTime _cacheLastModified;
- private string _cacheETag = default!;
-
- public DefaultUserAvatarProvider(IWebHostEnvironment environment, IETagGenerator eTagGenerator)
- {
- _avatarPath = Path.Combine(environment.ContentRootPath, "default-avatar.png");
- _eTagGenerator = eTagGenerator;
- }
-
- private async Task CheckAndInit()
- {
- var path = _avatarPath;
- if (_cacheData == null || File.GetLastWriteTime(path) > _cacheLastModified)
- {
- _cacheData = await File.ReadAllBytesAsync(path);
- _cacheLastModified = File.GetLastWriteTime(path);
- _cacheETag = await _eTagGenerator.Generate(_cacheData);
- }
- }
-
- public async Task<string> GetDefaultAvatarETag()
- {
- await CheckAndInit();
- return _cacheETag;
- }
-
- public async Task<AvatarInfo> GetDefaultAvatar()
- {
- await CheckAndInit();
- return new AvatarInfo
- {
- Avatar = new Avatar
- {
- Type = "image/png",
- Data = _cacheData
- },
- LastModified = _cacheLastModified
- };
- }
- }
-
- public class UserAvatarService : IUserAvatarService
- {
-
- private readonly ILogger<UserAvatarService> _logger;
-
- private readonly DatabaseContext _database;
-
- private readonly IDefaultUserAvatarProvider _defaultUserAvatarProvider;
-
- private readonly IImageValidator _imageValidator;
-
- private readonly IDataManager _dataManager;
-
- private readonly IClock _clock;
-
- public UserAvatarService(
- ILogger<UserAvatarService> logger,
- DatabaseContext database,
- IDefaultUserAvatarProvider defaultUserAvatarProvider,
- IImageValidator imageValidator,
- IDataManager dataManager,
- IClock clock)
- {
- _logger = logger;
- _database = database;
- _defaultUserAvatarProvider = defaultUserAvatarProvider;
- _imageValidator = imageValidator;
- _dataManager = dataManager;
- _clock = clock;
- }
-
- public async Task<string> GetAvatarETag(long id)
- {
- var eTag = (await _database.UserAvatars.Where(a => a.UserId == id).Select(a => new { a.DataTag }).SingleOrDefaultAsync())?.DataTag;
- if (eTag == null)
- return await _defaultUserAvatarProvider.GetDefaultAvatarETag();
- else
- return eTag;
- }
-
- public async Task<AvatarInfo> GetAvatar(long id)
- {
- var avatarEntity = await _database.UserAvatars.Where(a => a.UserId == id).Select(a => new { a.Type, a.DataTag, a.LastModified }).SingleOrDefaultAsync();
-
- if (avatarEntity != null)
- {
- if (!LanguageHelper.AreSame(avatarEntity.DataTag == null, avatarEntity.Type == null))
- {
- var message = Resources.Services.UserAvatarService.ExceptionDatabaseCorruptedDataAndTypeNotSame;
- _logger.LogCritical(message);
- throw new DatabaseCorruptedException(message);
- }
-
-
- if (avatarEntity.DataTag != null)
- {
- var data = await _dataManager.GetEntry(avatarEntity.DataTag);
- return new AvatarInfo
- {
- Avatar = new Avatar
- {
- Type = avatarEntity.Type!,
- Data = data
- },
- LastModified = avatarEntity.LastModified
- };
- }
- }
- var defaultAvatar = await _defaultUserAvatarProvider.GetDefaultAvatar();
- if (avatarEntity != null)
- defaultAvatar.LastModified = defaultAvatar.LastModified > avatarEntity.LastModified ? defaultAvatar.LastModified : avatarEntity.LastModified;
- return defaultAvatar;
- }
-
- public async Task<string> SetAvatar(long id, Avatar? avatar)
- {
- if (avatar != null)
- {
- if (avatar.Data == null)
- throw new ArgumentException(Resources.Services.UserAvatarService.ExceptionAvatarDataNull, nameof(avatar));
- if (string.IsNullOrEmpty(avatar.Type))
- throw new ArgumentException(Resources.Services.UserAvatarService.ExceptionAvatarTypeNullOrEmpty, nameof(avatar));
- }
-
- var avatarEntity = await _database.UserAvatars.Where(a => a.UserId == id).SingleOrDefaultAsync();
-
- if (avatar == null)
- {
- if (avatarEntity != null && avatarEntity.DataTag != null)
- {
- await _dataManager.FreeEntry(avatarEntity.DataTag);
- avatarEntity.DataTag = null;
- avatarEntity.Type = null;
- avatarEntity.LastModified = _clock.GetCurrentTime();
- await _database.SaveChangesAsync();
- _logger.LogInformation(Resources.Services.UserAvatarService.LogUpdateEntity);
- }
- return await _defaultUserAvatarProvider.GetDefaultAvatarETag();
- }
- else
- {
- await _imageValidator.Validate(avatar.Data, avatar.Type, true);
- var tag = await _dataManager.RetainEntry(avatar.Data);
- var oldTag = avatarEntity?.DataTag;
- var create = avatarEntity == null;
- if (avatarEntity == null)
- {
- avatarEntity = new UserAvatarEntity();
- _database.UserAvatars.Add(avatarEntity);
- }
- avatarEntity.DataTag = tag;
- avatarEntity.Type = avatar.Type;
- avatarEntity.LastModified = _clock.GetCurrentTime();
- avatarEntity.UserId = id;
- await _database.SaveChangesAsync();
- _logger.LogInformation(create ?
- Resources.Services.UserAvatarService.LogCreateEntity
- : Resources.Services.UserAvatarService.LogUpdateEntity);
- if (oldTag != null)
- {
- await _dataManager.FreeEntry(oldTag);
- }
-
- return avatarEntity.DataTag;
- }
- }
- }
-
- public static class UserAvatarServiceCollectionExtensions
- {
- public static void AddUserAvatarService(this IServiceCollection services)
- {
- services.AddScoped<IUserAvatarService, UserAvatarService>();
- services.AddScoped<IDefaultUserAvatarProvider, DefaultUserAvatarProvider>();
- }
- }
-}
diff --git a/Timeline/Services/UserDeleteService.cs b/Timeline/Services/UserDeleteService.cs deleted file mode 100644 index 845de573..00000000 --- a/Timeline/Services/UserDeleteService.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Models.Validation;
-using static Timeline.Resources.Services.UserService;
-
-namespace Timeline.Services
-{
- public interface IUserDeleteService
- {
- /// <summary>
- /// Delete a user of given username.
- /// </summary>
- /// <param name="username">Username of the user to delete. Can't be null.</param>
- /// <returns>True if user is deleted, false if user not exist.</returns>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- Task<bool> DeleteUser(string username);
- }
-
- public class UserDeleteService : IUserDeleteService
- {
- private readonly ILogger<UserDeleteService> _logger;
-
- private readonly DatabaseContext _databaseContext;
-
- private readonly ITimelineService _timelineService;
-
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
-
- public UserDeleteService(ILogger<UserDeleteService> logger, DatabaseContext databaseContext, ITimelineService timelineService)
- {
- _logger = logger;
- _databaseContext = databaseContext;
- _timelineService = timelineService;
- }
-
- public async Task<bool> DeleteUser(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
-
- if (!_usernameValidator.Validate(username, out var message))
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), nameof(username));
- }
-
- var user = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
- if (user == null)
- return false;
-
- await _timelineService.DeleteAllPostsOfUser(user.Id);
-
- _databaseContext.Users.Remove(user);
-
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(LogDatabaseRemove, ("Id", user.Id), ("Username", user.Username)));
-
- return true;
- }
-
- }
-}
diff --git a/Timeline/Services/UserRoleConvert.cs b/Timeline/Services/UserRoleConvert.cs deleted file mode 100644 index f27ee1bb..00000000 --- a/Timeline/Services/UserRoleConvert.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System;
-using System.Collections.Generic;
-using System.Linq;
-using Timeline.Entities;
-
-namespace Timeline.Services
-{
- public static class UserRoleConvert
- {
- public const string UserRole = UserRoles.User;
- public const string AdminRole = UserRoles.Admin;
-
- public static string[] ToArray(bool administrator)
- {
- return administrator ? new string[] { UserRole, AdminRole } : new string[] { UserRole };
- }
-
- public static string[] ToArray(string s)
- {
- return s.Split(',').ToArray();
- }
-
- public static bool ToBool(IReadOnlyCollection<string> roles)
- {
- return roles.Contains(AdminRole);
- }
-
- public static string ToString(IReadOnlyCollection<string> roles)
- {
- return string.Join(',', roles);
- }
-
- public static string ToString(bool administrator)
- {
- return administrator ? UserRole + "," + AdminRole : UserRole;
- }
-
- public static bool ToBool(string s)
- {
- return s.Contains("admin", StringComparison.InvariantCulture);
- }
- }
-}
diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs deleted file mode 100644 index 821bc33d..00000000 --- a/Timeline/Services/UserService.cs +++ /dev/null @@ -1,437 +0,0 @@ -using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using Timeline.Entities;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Models.Validation;
-using Timeline.Services.Exceptions;
-using static Timeline.Resources.Services.UserService;
-
-namespace Timeline.Services
-{
- public interface IUserService
- {
- /// <summary>
- /// Try to verify the given username and password.
- /// </summary>
- /// <param name="username">The username of the user to verify.</param>
- /// <param name="password">The password of the user to verify.</param>
- /// <returns>The user info and auth info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format or <paramref name="password"/> is empty.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown when password is wrong.</exception>
- Task<User> VerifyCredential(string username, string password);
-
- /// <summary>
- /// Try to get a user by id.
- /// </summary>
- /// <param name="id">The id of the user.</param>
- /// <returns>The user info.</returns>
- /// <exception cref="UserNotExistException">Thrown when the user with given id does not exist.</exception>
- Task<User> GetUserById(long id);
-
- /// <summary>
- /// Get the user info of given username.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <returns>The info of the user.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
- Task<User> GetUserByUsername(string username);
-
- /// <summary>
- /// Get the user id of given username.
- /// </summary>
- /// <param name="username">Username of the user.</param>
- /// <returns>The id of the user.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with given username does not exist.</exception>
- Task<long> GetUserIdByUsername(string username);
-
- /// <summary>
- /// List all users.
- /// </summary>
- /// <returns>The user info of users.</returns>
- Task<User[]> GetUsers();
-
- /// <summary>
- /// Create a user with given info.
- /// </summary>
- /// <param name="info">The info of new user.</param>
- /// <returns>The the new user.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="info"/>is null.</exception>
- /// <exception cref="ArgumentException">Thrown when some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when a user with given username already exists.</exception>
- /// <remarks>
- /// <see cref="User.Username"/> must not be null and must be a valid username.
- /// <see cref="User.Password"/> must not be null or empty.
- /// <see cref="User.Administrator"/> is false by default (null).
- /// <see cref="User.Nickname"/> must be a valid nickname if set. It is empty by default.
- /// Other fields are ignored.
- /// </remarks>
- Task<User> CreateUser(User info);
-
- /// <summary>
- /// Modify a user's info.
- /// </summary>
- /// <param name="id">The id of the user.</param>
- /// <param name="info">The new info. May be null.</param>
- /// <returns>The new user info.</returns>
- /// <exception cref="ArgumentException">Thrown when some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="UserNotExistException">Thrown when user with given id does not exist.</exception>
- /// <remarks>
- /// Only <see cref="User.Username"/>, <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
- /// If null, then not change.
- /// Other fields are ignored.
- /// Version will increase if password is changed.
- ///
- /// <see cref="User.Username"/> must be a valid username if set.
- /// <see cref="User.Password"/> can't be empty if set.
- /// <see cref="User.Nickname"/> must be a valid nickname if set.
- ///
- /// </remarks>
- /// <seealso cref="ModifyUser(string, User)"/>
- Task<User> ModifyUser(long id, User? info);
-
- /// <summary>
- /// Modify a user's info.
- /// </summary>
- /// <param name="username">The username of the user.</param>
- /// <param name="info">The new info. May be null.</param>
- /// <returns>The new user info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format or some fields in <paramref name="info"/> is bad.</exception>
- /// <exception cref="UserNotExistException">Thrown when user with given id does not exist.</exception>
- /// <exception cref="EntityAlreadyExistException">Thrown when user with the newusername already exist.</exception>
- /// <remarks>
- /// Only <see cref="User.Administrator"/>, <see cref="User.Password"/> and <see cref="User.Nickname"/> will be used.
- /// If null, then not change.
- /// Other fields are ignored.
- /// After modified, even if nothing is changed, version will increase.
- ///
- /// <see cref="User.Username"/> must be a valid username if set.
- /// <see cref="User.Password"/> can't be empty if set.
- /// <see cref="User.Nickname"/> must be a valid nickname if set.
- ///
- /// Note: Whether <see cref="User.Version"/> is set or not, version will increase and not set to the specified value if there is one.
- /// </remarks>
- /// <seealso cref="ModifyUser(long, User)"/>
- Task<User> ModifyUser(string username, User? info);
-
- /// <summary>
- /// Try to change a user's password with old password.
- /// </summary>
- /// <param name="id">The id of user to change password of.</param>
- /// <param name="oldPassword">Old password.</param>
- /// <param name="newPassword">New password.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="oldPassword"/> or <paramref name="newPassword"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown if <paramref name="oldPassword"/> or <paramref name="newPassword"/> is empty.</exception>
- /// <exception cref="UserNotExistException">Thrown if the user with given username does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown if the old password is wrong.</exception>
- Task ChangePassword(long id, string oldPassword, string newPassword);
- }
-
- public class UserService : IUserService
- {
- private readonly ILogger<UserService> _logger;
- private readonly IClock _clock;
-
- private readonly DatabaseContext _databaseContext;
-
- private readonly IPasswordService _passwordService;
-
- private readonly UsernameValidator _usernameValidator = new UsernameValidator();
- private readonly NicknameValidator _nicknameValidator = new NicknameValidator();
- public UserService(ILogger<UserService> logger, DatabaseContext databaseContext, IPasswordService passwordService, IClock clock)
- {
- _logger = logger;
- _clock = clock;
- _databaseContext = databaseContext;
- _passwordService = passwordService;
- }
-
- private void CheckUsernameFormat(string username, string? paramName)
- {
- if (!_usernameValidator.Validate(username, out var message))
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionUsernameBadFormat, message), paramName);
- }
- }
-
- private static void CheckPasswordFormat(string password, string? paramName)
- {
- if (password.Length == 0)
- {
- throw new ArgumentException(ExceptionPasswordEmpty, paramName);
- }
- }
-
- private void CheckNicknameFormat(string nickname, string? paramName)
- {
- if (!_nicknameValidator.Validate(nickname, out var message))
- {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExceptionNicknameBadFormat, message), paramName);
- }
- }
-
- private static void ThrowUsernameConflict()
- {
- throw new EntityAlreadyExistException(EntityNames.User, ExceptionUsernameConflict);
- }
-
- private static User CreateUserFromEntity(UserEntity entity)
- {
- return new User
- {
- UniqueId = entity.UniqueId,
- Username = entity.Username,
- Administrator = UserRoleConvert.ToBool(entity.Roles),
- Nickname = string.IsNullOrEmpty(entity.Nickname) ? entity.Username : entity.Nickname,
- Id = entity.Id,
- Version = entity.Version,
- CreateTime = entity.CreateTime,
- UsernameChangeTime = entity.UsernameChangeTime,
- LastModified = entity.LastModified
- };
- }
-
- public async Task<User> VerifyCredential(string username, string password)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- if (password == null)
- throw new ArgumentNullException(nameof(password));
-
- CheckUsernameFormat(username, nameof(username));
- CheckPasswordFormat(password, nameof(password));
-
- var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(username);
-
- if (!_passwordService.VerifyPassword(entity.Password, password))
- throw new BadPasswordException(password);
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task<User> GetUserById(long id)
- {
- var user = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
-
- if (user == null)
- throw new UserNotExistException(id);
-
- return CreateUserFromEntity(user);
- }
-
- public async Task<User> GetUserByUsername(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
-
- CheckUsernameFormat(username, nameof(username));
-
- var entity = await _databaseContext.Users.Where(user => user.Username == username).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(username);
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task<long> GetUserIdByUsername(string username)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
-
- CheckUsernameFormat(username, nameof(username));
-
- var entity = await _databaseContext.Users.Where(user => user.Username == username).Select(u => new { u.Id }).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(username);
-
- return entity.Id;
- }
-
- public async Task<User[]> GetUsers()
- {
- var entities = await _databaseContext.Users.ToArrayAsync();
- return entities.Select(user => CreateUserFromEntity(user)).ToArray();
- }
-
- public async Task<User> CreateUser(User info)
- {
- if (info == null)
- throw new ArgumentNullException(nameof(info));
-
- if (info.Username == null)
- throw new ArgumentException(ExceptionUsernameNull, nameof(info));
- CheckUsernameFormat(info.Username, nameof(info));
-
- if (info.Password == null)
- throw new ArgumentException(ExceptionPasswordNull, nameof(info));
- CheckPasswordFormat(info.Password, nameof(info));
-
- if (info.Nickname != null)
- CheckNicknameFormat(info.Nickname, nameof(info));
-
- var username = info.Username;
-
- var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username);
- if (conflict)
- ThrowUsernameConflict();
-
- var administrator = info.Administrator ?? false;
- var password = info.Password;
- var nickname = info.Nickname;
-
- var newEntity = new UserEntity
- {
- Username = username,
- Password = _passwordService.HashPassword(password),
- Roles = UserRoleConvert.ToString(administrator),
- Nickname = nickname,
- Version = 1
- };
- _databaseContext.Users.Add(newEntity);
- await _databaseContext.SaveChangesAsync();
-
- _logger.LogInformation(Log.Format(LogDatabaseCreate,
- ("Id", newEntity.Id), ("Username", username), ("Administrator", administrator)));
-
- return CreateUserFromEntity(newEntity);
- }
-
- private void ValidateModifyUserInfo(User? info)
- {
- if (info != null)
- {
- if (info.Username != null)
- CheckUsernameFormat(info.Username, nameof(info));
-
- if (info.Password != null)
- CheckPasswordFormat(info.Password, nameof(info));
-
- if (info.Nickname != null)
- CheckNicknameFormat(info.Nickname, nameof(info));
- }
- }
-
- private async Task UpdateUserEntity(UserEntity entity, User? info)
- {
- if (info != null)
- {
- var now = _clock.GetCurrentTime();
- bool updateLastModified = false;
-
- var username = info.Username;
- if (username != null && username != entity.Username)
- {
- var conflict = await _databaseContext.Users.AnyAsync(u => u.Username == username);
- if (conflict)
- ThrowUsernameConflict();
-
- entity.Username = username;
- entity.UsernameChangeTime = now;
- updateLastModified = true;
- }
-
- var password = info.Password;
- if (password != null)
- {
- entity.Password = _passwordService.HashPassword(password);
- entity.Version += 1;
- }
-
- var administrator = info.Administrator;
- if (administrator.HasValue && UserRoleConvert.ToBool(entity.Roles) != administrator)
- {
- entity.Roles = UserRoleConvert.ToString(administrator.Value);
- updateLastModified = true;
- }
-
- var nickname = info.Nickname;
- if (nickname != null && nickname != entity.Nickname)
- {
- entity.Nickname = nickname;
- updateLastModified = true;
- }
-
- if (updateLastModified)
- {
- entity.LastModified = now;
- }
- }
- }
-
-
- public async Task<User> ModifyUser(long id, User? info)
- {
- ValidateModifyUserInfo(info);
-
- var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
- if (entity == null)
- throw new UserNotExistException(id);
-
- await UpdateUserEntity(entity, info);
-
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(LogDatabaseUpdate, ("Id", id));
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task<User> ModifyUser(string username, User? info)
- {
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- CheckUsernameFormat(username, nameof(username));
-
- ValidateModifyUserInfo(info);
-
- var entity = await _databaseContext.Users.Where(u => u.Username == username).SingleOrDefaultAsync();
- if (entity == null)
- throw new UserNotExistException(username);
-
- await UpdateUserEntity(entity, info);
-
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(LogDatabaseUpdate, ("Username", username));
-
- return CreateUserFromEntity(entity);
- }
-
- public async Task ChangePassword(long id, string oldPassword, string newPassword)
- {
- if (oldPassword == null)
- throw new ArgumentNullException(nameof(oldPassword));
- if (newPassword == null)
- throw new ArgumentNullException(nameof(newPassword));
- CheckPasswordFormat(oldPassword, nameof(oldPassword));
- CheckPasswordFormat(newPassword, nameof(newPassword));
-
- var entity = await _databaseContext.Users.Where(u => u.Id == id).SingleOrDefaultAsync();
-
- if (entity == null)
- throw new UserNotExistException(id);
-
- if (!_passwordService.VerifyPassword(entity.Password, oldPassword))
- throw new BadPasswordException(oldPassword);
-
- entity.Password = _passwordService.HashPassword(newPassword);
- entity.Version += 1;
- await _databaseContext.SaveChangesAsync();
- _logger.LogInformation(Log.Format(LogDatabaseUpdate, ("Id", id), ("Operation", "Change password")));
- }
- }
-}
diff --git a/Timeline/Services/UserTokenException.cs b/Timeline/Services/UserTokenException.cs deleted file mode 100644 index d25fabb3..00000000 --- a/Timeline/Services/UserTokenException.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System;
-
-namespace Timeline.Services
-{
-
- [Serializable]
- public class UserTokenException : Exception
- {
- public UserTokenException() { }
- public UserTokenException(string message) : base(message) { }
- public UserTokenException(string message, Exception inner) : base(message, inner) { }
- public UserTokenException(string token, string message) : base(message) { Token = token; }
- public UserTokenException(string token, string message, Exception inner) : base(message, inner) { Token = token; }
- protected UserTokenException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public string Token { get; private set; } = "";
- }
-
-
- [Serializable]
- public class UserTokenTimeExpireException : UserTokenException
- {
- public UserTokenTimeExpireException() : base(Resources.Services.Exception.UserTokenTimeExpireException) { }
- public UserTokenTimeExpireException(string message) : base(message) { }
- public UserTokenTimeExpireException(string message, Exception inner) : base(message, inner) { }
- public UserTokenTimeExpireException(string token, DateTime expireTime, DateTime verifyTime) : base(token, Resources.Services.Exception.UserTokenTimeExpireException) { ExpireTime = expireTime; VerifyTime = verifyTime; }
- public UserTokenTimeExpireException(string token, DateTime expireTime, DateTime verifyTime, Exception inner) : base(token, Resources.Services.Exception.UserTokenTimeExpireException, inner) { ExpireTime = expireTime; VerifyTime = verifyTime; }
- protected UserTokenTimeExpireException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public DateTime ExpireTime { get; private set; }
-
- public DateTime VerifyTime { get; private set; }
- }
-
- [Serializable]
- public class UserTokenBadVersionException : UserTokenException
- {
- public UserTokenBadVersionException() : base(Resources.Services.Exception.UserTokenBadVersionException) { }
- public UserTokenBadVersionException(string message) : base(message) { }
- public UserTokenBadVersionException(string message, Exception inner) : base(message, inner) { }
- public UserTokenBadVersionException(string token, long tokenVersion, long requiredVersion) : base(token, Resources.Services.Exception.UserTokenBadVersionException) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; }
- public UserTokenBadVersionException(string token, long tokenVersion, long requiredVersion, Exception inner) : base(token, Resources.Services.Exception.UserTokenBadVersionException, inner) { TokenVersion = tokenVersion; RequiredVersion = requiredVersion; }
- protected UserTokenBadVersionException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
-
- public long TokenVersion { get; set; }
-
- public long RequiredVersion { get; set; }
- }
-
- [Serializable]
- public class UserTokenBadFormatException : UserTokenException
- {
- public UserTokenBadFormatException() : base(Resources.Services.Exception.UserTokenBadFormatException) { }
- public UserTokenBadFormatException(string token) : base(token, Resources.Services.Exception.UserTokenBadFormatException) { }
- public UserTokenBadFormatException(string token, string message) : base(token, message) { }
- public UserTokenBadFormatException(string token, Exception inner) : base(token, Resources.Services.Exception.UserTokenBadFormatException, inner) { }
- public UserTokenBadFormatException(string token, string message, Exception inner) : base(token, message, inner) { }
- protected UserTokenBadFormatException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
- }
-}
diff --git a/Timeline/Services/UserTokenManager.cs b/Timeline/Services/UserTokenManager.cs deleted file mode 100644 index 813dae67..00000000 --- a/Timeline/Services/UserTokenManager.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Microsoft.Extensions.Logging;
-using System;
-using System.Threading.Tasks;
-using Timeline.Helpers;
-using Timeline.Models;
-using Timeline.Services.Exceptions;
-
-namespace Timeline.Services
-{
- public class UserTokenCreateResult
- {
- public string Token { get; set; } = default!;
- public User User { get; set; } = default!;
- }
-
- public interface IUserTokenManager
- {
- /// <summary>
- /// Try to create a token for given username and password.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <param name="password">The password.</param>
- /// <param name="expireAt">The expire time of the token.</param>
- /// <returns>The created token and the user info.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="username"/> or <paramref name="password"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown when <paramref name="username"/> is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user with <paramref name="username"/> does not exist.</exception>
- /// <exception cref="BadPasswordException">Thrown when <paramref name="password"/> is wrong.</exception>
- public Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null);
-
- /// <summary>
- /// Verify a token and get the saved user info. This also check the database for existence of the user.
- /// </summary>
- /// <param name="token">The token.</param>
- /// <returns>The user stored in token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
- /// <exception cref="UserTokenTimeExpireException">Thrown when the token is expired.</exception>
- /// <exception cref="UserTokenBadVersionException">Thrown when the token is of bad version.</exception>
- /// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
- /// <exception cref="UserNotExistException">Thrown when the user specified by the token does not exist. Usually the user had been deleted after the token was issued.</exception>
- public Task<User> VerifyToken(string token);
- }
-
- public class UserTokenManager : IUserTokenManager
- {
- private readonly ILogger<UserTokenManager> _logger;
- private readonly IUserService _userService;
- private readonly IUserTokenService _userTokenService;
- private readonly IClock _clock;
-
- public UserTokenManager(ILogger<UserTokenManager> logger, IUserService userService, IUserTokenService userTokenService, IClock clock)
- {
- _logger = logger;
- _userService = userService;
- _userTokenService = userTokenService;
- _clock = clock;
- }
-
- public async Task<UserTokenCreateResult> CreateToken(string username, string password, DateTime? expireAt = null)
- {
- expireAt = expireAt?.MyToUtc();
-
- if (username == null)
- throw new ArgumentNullException(nameof(username));
- if (password == null)
- throw new ArgumentNullException(nameof(password));
-
- var user = await _userService.VerifyCredential(username, password);
- var token = _userTokenService.GenerateToken(new UserTokenInfo { Id = user.Id!.Value, Version = user.Version!.Value, ExpireAt = expireAt });
-
- return new UserTokenCreateResult { Token = token, User = user };
- }
-
-
- public async Task<User> VerifyToken(string token)
- {
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- var tokenInfo = _userTokenService.VerifyToken(token);
-
- if (tokenInfo.ExpireAt.HasValue)
- {
- var currentTime = _clock.GetCurrentTime();
- if (tokenInfo.ExpireAt < currentTime)
- throw new UserTokenTimeExpireException(token, tokenInfo.ExpireAt.Value, currentTime);
- }
-
- var user = await _userService.GetUserById(tokenInfo.Id);
-
- if (tokenInfo.Version < user.Version)
- throw new UserTokenBadVersionException(token, tokenInfo.Version, user.Version.Value);
-
- return user;
- }
- }
-}
diff --git a/Timeline/Services/UserTokenService.cs b/Timeline/Services/UserTokenService.cs deleted file mode 100644 index 86f3a0f7..00000000 --- a/Timeline/Services/UserTokenService.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Microsoft.Extensions.Options;
-using Microsoft.IdentityModel.Tokens;
-using System;
-using System.Globalization;
-using System.IdentityModel.Tokens.Jwt;
-using System.Linq;
-using System.Security.Claims;
-using Timeline.Configs;
-using Timeline.Entities;
-
-namespace Timeline.Services
-{
- public class UserTokenInfo
- {
- public long Id { get; set; }
- public long Version { get; set; }
- public DateTime? ExpireAt { get; set; }
- }
-
- public interface IUserTokenService
- {
- /// <summary>
- /// Create a token for a given token info.
- /// </summary>
- /// <param name="tokenInfo">The info to generate token.</param>
- /// <returns>Return the generated token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="tokenInfo"/> is null.</exception>
- string GenerateToken(UserTokenInfo tokenInfo);
-
- /// <summary>
- /// Verify a token and get the saved info.
- /// </summary>
- /// <param name="token">The token to verify.</param>
- /// <returns>The saved info in token.</returns>
- /// <exception cref="ArgumentNullException">Thrown when <paramref name="token"/> is null.</exception>
- /// <exception cref="UserTokenBadFormatException">Thrown when the token is of bad format.</exception>
- /// <remarks>
- /// If this method throw <see cref="UserTokenBadFormatException"/>, it usually means the token is not created by this service.
- /// </remarks>
- UserTokenInfo VerifyToken(string token);
- }
-
- public class JwtUserTokenService : IUserTokenService
- {
- private const string VersionClaimType = "timeline_version";
-
- private readonly IOptionsMonitor<JwtConfiguration> _jwtConfig;
- private readonly IClock _clock;
-
- private readonly JwtSecurityTokenHandler _tokenHandler = new JwtSecurityTokenHandler();
- private SymmetricSecurityKey _tokenSecurityKey;
-
- public JwtUserTokenService(IOptionsMonitor<JwtConfiguration> jwtConfig, IClock clock, DatabaseContext database)
- {
- _jwtConfig = jwtConfig;
- _clock = clock;
-
- var key = database.JwtToken.Select(t => t.Key).SingleOrDefault();
-
- if (key == null)
- {
- throw new InvalidOperationException(Resources.Services.UserTokenService.JwtKeyNotExist);
- }
-
- _tokenSecurityKey = new SymmetricSecurityKey(key);
- }
-
- public string GenerateToken(UserTokenInfo tokenInfo)
- {
- if (tokenInfo == null)
- throw new ArgumentNullException(nameof(tokenInfo));
-
- var config = _jwtConfig.CurrentValue;
-
- var identity = new ClaimsIdentity();
- identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokenInfo.Id.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64));
- identity.AddClaim(new Claim(VersionClaimType, tokenInfo.Version.ToString(CultureInfo.InvariantCulture.NumberFormat), ClaimValueTypes.Integer64));
-
- var tokenDescriptor = new SecurityTokenDescriptor()
- {
- Subject = identity,
- Issuer = config.Issuer,
- Audience = config.Audience,
- SigningCredentials = new SigningCredentials(_tokenSecurityKey, SecurityAlgorithms.HmacSha384),
- IssuedAt = _clock.GetCurrentTime(),
- Expires = tokenInfo.ExpireAt.GetValueOrDefault(_clock.GetCurrentTime().AddSeconds(config.DefaultExpireOffset)),
- NotBefore = _clock.GetCurrentTime() // I must explicitly set this or it will use the current time by default and mock is not work in which case test will not pass.
- };
-
- var token = _tokenHandler.CreateToken(tokenDescriptor);
- var tokenString = _tokenHandler.WriteToken(token);
-
- return tokenString;
- }
-
-
- public UserTokenInfo VerifyToken(string token)
- {
- if (token == null)
- throw new ArgumentNullException(nameof(token));
-
- var config = _jwtConfig.CurrentValue;
- try
- {
- var principal = _tokenHandler.ValidateToken(token, new TokenValidationParameters
- {
- ValidateIssuer = true,
- ValidateAudience = true,
- ValidateIssuerSigningKey = true,
- ValidateLifetime = false,
- ValidIssuer = config.Issuer,
- ValidAudience = config.Audience,
- IssuerSigningKey = _tokenSecurityKey
- }, out var t);
-
- var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier);
- if (idClaim == null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoIdClaim);
- if (!long.TryParse(idClaim, out var id))
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.IdClaimBadFormat);
-
- var versionClaim = principal.FindFirstValue(VersionClaimType);
- if (versionClaim == null)
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.NoVersionClaim);
- if (!long.TryParse(versionClaim, out var version))
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.VersionClaimBadFormat);
-
- var decodedToken = (JwtSecurityToken)t;
- var exp = decodedToken.Payload.Exp;
- DateTime? expireAt = null;
- if (exp.HasValue)
- {
- expireAt = EpochTime.DateTime(exp.Value);
- }
-
- return new UserTokenInfo
- {
- Id = id,
- Version = version,
- ExpireAt = expireAt
- };
- }
- catch (Exception e) when (e is SecurityTokenException || e is ArgumentException)
- {
- throw new JwtUserTokenBadFormatException(token, JwtUserTokenBadFormatException.ErrorKind.Other, e);
- }
- }
- }
-}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs deleted file mode 100644 index 82c231cb..00000000 --- a/Timeline/Startup.cs +++ /dev/null @@ -1,187 +0,0 @@ -using AutoMapper;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Infrastructure;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
-using NSwag;
-using NSwag.Generation.Processors.Security;
-using System;
-using System.ComponentModel;
-using System.Net.Mime;
-using System.Text.Json.Serialization;
-using Timeline.Auth;
-using Timeline.Configs;
-using Timeline.Entities;
-using Timeline.Formatters;
-using Timeline.Helpers;
-using Timeline.Models.Converters;
-using Timeline.Routes;
-using Timeline.Services;
-using Timeline.Swagger;
-
-namespace Timeline
-{
- public class Startup
- {
- private readonly bool disableFrontEnd;
- private readonly bool useMockFrontEnd;
-
- public Startup(IConfiguration configuration, IWebHostEnvironment environment)
- {
- Environment = environment;
- Configuration = configuration;
-
- disableFrontEnd = Configuration.GetValue<bool?>(ApplicationConfiguration.DisableFrontEndKey) ?? false;
- useMockFrontEnd = Configuration.GetValue<bool?>(ApplicationConfiguration.UseMockFrontEndKey) ?? false;
- }
-
- public IWebHostEnvironment Environment { get; }
- public IConfiguration Configuration { get; }
-
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- TypeDescriptor.AddAttributes(typeof(DateTime), new TypeConverterAttribute(typeof(MyDateTimeConverter)));
-
- services.AddControllers(setup =>
- {
- setup.InputFormatters.Add(new StringInputFormatter());
- setup.InputFormatters.Add(new BytesInputFormatter());
- setup.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json, "text/json"));
- setup.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json, "text/json"));
- setup.UseApiRoutePrefix("api");
- })
- .AddJsonOptions(options =>
- {
- options.JsonSerializerOptions.IgnoreNullValues = true;
- options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
- options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter());
- })
- .ConfigureApiBehaviorOptions(options =>
- {
- options.InvalidModelStateResponseFactory = InvalidModelResponseFactory.Factory;
- });
-
- services.Configure<JwtConfiguration>(Configuration.GetSection("Jwt"));
- services.AddAuthentication(AuthenticationConstants.Scheme)
- .AddScheme<MyAuthenticationOptions, MyAuthenticationHandler>(AuthenticationConstants.Scheme, AuthenticationConstants.DisplayName, o => { });
- services.AddAuthorization();
-
- services.AddSingleton<IPathProvider, PathProvider>();
-
- services.AddSingleton<IDatabaseBackupService, DatabaseBackupService>();
-
- services.AddAutoMapper(GetType().Assembly);
-
- services.AddTransient<IClock, Clock>();
-
- services.AddTransient<IPasswordService, PasswordService>();
- services.AddScoped<IUserService, UserService>();
- services.AddScoped<IUserDeleteService, UserDeleteService>();
- services.AddScoped<IUserTokenService, JwtUserTokenService>();
- services.AddScoped<IUserTokenManager, UserTokenManager>();
-
- services.AddScoped<IETagGenerator, ETagGenerator>();
- services.AddScoped<IDataManager, DataManager>();
-
- services.AddScoped<IImageValidator, ImageValidator>();
-
- services.AddUserAvatarService();
-
- services.AddScoped<ITimelineService, TimelineService>();
-
- services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
-
- services.AddDbContext<DatabaseContext>((services, options) =>
- {
- var pathProvider = services.GetRequiredService<IPathProvider>();
- options.UseSqlite($"Data Source={pathProvider.GetDatabaseFilePath()}");
- });
-
- services.AddSwaggerDocument(document =>
- {
- document.DocumentName = "Timeline";
- document.Title = "Timeline REST API Reference";
- document.Version = typeof(Startup).Assembly.GetName().Version?.ToString() ?? "unknown version";
- document.DocumentProcessors.Add(new DocumentDescriptionDocumentProcessor());
- document.DocumentProcessors.Add(
- new SecurityDefinitionAppender("JWT",
- new OpenApiSecurityScheme
- {
- Type = OpenApiSecuritySchemeType.ApiKey,
- Name = "Authorization",
- In = OpenApiSecurityApiKeyLocation.Header,
- Description = "Create token via `/api/token/create` ."
- }));
- document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("JWT"));
- document.OperationProcessors.Add(new DefaultDescriptionOperationProcessor());
- document.OperationProcessors.Add(new ByteDataRequestOperationProcessor());
- });
-
- if (!disableFrontEnd)
- {
- if (useMockFrontEnd)
- {
- services.AddSpaStaticFiles(config =>
- {
- config.RootPath = "MockClientApp";
- });
-
- }
- else if (!Environment.IsDevelopment()) // In development, we don't want to serve dist. Or it will take precedence than front end dev server.
- {
- services.AddSpaStaticFiles(config =>
- {
- config.RootPath = "ClientApp/dist";
- });
- }
- }
- }
-
-
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app)
- {
- app.UseRouting();
-
- if (!disableFrontEnd && (useMockFrontEnd || !Environment.IsDevelopment()))
- {
- app.UseSpaStaticFiles(new StaticFileOptions
- {
- ServeUnknownFileTypes = true
- });
- }
-
- app.UseOpenApi();
- app.UseReDoc();
-
- app.UseAuthentication();
- app.UseAuthorization();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
-
- UnknownEndpointMiddleware.Attach(app);
-
- if (!disableFrontEnd)
- {
- app.UseSpa(spa =>
- {
- spa.Options.SourcePath = useMockFrontEnd ? "MockClientApp" : "ClientApp";
-
- if (!useMockFrontEnd && (Configuration.GetValue<bool?>(ApplicationConfiguration.UseProxyFrontEndKey) ?? false))
- {
- spa.UseProxyToSpaDevelopmentServer(new UriBuilder("http", "localhost", 3000).Uri);
- }
- });
- }
- }
- }
-}
diff --git a/Timeline/Swagger/ApiConvention.cs b/Timeline/Swagger/ApiConvention.cs deleted file mode 100644 index dbf0b2fe..00000000 --- a/Timeline/Swagger/ApiConvention.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.AspNetCore.Mvc;
-
-[assembly: ApiConventionType(typeof(Timeline.Controllers.ApiConvention))]
-
-namespace Timeline.Controllers
-{
- // There is some bug if nullable is enable. So disable it.
-#nullable disable
- /// <summary>
- /// My api convention.
- /// </summary>
- public static class ApiConvention
- {
- }
-}
diff --git a/Timeline/Swagger/ByteDataRequestOperationProcessor.cs b/Timeline/Swagger/ByteDataRequestOperationProcessor.cs deleted file mode 100644 index 887831ac..00000000 --- a/Timeline/Swagger/ByteDataRequestOperationProcessor.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NJsonSchema;
-using NSwag;
-using NSwag.Generation.Processors;
-using NSwag.Generation.Processors.Contexts;
-using System.Linq;
-using Timeline.Models;
-
-namespace Timeline.Swagger
-{
- /// <summary>
- /// Coerce ByteData body type into the right one.
- /// </summary>
- public class ByteDataRequestOperationProcessor : IOperationProcessor
- {
- /// <inheritdoc/>
- public bool Process(OperationProcessorContext context)
- {
- var hasByteDataBody = context.MethodInfo.GetParameters().Where(p => p.ParameterType == typeof(ByteData)).Any();
- if (hasByteDataBody)
- {
- var bodyParameter = context.OperationDescription.Operation.Parameters.Where(p => p.Kind == OpenApiParameterKind.Body).Single();
- bodyParameter.Schema = JsonSchema.FromType<byte[]>();
- }
- return true;
- }
- }
-}
diff --git a/Timeline/Swagger/DefaultDescriptionOperationProcessor.cs b/Timeline/Swagger/DefaultDescriptionOperationProcessor.cs deleted file mode 100644 index 4967cc6a..00000000 --- a/Timeline/Swagger/DefaultDescriptionOperationProcessor.cs +++ /dev/null @@ -1,39 +0,0 @@ -using NSwag.Generation.Processors;
-using NSwag.Generation.Processors.Contexts;
-using System.Collections.Generic;
-
-namespace Timeline.Swagger
-{
- /// <summary>
- /// Swagger operation processor that adds default description to response.
- /// </summary>
- public class DefaultDescriptionOperationProcessor : IOperationProcessor
- {
- private readonly Dictionary<string, string> defaultDescriptionMap = new Dictionary<string, string>
- {
- ["200"] = "Succeeded to perform the operation.",
- ["304"] = "Item does not change.",
- ["400"] = "See code and message for error info.",
- ["401"] = "You need to log in to perform this operation.",
- ["403"] = "You have no permission to perform the operation.",
- ["404"] = "Item does not exist. See code and message for error info."
- };
-
- /// <inheritdoc/>
- public bool Process(OperationProcessorContext context)
- {
- var responses = context.OperationDescription.Operation.Responses;
-
- foreach (var (httpStatusCode, res) in responses)
- {
- if (!string.IsNullOrEmpty(res.Description)) continue;
- if (defaultDescriptionMap.ContainsKey(httpStatusCode))
- {
- res.Description = defaultDescriptionMap[httpStatusCode];
- }
- }
-
- return true;
- }
- }
-}
diff --git a/Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs b/Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs deleted file mode 100644 index dc5ddd96..00000000 --- a/Timeline/Swagger/DocumentDescriptionDocumentProcessor.cs +++ /dev/null @@ -1,55 +0,0 @@ -using NSwag.Generation.Processors;
-using NSwag.Generation.Processors.Contexts;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-using Timeline.Models.Http;
-
-namespace Timeline.Swagger
-{
- public class DocumentDescriptionDocumentProcessor : IDocumentProcessor
- {
- private static Dictionary<string, int> GetAllErrorCodes()
- {
- var errorCodes = new Dictionary<string, int>();
-
- void RecursiveCheckErrorCode(Type type)
- {
- foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
- .Where(fi => fi.IsLiteral && !fi.IsInitOnly && fi.FieldType == typeof(int)))
- {
- var name = (type.FullName + "." + field.Name).Remove(0, typeof(ErrorCodes).FullName!.Length + 1).Replace("+", ".", StringComparison.OrdinalIgnoreCase);
- int value = (int)field.GetRawConstantValue()!;
- errorCodes.Add(name, value);
- }
-
- foreach (var nestedType in type.GetNestedTypes())
- {
- RecursiveCheckErrorCode(nestedType);
- }
- }
-
- RecursiveCheckErrorCode(typeof(ErrorCodes));
-
- return errorCodes;
- }
-
- public void Process(DocumentProcessorContext context)
- {
- StringBuilder description = new StringBuilder();
- description.AppendLine("# Error Codes");
- description.AppendLine("name | value");
- description.AppendLine("---- | -----");
- foreach (var (name, value) in GetAllErrorCodes())
- {
- description.AppendLine($"`{name}` | `{value}`");
- }
-
- context.Document.Info.Description = description.ToString();
- }
- }
-}
diff --git a/Timeline/Timeline.csproj b/Timeline/Timeline.csproj deleted file mode 100644 index 39412a65..00000000 --- a/Timeline/Timeline.csproj +++ /dev/null @@ -1,289 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk.Web">
- <PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
- <UserSecretsId>1f6fb74d-4277-4bc0-aeea-b1fc5ffb0b43</UserSecretsId>
- <Authors>crupest</Authors>
-
- <IsPackable>false</IsPackable>
-
- <LangVersion>8.0</LangVersion>
- <Nullable>enable</Nullable>
-
- <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
- <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
- <SpaRoot>ClientApp\</SpaRoot>
-
- <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
-
- <Version>0.3.0</Version>
- <GenerateDocumentationFile>true</GenerateDocumentationFile>
- <IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>
-
- <NoWarn>1701;1702;1591</NoWarn>
- </PropertyGroup>
-
- <ItemGroup>
- <Content Include="default-avatar.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="MockClientApp\index.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="AutoMapper" Version="10.1.1" />
- <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
- <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.9" />
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- </PackageReference>
- <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.9" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="3.1.9" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.9" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.9">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- </PackageReference>
- <PackageReference Include="NSwag.AspNetCore" Version="13.8.2" />
- <PackageReference Include="SixLabors.ImageSharp" Version="1.0.1" />
- <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\Timeline.ErrorCodes\Timeline.ErrorCodes.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <!-- Don't publish the SPA source files, but do show them in the project files list -->
- <Content Remove="$(SpaRoot)**" />
- <None Remove="$(SpaRoot)**" />
- <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
- </ItemGroup>
-
- <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
- <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn" StdOutEncoding="UTF-8" IgnoreStandardErrorWarningFormat="true" />
- <Exec WorkingDirectory="$(SpaRoot)" Command="yarn build" StdOutEncoding="UTF-8" IgnoreStandardErrorWarningFormat="true" />
-
- <!-- Include the newly-built files in the publish output -->
- <ItemGroup>
- <DistFiles Include="$(SpaRoot)dist\**" />
- <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
- <RelativePath>%(DistFiles.Identity)</RelativePath>
- <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
- <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
- </ResolvedFileToPublish>
- </ItemGroup>
- </Target>
-
- <ItemGroup>
- <None Remove="MockClientApp\index.html" />
- </ItemGroup>
-
- <ItemGroup>
- <Compile Update="Resources\Authentication\AuthHandler.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>AuthHandler.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\ControllerAuthExtensions.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>ControllerAuthExtensions.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\TimelineController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>TimelineController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\TokenController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>TokenController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\UserAvatarController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserAvatarController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Controllers\UserController.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserController.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Entities.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Entities.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Filters.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Filters.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Helper\DataCacheHelper.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>DataCacheHelper.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Messages.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Messages.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Http\Common.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Common.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Http\Exception.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Exception.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Validation\NicknameValidator.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>NicknameValidator.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Validation\NameValidator.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>NameValidator.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Models\Validation\Validator.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Validator.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\DataManager.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>DataManager.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\Exception.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Exception.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\Exceptions.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Exceptions.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\TimelineService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>TimelineService.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\UserAvatarService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserAvatarService.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\UserService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserService.resx</DependentUpon>
- </Compile>
- <Compile Update="Resources\Services\UserTokenService.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>UserTokenService.resx</DependentUpon>
- </Compile>
- </ItemGroup>
-
- <ItemGroup>
- <EmbeddedResource Update="Resources\Authentication\AuthHandler.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>AuthHandler.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\ControllerAuthExtensions.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>ControllerAuthExtensions.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\TimelineController.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>TimelineController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\TokenController.resx">
- <SubType>Designer</SubType>
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>TokenController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\UserAvatarController.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserAvatarController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Controllers\UserController.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserController.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Entities.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Entities.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Filters.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Filters.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Helper\DataCacheHelper.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>DataCacheHelper.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Messages.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Messages.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Http\Common.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Common.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Http\Exception.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Exception.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\NicknameValidator.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>NicknameValidator.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\NameValidator.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>NameValidator.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Models\Validation\Validator.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Validator.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\DataManager.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>DataManager.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\Exception.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Exception.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\Exceptions.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Exceptions.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\TimelineService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>TimelineService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserAvatarService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserAvatarService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Update="Resources\Services\UserTokenService.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>UserTokenService.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- </ItemGroup>
-</Project>
diff --git a/Timeline/appsettings.Development.json b/Timeline/appsettings.Development.json deleted file mode 100644 index a2880cbf..00000000 --- a/Timeline/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{
- "Logging": {
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
- }
- }
-}
diff --git a/Timeline/appsettings.json b/Timeline/appsettings.json deleted file mode 100644 index 804ca43a..00000000 --- a/Timeline/appsettings.json +++ /dev/null @@ -1,11 +0,0 @@ -{
- "Logging": {
- "LogLevel": {
- "Default": "Warning"
- }
- },
- "Jwt": {
- "Issuer": "api.crupest.xyz",
- "Audience": "api.crupest.xyz"
- }
-}
diff --git a/Timeline/default-avatar.png b/Timeline/default-avatar.png Binary files differdeleted file mode 100644 index 4086e1d2..00000000 --- a/Timeline/default-avatar.png +++ /dev/null diff --git a/Timeline/packages.lock.json b/Timeline/packages.lock.json deleted file mode 100644 index ed92c672..00000000 --- a/Timeline/packages.lock.json +++ /dev/null @@ -1,1563 +0,0 @@ -{
- "version": 1,
- "dependencies": {
- ".NETCoreApp,Version=v3.1": {
- "AutoMapper": {
- "type": "Direct",
- "requested": "[10.1.1, )",
- "resolved": "10.1.1",
- "contentHash": "uMgbqOdu9ZG5cIOty0C85hzzayBH2i9BthnS5FlMqKtMSHDv4ts81a2jS1VFaDBVhlBeIqJ/kQKjQY95BZde9w==",
- "dependencies": {
- "Microsoft.CSharp": "4.7.0",
- "System.Reflection.Emit": "4.7.0"
- }
- },
- "AutoMapper.Extensions.Microsoft.DependencyInjection": {
- "type": "Direct",
- "requested": "[8.1.0, )",
- "resolved": "8.1.0",
- "contentHash": "dQyGCAYcHbGuimVvCMu4Ea2S1oYOlgO9XfVdClmY5wgygJMZoS57emPzH0qNfknmtzMm4QbDO9i237W5IDjU1A==",
- "dependencies": {
- "AutoMapper": "[10.1.0, 11.0.0)",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.0",
- "Microsoft.Extensions.Options": "3.0.0"
- }
- },
- "Microsoft.AspNetCore.SpaServices.Extensions": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "ciy2GCvRnh9C22laArLsaItS+72U6Hqf4nDYShdvFgcen2ZV+NNSitb/B3vsmFfIPM8m4mf2x4T+vZ6OlI5XaA==",
- "dependencies": {
- "Microsoft.AspNetCore.SpaServices": "3.1.9",
- "Microsoft.Extensions.FileProviders.Physical": "3.1.9"
- }
- },
- "Microsoft.CodeAnalysis.FxCopAnalyzers": {
- "type": "Direct",
- "requested": "[3.3.0, )",
- "resolved": "3.3.0",
- "contentHash": "k3Icqx8kc+NrHImuiB8Jc/wd32Xeyd2B/7HOR5Qu9pyKzXQ4ikPeBAwzG2FSTuYhyIuNWvwL5k9yYBbbVz6w9w==",
- "dependencies": {
- "Microsoft.CodeAnalysis.VersionCheckAnalyzer": "[3.3.0]",
- "Microsoft.CodeQuality.Analyzers": "[3.3.0]",
- "Microsoft.NetCore.Analyzers": "[3.3.0]",
- "Microsoft.NetFramework.Analyzers": "[3.3.0]"
- }
- },
- "Microsoft.EntityFrameworkCore": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "u3A2W0BvAuAF2jgW+WX+C+Sh8sMGX5Kl1hdA0gu6A/XSrZQoW/BUP4a/q2n3iitDGndaorqjAKx+Spb9gBto+w==",
- "dependencies": {
- "Microsoft.Bcl.AsyncInterfaces": "1.1.1",
- "Microsoft.Bcl.HashCode": "1.1.0",
- "Microsoft.EntityFrameworkCore.Abstractions": "3.1.9",
- "Microsoft.EntityFrameworkCore.Analyzers": "3.1.9",
- "Microsoft.Extensions.Caching.Memory": "3.1.9",
- "Microsoft.Extensions.DependencyInjection": "3.1.9",
- "Microsoft.Extensions.Logging": "3.1.9",
- "System.Collections.Immutable": "1.7.1",
- "System.ComponentModel.Annotations": "4.7.0",
- "System.Diagnostics.DiagnosticSource": "4.7.1"
- }
- },
- "Microsoft.EntityFrameworkCore.Analyzers": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "eXGyx/Lb1fiiKtnIStdxGrfBSSQg8oZytE10f1T/2xAx12W9dKB9U9fg05cwNCDC0S2CXILsmZHYaGqCSXVAqQ=="
- },
- "Microsoft.EntityFrameworkCore.Sqlite": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "sMFCWv/1UcsFQZeGQcbfPbEZKZ1oKZqWZXTbc7PEZVMIXu82nbavstdNQ84x5IBXJkxl8iW3zjChb/FRBr5uLQ==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore.Sqlite.Core": "3.1.9",
- "SQLitePCLRaw.bundle_e_sqlite3": "2.0.2"
- }
- },
- "Microsoft.EntityFrameworkCore.Tools": {
- "type": "Direct",
- "requested": "[3.1.9, )",
- "resolved": "3.1.9",
- "contentHash": "mSgwjp0h5iqW5V49SVijR5O+kNpI1nitcbN12n9FYx/Ga6oCEFwXR/llBDesD6ASHw3Mx16vodJYJ7CEBx5rig==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore.Design": "3.1.9"
- }
- },
- "NSwag.AspNetCore": {
- "type": "Direct",
- "requested": "[13.8.2, )",
- "resolved": "13.8.2",
- "contentHash": "SNGlVSZoMyywBWueZBxl3B/nfaIM0fAcuNhTD/cfMKUn3Cn/Oi8d45HZY5vAPqczvppTbk4cZXyVwWDOfgiPbA==",
- "dependencies": {
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4",
- "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.4",
- "Microsoft.AspNetCore.StaticFiles": "1.0.4",
- "Microsoft.Extensions.ApiDescription.Server": "3.0.0",
- "Microsoft.Extensions.FileProviders.Embedded": "1.0.1",
- "NSwag.Annotations": "13.8.2",
- "NSwag.Core": "13.8.2",
- "NSwag.Generation": "13.8.2",
- "NSwag.Generation.AspNetCore": "13.8.2",
- "System.IO.FileSystem": "4.3.0",
- "System.Xml.XPath.XDocument": "4.0.1"
- }
- },
- "SixLabors.ImageSharp": {
- "type": "Direct",
- "requested": "[1.0.1, )",
- "resolved": "1.0.1",
- "contentHash": "DjLoFNdUfsDP7RhPpr5hcUhl1XiejqBML9uDWuOUwCkc0Y+sG9IJLLbqSOi9XeoWqPviwdcDm1F8nKdF0qTYIQ=="
- },
- "System.IdentityModel.Tokens.Jwt": {
- "type": "Direct",
- "requested": "[6.8.0, )",
- "resolved": "6.8.0",
- "contentHash": "5tBCjAub2Bhd5qmcd0WhR5s354e4oLYa//kOWrkX+6/7ZbDDJjMTfwLSOiZ/MMpWdE4DWPLOfTLOq/juj9CKzA==",
- "dependencies": {
- "Microsoft.IdentityModel.JsonWebTokens": "6.8.0",
- "Microsoft.IdentityModel.Tokens": "6.8.0"
- }
- },
- "Microsoft.AspNetCore.Authorization": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "cN2KJkfHcKwh82c9WGx4Tqfd2h5HflU/Mu5vYLMHON8WahHU9hE32ciIXcEIoKLNpu+zs1u1cN/qxcKTdqu89w==",
- "dependencies": {
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.Options": "1.0.2",
- "System.Security.Claims": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.Hosting.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "ybY8FOkdNfBPB5PLv1JO+It/94ftBzGUI1WqU4XySbIWyhw2TPmmKAUuO9uvJoR0qpsFup8FJz6trsBcBITg9w==",
- "dependencies": {
- "Microsoft.AspNetCore.Hosting.Server.Abstractions": "1.0.4",
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "Microsoft.Extensions.Configuration.Abstractions": "1.0.2",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.2",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2"
- }
- },
- "Microsoft.AspNetCore.Hosting.Server.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "XUiQPe/CflK1i0Voo9S6/G1iQh00gQ6sMqi3LRtKeceBbO6AOostaAUdhjyME92MapI4VFNl+Z+/KXUlMAExJQ==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Features": "1.0.3",
- "Microsoft.Extensions.Configuration.Abstractions": "1.0.2"
- }
- },
- "Microsoft.AspNetCore.Http": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "kfNOIGGgVtMzsSWZzXBqz5zsdo8ssBa90YHzZt95N8ARGXoolSaBHy6yBoMm/XcpbXM+m/x1fixTTMIWMgzJdQ==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "Microsoft.AspNetCore.WebUtilities": "1.0.3",
- "Microsoft.Extensions.ObjectPool": "1.0.1",
- "Microsoft.Extensions.Options": "1.0.2",
- "Microsoft.Net.Http.Headers": "1.0.3",
- "System.Buffers": "4.0.0",
- "System.Threading": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Http.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "nnjvAf7ag6P0DyD/0nhRGjLpv+3DkPU0juF8aQh46X8uF4kzjJdrh65oL+4PVOu3K6BgSg6OVUs0QC0SE0FRtg==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Features": "1.0.3",
- "System.Globalization.Extensions": "4.0.1",
- "System.Linq.Expressions": "4.1.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Text.Encodings.Web": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.Http.Extensions": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "+7Sd+14nexIJqcB4S1Eur9kzeMZ5CBtrxkei+PNbD78fg8vO3+TcCgrl5SBNTsUB/VJAfD/s0fgs5t+hHRj2Pg==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Net.Http.Headers": "1.0.3",
- "System.Buffers": "4.0.0",
- "System.IO.FileSystem": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.Http.Features": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "Ihq57tseNyPbJTmFXY4jQ4JkxLP0lh45VRwocQci/sFx+qcJGvWB+sJJ2/YPLy/qTWFAEfNAcswuY3OsNH9Gwg==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "1.0.1",
- "System.Collections": "4.0.11",
- "System.ComponentModel": "4.0.1",
- "System.Linq": "4.1.0",
- "System.Net.Primitives": "4.0.11",
- "System.Net.WebSockets": "4.0.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Security.Claims": "4.0.1",
- "System.Security.Cryptography.X509Certificates": "4.1.0",
- "System.Security.Principal": "4.0.1"
- }
- },
- "Microsoft.AspNetCore.JsonPatch": {
- "type": "Transitive",
- "resolved": "1.0.0",
- "contentHash": "WVaSVS+dDlWCR/qerHnBxU9tIeJ9GMA3M5tg4cxH7/cJYZZLnr2zvaFHGB+cRRNCKKTJ0pFRxT7ES8knhgAAaA==",
- "dependencies": {
- "Microsoft.CSharp": "4.0.1",
- "Newtonsoft.Json": "9.0.1",
- "System.Collections.Concurrent": "4.0.12",
- "System.ComponentModel.TypeConverter": "4.1.0",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Serialization.Primitives": "4.1.1",
- "System.Text.Encoding.Extensions": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Mvc.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "Isqgif1nuB+um86cEkpL8KnoxFCUCXBsbs9PuiuzElvlSiv4Ek3LvtrSUcbivekDDfys8CDbJhxwEI7WKJieAQ==",
- "dependencies": {
- "Microsoft.AspNetCore.Routing.Abstractions": "1.0.4",
- "Microsoft.CSharp": "4.0.1",
- "Microsoft.Net.Http.Headers": "1.0.3",
- "System.ComponentModel.TypeConverter": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Text.Encoding.Extensions": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Mvc.ApiExplorer": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "ujCFTM42U2WKUBhdaoLoiI+wVHgYhrmDrkl5+hWJ7EJW4fhp42w4cRZ97tjuveWr+M6JZjpS0q+7PVofQzFUiw==",
- "dependencies": {
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4"
- }
- },
- "Microsoft.AspNetCore.Mvc.Core": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "1ukcttN1+T82hWXE8WS5kawkruolKI6LPVqVI4rTzN16kFszS/UqTrcwSUEnmTRpmWgFo665V3c2GpdQ9B6znw==",
- "dependencies": {
- "Microsoft.AspNetCore.Authorization": "1.0.3",
- "Microsoft.AspNetCore.Hosting.Abstractions": "1.0.3",
- "Microsoft.AspNetCore.Http": "1.0.3",
- "Microsoft.AspNetCore.Mvc.Abstractions": "1.0.4",
- "Microsoft.AspNetCore.Routing": "1.0.4",
- "Microsoft.Extensions.DependencyModel": "1.0.0",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.PlatformAbstractions": "1.0.0",
- "System.Buffers": "4.0.0",
- "System.Diagnostics.DiagnosticSource": "4.0.0",
- "System.Text.Encoding": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.Mvc.Formatters.Json": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "i8WWK2GwlBHfOL+d+kknJWPks6DS9tbN6nfJZU4yb+/wfUAYd311B2CIHzdat3IewubnK1TYONwrhQcs2FbLeA==",
- "dependencies": {
- "Microsoft.AspNetCore.JsonPatch": "1.0.0",
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4"
- }
- },
- "Microsoft.AspNetCore.NodeServices": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "bbd3FlSPWiRQrIcBLa5TaOvo4gjmmiNMkxA8VmZ6u0eIpS0Yj35/eTopaGdtzqwlqj5jXbdRoib1MruXuPaW8A==",
- "dependencies": {
- "Microsoft.Extensions.Logging.Console": "3.1.9",
- "Newtonsoft.Json": "12.0.2"
- }
- },
- "Microsoft.AspNetCore.Routing": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "mdIF3ckRothHWuCSFkk6YXACj5zxi5qM+cEAHjcpP04/wCHUoV0gGVnW+HI+LyFXE6JUwu2zXn5tfsCpW0U+SA==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Extensions": "1.0.3",
- "Microsoft.AspNetCore.Routing.Abstractions": "1.0.4",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.ObjectPool": "1.0.1",
- "Microsoft.Extensions.Options": "1.0.2",
- "System.Collections": "4.0.11",
- "System.Text.RegularExpressions": "4.1.0"
- }
- },
- "Microsoft.AspNetCore.Routing.Abstractions": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "GHxVt6LlXHFsCUd2Un+/vY1tBTXxnogfbDO0b8G5EGmkapSK+dOGOLJviscxQkp338Uabs081JEIdkRymI5GXA==",
- "dependencies": {
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.3",
- "System.Collections.Concurrent": "4.0.12",
- "System.Reflection.Extensions": "4.0.1",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "Microsoft.AspNetCore.SpaServices": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "Fb+N2ZyF1wNrGeWggT+Ovv6W8AAVxfi4V/SnuEsBOR+nmkFhty9zyh6IDRRS98GJK6OE3adqqPbWMtJqbxYnNA==",
- "dependencies": {
- "Microsoft.AspNetCore.NodeServices": "3.1.9"
- }
- },
- "Microsoft.AspNetCore.StaticFiles": {
- "type": "Transitive",
- "resolved": "1.0.4",
- "contentHash": "2pNvwewAazhaaCdw2CGUvIcDrNQMlqP57JgBDf3v+pRj1rZ29HVnpvkX6a+TrmRYlJNmmxHOKEt468uE/gDcFw==",
- "dependencies": {
- "Microsoft.AspNetCore.Hosting.Abstractions": "1.0.4",
- "Microsoft.AspNetCore.Http.Extensions": "1.0.3",
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.2",
- "Microsoft.Extensions.WebEncoders": "1.0.3"
- }
- },
- "Microsoft.AspNetCore.WebUtilities": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "snSGNs5EEisqivDjDiskFkFyu+DV2Ib9sMPOBQKtoFwI5H1W5YNB/rIVqDZQL16zj/uzdwwxrdE/5xhkVyf6gQ==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "1.0.1",
- "System.Buffers": "4.0.0",
- "System.Collections": "4.0.11",
- "System.IO": "4.1.0",
- "System.IO.FileSystem": "4.0.1",
- "System.Text.Encodings.Web": "4.0.1"
- }
- },
- "Microsoft.Bcl.AsyncInterfaces": {
- "type": "Transitive",
- "resolved": "1.1.1",
- "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w=="
- },
- "Microsoft.Bcl.HashCode": {
- "type": "Transitive",
- "resolved": "1.1.0",
- "contentHash": "J2G1k+u5unBV+aYcwxo94ip16Rkp65pgWFb0R6zwJipzWNMgvqlWeuI7/+R+e8bob66LnSG+llLJ+z8wI94cHg=="
- },
- "Microsoft.CodeAnalysis.VersionCheckAnalyzer": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "xjLM3DRFZMan3nQyBQEM1mBw6VqQybi4iMJhMFW6Ic1E1GCvqJR3ABOwEL7WtQjDUzxyrGld9bASnAos7G/Xyg=="
- },
- "Microsoft.CodeQuality.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "zZ3miq6u22UFQKhfJyLnVEJ+DgeOopLh3eKJnKAcOetPP2hiv3wa7kHZlBDeTvtqJQiAQhAVbttket8XxjN1zw=="
- },
- "Microsoft.CSharp": {
- "type": "Transitive",
- "resolved": "4.7.0",
- "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
- },
- "Microsoft.Data.Sqlite.Core": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "+u4PeT1npi2EzhxGc5r1Z2z73zuXw+TlKVZm44WQhNCUw4LtUVDaxGSpUhrjW+X4snBCBfr4kT/uJyKnL4R4og==",
- "dependencies": {
- "SQLitePCLRaw.core": "2.0.2"
- }
- },
- "Microsoft.DotNet.PlatformAbstractions": {
- "type": "Transitive",
- "resolved": "3.1.6",
- "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
- },
- "Microsoft.EntityFrameworkCore.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "IR6Y4RJVlw0QXdWXjF3Kx9s1QLiicJus+BFBKr43lBtriV20j3yrWMoaZ9W1AUUgnicZXpXVcNfklqtmwb9Sxw=="
- },
- "Microsoft.EntityFrameworkCore.Design": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "2zgP7BWcw5nqGQiT4bEtiI6ras+4pvKg5D+tA3AYvjEifzzaWvmRTb3B9nRHpIYJAhPtmWNBVnVXLbu3fS1OYA==",
- "dependencies": {
- "Microsoft.CSharp": "4.7.0",
- "Microsoft.EntityFrameworkCore.Relational": "3.1.9"
- }
- },
- "Microsoft.EntityFrameworkCore.Relational": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "7fhWuSfrCYlv/hvOX5OhbFJF/G9f8sifqTrJiYnAYLDOvNizwv7t9tFPD8JwaF3zM2S54O5/Vni2NxvwzSaW2w==",
- "dependencies": {
- "Microsoft.EntityFrameworkCore": "3.1.9"
- }
- },
- "Microsoft.EntityFrameworkCore.Sqlite.Core": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "Da6h8LdpJwKc1az9DMWt2Mt6gHXPRZqwiumV1Zx0AuM3EThyokVDzBGy2sti0AcBhcQMLJHPEr5R9xuiWvaYYQ==",
- "dependencies": {
- "Microsoft.Data.Sqlite.Core": "3.1.9",
- "Microsoft.DotNet.PlatformAbstractions": "3.1.6",
- "Microsoft.EntityFrameworkCore.Relational": "3.1.9",
- "Microsoft.Extensions.DependencyModel": "3.1.6"
- }
- },
- "Microsoft.Extensions.ApiDescription.Server": {
- "type": "Transitive",
- "resolved": "3.0.0",
- "contentHash": "LH4OE/76F6sOCslif7+Xh3fS/wUUrE5ryeXAMcoCnuwOQGT5Smw0p57IgDh/pHgHaGz/e+AmEQb7pRgb++wt0w=="
- },
- "Microsoft.Extensions.Caching.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "/2QsPAsUZD4qvftZkUKHRRRryPDXWh606/iNXPLrulwHLMr9JNsKBJWVqylT3qU92nJok5VoqSblkY9mSyxFyg==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.Caching.Memory": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "/JrVMVetX/kpJQUIlJ6NLQ3zbF0yyryXpo4+uFCqYIUZzgmWk8DS/zSKcyj1tQ3410+vhDEAPngxC+hg0IlJeg==",
- "dependencies": {
- "Microsoft.Extensions.Caching.Abstractions": "3.1.9",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9",
- "Microsoft.Extensions.Logging.Abstractions": "3.1.9",
- "Microsoft.Extensions.Options": "3.1.9"
- }
- },
- "Microsoft.Extensions.Configuration": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "lqdkOGNeTMKG981Q7yWGlRiFbIlsRwTlMMiybT+WOzUCFBS/wc25tZgh7Wm/uRoBbWefgvokzmnea7ZjmFedmA==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "3.1.9"
- }
- },
- "Microsoft.Extensions.Configuration.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "vOJxPKczaHpXeZFrxARxYwsEulhEouXc5aZGgMdkhV/iEXX9/pfjqKk76rTG+4CsJjHV+G/4eMhvOIaQMHENNA==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.Configuration.Binder": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "BG6HcT7tARYakftqfQu+cLksgIWG1NdxMY+igI12hdZrUK+WjS973NiRyuao/U9yyTeM9NPwRnC61hCmG3G3jg==",
- "dependencies": {
- "Microsoft.Extensions.Configuration": "3.1.9"
- }
- },
- "Microsoft.Extensions.DependencyInjection": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "ORqfrAACcvTInie1oGola5uky344/PiNfgayTPuZWV4WnSfIQZJQm/ZLpGshJE3h7TqwYaYElGazK/yaM2bFLA==",
- "dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9"
- }
- },
- "Microsoft.Extensions.DependencyInjection.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "8PkcaPwiTPOhqshoY4+rQUbz86X6YpLDLUqXOezh7L2A3pgpBmeBBByYIffofBlvQxDdQ0zB2DkWjbZWyCxRWg=="
- },
- "Microsoft.Extensions.DependencyModel": {
- "type": "Transitive",
- "resolved": "3.1.6",
- "contentHash": "/UlDKULIVkLQYn1BaHcy/rc91ApDxJb7T75HcCbGdqwvxhnRQRKM2di1E70iCPMF9zsr6f4EgQTotBGxFIfXmw==",
- "dependencies": {
- "System.Text.Json": "4.7.2"
- }
- },
- "Microsoft.Extensions.FileProviders.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "Q4SGwEFZKiZbpzPgdGbQUULxtcH1zXMOwCPKSm6QwVcOCGshf3QLfBh+O/GyFH4B0RfZ16nKyeW1mMONlRyjUw==",
- "dependencies": {
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.FileProviders.Embedded": {
- "type": "Transitive",
- "resolved": "1.0.1",
- "contentHash": "nSEa8bH3fVdTYGqK4twOKLxxgKIW3cz9g9mrzhPh/CmdvGJWKRTIlBIZi7lz+lqNQpxean5vbAo84R/mU+JpGA==",
- "dependencies": {
- "Microsoft.Extensions.FileProviders.Abstractions": "1.0.1",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "Microsoft.Extensions.FileProviders.Physical": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "HWDSsblTCQp7EEJJmnLzttIhFGzDu+DGqBbOvGCdFT0+pkCuBkn3EiWpEEcm5WMTO5njmsbLSK9ZuUUf2zPsFg==",
- "dependencies": {
- "Microsoft.Extensions.FileProviders.Abstractions": "3.1.9",
- "Microsoft.Extensions.FileSystemGlobbing": "3.1.9"
- }
- },
- "Microsoft.Extensions.FileSystemGlobbing": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "5bnewG1aBiSESPNwcXGIxDDRN95uqdy+fqZZ8Z63Et5rRNlAwAfXHOrg+FTht7UjHobjvtjzquMCbAWhWEPHIw=="
- },
- "Microsoft.Extensions.Logging": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "+V3i0jCQCO6IIOf6e+fL0SqrZd2x/Krug9EEL1JHa9R03RsbEpltCtjVY5hxedyuyuQKwvLoR12sCfu/9XEUAw==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Binder": "3.1.9",
- "Microsoft.Extensions.DependencyInjection": "3.1.9",
- "Microsoft.Extensions.Logging.Abstractions": "3.1.9",
- "Microsoft.Extensions.Options": "3.1.9"
- }
- },
- "Microsoft.Extensions.Logging.Abstractions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "W5fbF8qVR9SMVVJqDQLIR7meWbev6Pu/lbrm7LDNr4Sp7HOotr4k2UULTdFSXOi5aoDdkQZpWnq0ZSpjrR3tjg=="
- },
- "Microsoft.Extensions.Logging.Configuration": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "hv6XsGgikrbkolBJdF1usl9R/nrliC5mifMqHMEY9zWcCLwNkXMJiS8p0lbosrnpVAMi4PbNx39DB51Dqscd0w==",
- "dependencies": {
- "Microsoft.Extensions.Logging": "3.1.9",
- "Microsoft.Extensions.Options.ConfigurationExtensions": "3.1.9"
- }
- },
- "Microsoft.Extensions.Logging.Console": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "8Dusl1rkDivmvLrwj6QAo917xMHPiDBzG3IG3agiyDdtsC/fRp+1VN5iIN+O09PtEaMged2OLA6wCDwfSTSTZw==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "3.1.9",
- "Microsoft.Extensions.Logging": "3.1.9",
- "Microsoft.Extensions.Logging.Configuration": "3.1.9"
- }
- },
- "Microsoft.Extensions.ObjectPool": {
- "type": "Transitive",
- "resolved": "1.0.1",
- "contentHash": "pJMOnxuqmG37OjccfvtqVoo3bQGoN+0EJUzzp7+2uxSdioER82caAk6Yi/z5aysapn5XENNIIa7SaYnYKSS69A==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "Microsoft.Extensions.Options": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "EIb3G1DL+Rl9MvJR7LjI1wCy2nfTN4y8MflbOftn1HLYQBj/Rwl8kUbGTrSFE01c99Wm4ETjWVsjqKcpFvhPng==",
- "dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9",
- "Microsoft.Extensions.Primitives": "3.1.9"
- }
- },
- "Microsoft.Extensions.Options.ConfigurationExtensions": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "u5jh7RW+Ev81YqK1ZoBG0lftp2MA9xqXiTiRL46XzaPj2ScNUyiVbzcVY0fPbE27UOpT2hj+yPzRSOMIIo55UA==",
- "dependencies": {
- "Microsoft.Extensions.Configuration.Abstractions": "3.1.9",
- "Microsoft.Extensions.Configuration.Binder": "3.1.9",
- "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.9",
- "Microsoft.Extensions.Options": "3.1.9"
- }
- },
- "Microsoft.Extensions.PlatformAbstractions": {
- "type": "Transitive",
- "resolved": "1.0.0",
- "contentHash": "zyjUzrOmuevOAJpIo3Mt5GmpALVYCVdLZ99keMbmCxxgQH7oxzU58kGHzE6hAgYEiWsdfMJLjVR7r+vSmaJmtg==",
- "dependencies": {
- "System.AppContext": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "Microsoft.Extensions.Primitives": {
- "type": "Transitive",
- "resolved": "3.1.9",
- "contentHash": "IrHecH0eGG7/XoeEtv++oLg/sJHRNyeCqlA9RhAo6ig4GpOTjtDr32sBMYuuLtUq8ALahneWkrOzoBAwJ4L4iA=="
- },
- "Microsoft.Extensions.WebEncoders": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "TClNvczWRxF6bVPhn5EK3Y3QNi5jTP68Qur+5Fk+MQLPeBI18WN7X145DDJ6bFeNOwgdCHl73lHs5uZp9ish1A==",
- "dependencies": {
- "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.2",
- "Microsoft.Extensions.Options": "1.0.2",
- "System.Text.Encodings.Web": "4.0.1"
- }
- },
- "Microsoft.IdentityModel.JsonWebTokens": {
- "type": "Transitive",
- "resolved": "6.8.0",
- "contentHash": "+7JIww64PkMt7NWFxoe4Y/joeF7TAtA/fQ0b2GFGcagzB59sKkTt/sMZWR6aSZht5YC7SdHi3W6yM1yylRGJCQ==",
- "dependencies": {
- "Microsoft.IdentityModel.Tokens": "6.8.0"
- }
- },
- "Microsoft.IdentityModel.Logging": {
- "type": "Transitive",
- "resolved": "6.8.0",
- "contentHash": "Rfh/p4MaN4gkmhPxwbu8IjrmoDncGfHHPh1sTnc0AcM/Oc39/fzC9doKNWvUAjzFb8LqA6lgZyblTrIsX/wDXg=="
- },
- "Microsoft.IdentityModel.Tokens": {
- "type": "Transitive",
- "resolved": "6.8.0",
- "contentHash": "gTqzsGcmD13HgtNePPcuVHZ/NXWmyV+InJgalW/FhWpII1D7V1k0obIseGlWMeA4G+tZfeGMfXr0klnWbMR/mQ==",
- "dependencies": {
- "Microsoft.CSharp": "4.5.0",
- "Microsoft.IdentityModel.Logging": "6.8.0",
- "System.Security.Cryptography.Cng": "4.5.0"
- }
- },
- "Microsoft.Net.Http.Headers": {
- "type": "Transitive",
- "resolved": "1.0.3",
- "contentHash": "2F8USh4hR5xppvaxtw2EStX74Ih+HhRj7aQD1uaB9JmTGy478F7t4VU+IdZXauEDrvS7LYAyyhmOExsUFK3PAw==",
- "dependencies": {
- "System.Buffers": "4.0.0",
- "System.Collections": "4.0.11",
- "System.Diagnostics.Contracts": "4.0.1",
- "System.Globalization": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime.Extensions": "4.1.0",
- "System.Text.Encoding": "4.0.11"
- }
- },
- "Microsoft.NetCore.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "6qptTHUu1Wfszuf83NhU0IoAb4j7YWOpJs6oc6S4G/nI6aGGWKH/Xi5Vs9L/8lrI74ijEEzPcIwafSQW5ASHtA=="
- },
- "Microsoft.NETCore.Platforms": {
- "type": "Transitive",
- "resolved": "1.1.0",
- "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
- },
- "Microsoft.NETCore.Targets": {
- "type": "Transitive",
- "resolved": "1.1.0",
- "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
- },
- "Microsoft.NetFramework.Analyzers": {
- "type": "Transitive",
- "resolved": "3.3.0",
- "contentHash": "JTfMic5fEFWICePbr7GXOGPranqS9Qxu2U/BZEcnnGbK1SFW8TxRyGp6O1L52xsbfOdqmzjc0t5ubhDrjj+Xpg=="
- },
- "Microsoft.Win32.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "fQnBHO9DgcmkC9dYSJoBqo6sH1VJwJprUHh8F3hbcRlxiQiBUuTntdk8tUwV490OqC2kQUrinGwZyQHTieuXRA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "Namotion.Reflection": {
- "type": "Transitive",
- "resolved": "1.0.14",
- "contentHash": "wuJGiFvGfehH2w7jAhMbCJt0/rvUuHyqSZn0sMhNTviDfBZRyX8LFlR/ndQcofkGWulPDfH5nKYTeGXE8xBHPA==",
- "dependencies": {
- "Microsoft.CSharp": "4.3.0"
- }
- },
- "Newtonsoft.Json": {
- "type": "Transitive",
- "resolved": "12.0.2",
- "contentHash": "rTK0s2EKlfHsQsH6Yx2smvcTCeyoDNgCW7FEYyV01drPlh2T243PR2DiDXqtC5N4GDm4Ma/lkxfW5a/4793vbA=="
- },
- "NJsonSchema": {
- "type": "Transitive",
- "resolved": "10.2.1",
- "contentHash": "/BtWbYTusyoSgQkCB4eYijMfZotB/rfASDsl1k9evlkm5vlOP4s4Y09TOzBChU77d/qUABVYL1Xf+TB8E0Wfpw==",
- "dependencies": {
- "Namotion.Reflection": "1.0.14",
- "Newtonsoft.Json": "9.0.1"
- }
- },
- "NSwag.Annotations": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "/GO+35CjPYQTPS5/Q8udM5JAMEWVo8JsrkV2Uw3OW4/AJU9iOS7t6WJid6ZlkpLMjnW7oex9mvJ2EZNE4eOG/Q=="
- },
- "NSwag.Core": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "Hm6pU9qFJuXLo3b27+JTXztfeuI/15Ob1sDsfUu4rchN0+bMogtn8Lia8KVbcalw/M+hXc0rWTFp5ueP23e+iA==",
- "dependencies": {
- "NJsonSchema": "10.2.1",
- "Newtonsoft.Json": "9.0.1"
- }
- },
- "NSwag.Generation": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "LBIrpHFRZeMMbqL1hdyGb7r8v+T52aOCARxwfAmzE+MlOHVpjsIxyNSXht9EzBFMbSH0tj7CK2Ugo7bm+zUssg==",
- "dependencies": {
- "NJsonSchema": "10.2.1",
- "NSwag.Core": "13.8.2",
- "Newtonsoft.Json": "9.0.1"
- }
- },
- "NSwag.Generation.AspNetCore": {
- "type": "Transitive",
- "resolved": "13.8.2",
- "contentHash": "0ydVv6OidspZ/MS6qmU8hswGtXwq5YZPg+2a2PHGD6jNp2Fef4j1wC3xa3hplDAq7cK+BgpyDKtvj9+X01+P5g==",
- "dependencies": {
- "Microsoft.AspNetCore.Mvc.ApiExplorer": "1.0.4",
- "Microsoft.AspNetCore.Mvc.Core": "1.0.4",
- "Microsoft.AspNetCore.Mvc.Formatters.Json": "1.0.4",
- "NJsonSchema": "10.2.1",
- "NSwag.Generation": "13.8.2"
- }
- },
- "runtime.native.System": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "QfS/nQI7k/BLgmLrw7qm7YBoULEvgWnPI+cYsbfCVFTW8Aj+i8JhccxcFMu1RWms0YZzF+UHguNBK4Qn89e2Sg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1"
- }
- },
- "runtime.native.System.Net.Http": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "Nh0UPZx2Vifh8r+J+H2jxifZUD3sBrmolgiFWJd2yiNrxO0xTa6bAw3YwRn1VOiSen/tUXMS31ttNItCZ6lKuA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1"
- }
- },
- "runtime.native.System.Security.Cryptography": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "2CQK0jmO6Eu7ZeMgD+LOFbNJSXHFVQbCJJkEyEwowh1SCgYnrn9W9RykMfpeeVGw7h4IBvYikzpGUlmZTUafJw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1"
- }
- },
- "SQLitePCLRaw.bundle_e_sqlite3": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "OVPI/nh5AqfLCIKhAYqjCa6AHhc7oKApGcGM3UhMRSerFiBx58nSpGwxVFdMgjOCWZR+fA49nzsnKlWp5hFo8w==",
- "dependencies": {
- "SQLitePCLRaw.core": "2.0.2",
- "SQLitePCLRaw.lib.e_sqlite3": "2.0.2",
- "SQLitePCLRaw.provider.dynamic_cdecl": "2.0.2"
- }
- },
- "SQLitePCLRaw.core": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "TFSBX426OelS1tkaVC254NVVlrJIe9YLhWPkEvuqJj2104QpmDmEYOhfdfDJD1E/2SmqDhoRw1ek5cQHj8olcQ==",
- "dependencies": {
- "System.Memory": "4.5.3"
- }
- },
- "SQLitePCLRaw.lib.e_sqlite3": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "S+Tsqe/M7wsc+9HeediI6UHtBKf2X586aRwhi1aBVLGe0WxkAo52O9ZxwEy/v8XMLefcrEMupd2e9CDlIT6QCw=="
- },
- "SQLitePCLRaw.provider.dynamic_cdecl": {
- "type": "Transitive",
- "resolved": "2.0.2",
- "contentHash": "ZSwacbKJUsxJEZxwT23uZVrGbaIvXcADZDz5Sr66fikO5eehdcceDncjzwzTzWfW13di8gpTpstx3WJSt/Ci5Q==",
- "dependencies": {
- "SQLitePCLRaw.core": "2.0.2"
- }
- },
- "System.AppContext": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "3QjO4jNV7PdKkmQAVp9atA+usVnKRwI3Kx1nMwJ93T0LcQfx7pKAYk0nKz5wn1oP5iqlhZuy6RXOFdhr7rDwow==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.Buffers": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "msXumHfjjURSkvxUjYuq4N2ghHoRi2VpXcKMA7gK6ujQfU3vGpl+B6ld0ATRg+FZFpRyA6PgEPA+VlIkTeNf2w==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Diagnostics.Tracing": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Collections": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Collections.Concurrent": {
- "type": "Transitive",
- "resolved": "4.0.12",
- "contentHash": "2gBcbb3drMLgxlI0fBfxMA31ec6AEyYCHygGse4vxceJan8mRIWeKJ24BFzN7+bi/NFTgdIgufzb94LWO5EERQ==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Diagnostics.Tracing": "4.1.0",
- "System.Globalization": "4.0.11",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Collections.Immutable": {
- "type": "Transitive",
- "resolved": "1.7.1",
- "contentHash": "B43Zsz5EfMwyEbnObwRxW5u85fzJma3lrDeGcSAV1qkhSRTNY5uXAByTn9h9ddNdhM+4/YoLc/CI43umjwIl9Q=="
- },
- "System.Collections.NonGeneric": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Collections.Specialized": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "/HKQyVP0yH1I0YtK7KJL/28snxHNH/bi+0lgk/+MbURF6ULhAE31MDI+NZDerNWu264YbxklXCCygISgm+HMug==",
- "dependencies": {
- "System.Collections.NonGeneric": "4.0.1",
- "System.Globalization": "4.0.11",
- "System.Globalization.Extensions": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.ComponentModel": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.ComponentModel.Annotations": {
- "type": "Transitive",
- "resolved": "4.7.0",
- "contentHash": "0YFqjhp/mYkDGpU0Ye1GjE53HMp9UVfGN7seGpAMttAC0C40v5gw598jCgpbBLMmCo0E5YRLBv5Z2doypO49ZQ=="
- },
- "System.ComponentModel.Primitives": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "sc/7eVCdxPrp3ljpgTKVaQGUXiW05phNWvtv/m2kocXqrUQvTVWKou1Edas2aDjTThLPZOxPYIGNb/HN0QjURg==",
- "dependencies": {
- "System.ComponentModel": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.ComponentModel.TypeConverter": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "MnDAlaeJZy9pdB5ZdOlwdxfpI+LJQ6e0hmH7d2+y2LkiD8DRJynyDYl4Xxf3fWFm7SbEwBZh4elcfzONQLOoQw==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Collections.NonGeneric": "4.0.1",
- "System.Collections.Specialized": "4.0.1",
- "System.ComponentModel": "4.0.1",
- "System.ComponentModel.Primitives": "4.1.0",
- "System.Globalization": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Reflection.Extensions": "4.0.1",
- "System.Reflection.Primitives": "4.0.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Diagnostics.Contracts": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "HvQQjy712vnlpPxaloZYkuE78Gn353L0SJLJVeLcNASeg9c4qla2a1Xq8I7B3jZoDzKPtHTkyVO7AZ5tpeQGuA==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.Diagnostics.Debug": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Diagnostics.DiagnosticSource": {
- "type": "Transitive",
- "resolved": "4.7.1",
- "contentHash": "j81Lovt90PDAq8kLpaJfJKV/rWdWuEk6jfV+MBkee33vzYLEUsy4gXK8laa9V2nZlLM9VM9yA/OOQxxPEJKAMw=="
- },
- "System.Diagnostics.Tools": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "xBfJ8pnd4C17dWaC9FM6aShzbJcRNMChUMD42I6772KGGrqaFdumwhn9OdM68erj1ueNo3xdQ1EwiFjK5k8p0g==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Diagnostics.Tracing": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "vDN1PoMZCkkdNjvZLql592oYJZgS7URcJzJ7bxeBgGtx5UtR5leNm49VmfHGqIffX4FKacHbI3H6UyNSHQknBg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Globalization": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Globalization.Calendars": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "L1c6IqeQ88vuzC1P81JeHmHA8mxq8a18NUBNXnIY/BVb+TCyAaGIFbhpZt60h9FJNmisymoQkHEFSE9Vslja1Q==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Globalization": "4.0.11",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Globalization.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "KKo23iKeOaIg61SSXwjANN7QYDr/3op3OWGGzDzz7mypx0Za0fZSeG0l6cco8Ntp8YMYkIQcAqlk8yhm5/Uhcg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.InteropServices": "4.1.0"
- }
- },
- "System.IO": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0",
- "System.Text.Encoding": "4.3.0",
- "System.Threading.Tasks": "4.3.0"
- }
- },
- "System.IO.FileSystem": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.IO": "4.3.0",
- "System.IO.FileSystem.Primitives": "4.3.0",
- "System.Runtime": "4.3.0",
- "System.Runtime.Handles": "4.3.0",
- "System.Text.Encoding": "4.3.0",
- "System.Threading.Tasks": "4.3.0"
- }
- },
- "System.IO.FileSystem.Primitives": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==",
- "dependencies": {
- "System.Runtime": "4.3.0"
- }
- },
- "System.Linq": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "bQ0iYFOQI0nuTnt+NQADns6ucV4DUvMdwN6CbkB1yj8i7arTGiTN5eok1kQwdnnNWSDZfIUySQY+J3d5KjWn0g==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "System.Linq.Expressions": {
- "type": "Transitive",
- "resolved": "4.1.1",
- "contentHash": "bXwi8FrK/XIGPvtk1ZnawffhqLPyacj7dZnbFaV52YGaQigNqGEzNAByAIvL9FlEe3TCzoInorHF91IK//Q3Xg==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Linq": "4.1.0",
- "System.ObjectModel": "4.0.12",
- "System.Reflection": "4.1.0",
- "System.Reflection.Emit": "4.0.1",
- "System.Reflection.Emit.ILGeneration": "4.0.1",
- "System.Reflection.Emit.Lightweight": "4.0.1",
- "System.Reflection.Extensions": "4.0.1",
- "System.Reflection.Primitives": "4.0.1",
- "System.Reflection.TypeExtensions": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Memory": {
- "type": "Transitive",
- "resolved": "4.5.3",
- "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
- },
- "System.Net.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "hVvfl4405DRjA2408luZekbPhplJK03j2Y2lSfMlny7GHXlkByw1iLnc9mgKW0GdQn73vvMcWrWewAhylXA4Nw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Handles": "4.0.1"
- }
- },
- "System.Net.WebSockets": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==",
- "dependencies": {
- "Microsoft.Win32.Primitives": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.ObjectModel": {
- "type": "Transitive",
- "resolved": "4.0.12",
- "contentHash": "tAgJM1xt3ytyMoW4qn4wIqgJYm7L7TShRZG4+Q4Qsi2PCcj96pXN7nRywS9KkB3p/xDUjc2HSwP9SROyPYDYKQ==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Reflection": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.IO": "4.1.0",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Emit": {
- "type": "Transitive",
- "resolved": "4.7.0",
- "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ=="
- },
- "System.Reflection.Emit.ILGeneration": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "Ov6dU8Bu15Bc7zuqttgHF12J5lwSWyTf1S+FJouUXVMSqImLZzYaQ+vRr1rQ0OZ0HqsrwWl4dsKHELckQkVpgA==",
- "dependencies": {
- "System.Reflection": "4.1.0",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Emit.Lightweight": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "sSzHHXueZ5Uh0OLpUQprhr+ZYJrLPA2Cmr4gn0wj9+FftNKXx8RIMKvO9qnjk2ebPYUjZ+F2ulGdPOsvj+MEjA==",
- "dependencies": {
- "System.Reflection": "4.1.0",
- "System.Reflection.Emit.ILGeneration": "4.0.1",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "GYrtRsZcMuHF3sbmRHfMYpvxZoIN2bQGrYGerUiWLEkqdEUQZhH3TRSaC/oI4wO0II1RKBPlpIa1TOMxIcOOzQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Reflection": "4.1.0",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Reflection.TypeExtensions": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "tsQ/ptQ3H5FYfON8lL4MxRk/8kFyE0A+tGPXmVP967cT/gzLHYxIejIYSxp4JmIeFHVP78g/F2FE1mUUTbDtrg==",
- "dependencies": {
- "System.Reflection": "4.1.0",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Resources.ResourceManager": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Globalization": "4.0.11",
- "System.Reflection": "4.1.0",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Runtime": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0"
- }
- },
- "System.Runtime.Extensions": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Runtime.Handles": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0"
- }
- },
- "System.Runtime.InteropServices": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Reflection": "4.1.0",
- "System.Reflection.Primitives": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Handles": "4.0.1"
- }
- },
- "System.Runtime.Numerics": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "+XbKFuzdmLP3d1o9pdHu2nxjNr2OEPqGzKeegPLCUMM71a0t50A/rOcIRmGs9wR7a8KuHX6hYs/7/TymIGLNqg==",
- "dependencies": {
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0"
- }
- },
- "System.Runtime.Serialization.Primitives": {
- "type": "Transitive",
- "resolved": "4.1.1",
- "contentHash": "HZ6Du5QrTG8MNJbf4e4qMO3JRAkIboGT5Fk804uZtg3Gq516S7hAqTm2UZKUHa7/6HUGdVy3AqMQKbns06G/cg==",
- "dependencies": {
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0"
- }
- },
- "System.Security.Claims": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "4Jlp0OgJLS/Voj1kyFP6MJlIYp3crgfH8kNQk2p7+4JYfc1aAmh9PZyAMMbDhuoolGNtux9HqSOazsioRiDvCw==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Security.Principal": "4.0.1"
- }
- },
- "System.Security.Cryptography.Algorithms": {
- "type": "Transitive",
- "resolved": "4.2.0",
- "contentHash": "8JQFxbLVdrtIOKMDN38Fn0GWnqYZw/oMlwOUG/qz1jqChvyZlnUmu+0s7wLx7JYua/nAXoESpHA3iw11QFWhXg==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Collections": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Runtime.Numerics": "4.0.1",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Cryptography.Cng": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "WG3r7EyjUe9CMPFSs6bty5doUqT+q9pbI80hlNzo2SkPkZ4VTuZkGWjpp77JB8+uaL4DFPRdBsAY+DX3dBK92A=="
- },
- "System.Security.Cryptography.Csp": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "/i1Usuo4PgAqgbPNC0NjbO3jPW//BoBlTpcWFD1EHVbidH21y4c1ap5bbEMSGAXjAShhMH4abi/K8fILrnu4BQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.IO": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Security.Cryptography.Algorithms": "4.2.0",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "System.Threading": "4.0.11"
- }
- },
- "System.Security.Cryptography.Encoding": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "FbKgE5MbxSQMPcSVRgwM6bXN3GtyAh04NkV8E5zKCBE26X0vYW0UtTa2FIgkH33WVqBVxRgxljlVYumWtU+HcQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Collections": "4.0.11",
- "System.Collections.Concurrent": "4.0.12",
- "System.Linq": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Cryptography.OpenSsl": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "HUG/zNUJwEiLkoURDixzkzZdB5yGA5pQhDP93ArOpDPQMteURIGERRNzzoJlmTreLBWr5lkFSjjMSk8ySEpQMw==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Runtime.Numerics": "4.0.1",
- "System.Security.Cryptography.Algorithms": "4.2.0",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Cryptography.Primitives": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "Wkd7QryWYjkQclX0bngpntW5HSlMzeJU24UaLJQ7YTfI8ydAVAaU2J+HXLLABOVJlKTVvAeL0Aj39VeTe7L+oA==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Security.Cryptography.X509Certificates": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "4HEfsQIKAhA1+ApNn729Gi09zh+lYWwyIuViihoMDWp1vQnEkL2ct7mAbhBlLYm+x/L4Rr/pyGge1lIY635e0w==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Globalization.Calendars": "4.0.1",
- "System.IO": "4.1.0",
- "System.IO.FileSystem": "4.0.1",
- "System.IO.FileSystem.Primitives": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.Handles": "4.0.1",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Runtime.Numerics": "4.0.1",
- "System.Security.Cryptography.Algorithms": "4.2.0",
- "System.Security.Cryptography.Cng": "4.2.0",
- "System.Security.Cryptography.Csp": "4.0.0",
- "System.Security.Cryptography.Encoding": "4.0.0",
- "System.Security.Cryptography.OpenSsl": "4.0.0",
- "System.Security.Cryptography.Primitives": "4.0.0",
- "System.Text.Encoding": "4.0.11",
- "System.Threading": "4.0.11",
- "runtime.native.System": "4.0.0",
- "runtime.native.System.Net.Http": "4.0.1",
- "runtime.native.System.Security.Cryptography": "4.0.0"
- }
- },
- "System.Security.Principal": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "On+SKhXY5rzxh/S8wlH1Rm0ogBlu7zyHNxeNBiXauNrhHRXAe9EuX8Yl5IOzLPGU5Z4kLWHMvORDOCG8iu9hww==",
- "dependencies": {
- "System.Runtime": "4.1.0"
- }
- },
- "System.Text.Encoding": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0"
- }
- },
- "System.Text.Encoding.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "jtbiTDtvfLYgXn8PTfWI+SiBs51rrmO4AAckx4KR6vFK9Wzf6tI8kcRdsYQNwriUeQ1+CtQbM1W4cMbLXnj/OQ==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.0.1",
- "Microsoft.NETCore.Targets": "1.0.1",
- "System.Runtime": "4.1.0",
- "System.Text.Encoding": "4.0.11"
- }
- },
- "System.Text.Encodings.Web": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "GgJDO6/1bW6kkttxIiPK2jsqllQ3ifaeeBAJJrcoJq0lAclIZsAZZdEqi6JHq+QLZXL2UsjyWb8K8EOH7nOSPw==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.IO": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Text.Json": {
- "type": "Transitive",
- "resolved": "4.7.2",
- "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg=="
- },
- "System.Text.RegularExpressions": {
- "type": "Transitive",
- "resolved": "4.1.0",
- "contentHash": "i88YCXpRTjCnoSQZtdlHkAOx4KNNik4hMy83n0+Ftlb7jvV6ZiZWMpnEZHhjBp6hQVh8gWd/iKNPzlPF7iyA2g==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11"
- }
- },
- "System.Threading": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==",
- "dependencies": {
- "System.Runtime": "4.1.0",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Threading.Tasks": {
- "type": "Transitive",
- "resolved": "4.3.0",
- "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
- "dependencies": {
- "Microsoft.NETCore.Platforms": "1.1.0",
- "Microsoft.NETCore.Targets": "1.1.0",
- "System.Runtime": "4.3.0"
- }
- },
- "System.Threading.Tasks.Extensions": {
- "type": "Transitive",
- "resolved": "4.0.0",
- "contentHash": "pH4FZDsZQ/WmgJtN4LWYmRdJAEeVkyriSwrv2Teoe5FOU0Yxlb6II6GL8dBPOfRmutHGATduj3ooMt7dJ2+i+w==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Runtime": "4.1.0",
- "System.Threading.Tasks": "4.0.11"
- }
- },
- "System.Xml.ReaderWriter": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "ZIiLPsf67YZ9zgr31vzrFaYQqxRPX9cVHjtPSnmx4eN6lbS/yEyYNr2vs1doGDEscF0tjCZFsk9yUg1sC9e8tg==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.IO.FileSystem": "4.0.1",
- "System.IO.FileSystem.Primitives": "4.0.1",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Runtime.InteropServices": "4.1.0",
- "System.Text.Encoding": "4.0.11",
- "System.Text.Encoding.Extensions": "4.0.11",
- "System.Text.RegularExpressions": "4.1.0",
- "System.Threading.Tasks": "4.0.11",
- "System.Threading.Tasks.Extensions": "4.0.0"
- }
- },
- "System.Xml.XDocument": {
- "type": "Transitive",
- "resolved": "4.0.11",
- "contentHash": "Mk2mKmPi0nWaoiYeotq1dgeNK1fqWh61+EK+w4Wu8SWuTYLzpUnschb59bJtGywaPq7SmTuPf44wrXRwbIrukg==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Diagnostics.Tools": "4.0.1",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Reflection": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Text.Encoding": "4.0.11",
- "System.Threading": "4.0.11",
- "System.Xml.ReaderWriter": "4.0.11"
- }
- },
- "System.Xml.XPath": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "UWd1H+1IJ9Wlq5nognZ/XJdyj8qPE4XufBUkAW59ijsCPjZkZe0MUzKKJFBr+ZWBe5Wq1u1d5f2CYgE93uH7DA==",
- "dependencies": {
- "System.Collections": "4.0.11",
- "System.Diagnostics.Debug": "4.0.11",
- "System.Globalization": "4.0.11",
- "System.IO": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Xml.ReaderWriter": "4.0.11"
- }
- },
- "System.Xml.XPath.XDocument": {
- "type": "Transitive",
- "resolved": "4.0.1",
- "contentHash": "FLhdYJx4331oGovQypQ8JIw2kEmNzCsjVOVYY/16kZTUoquZG85oVn7yUhBE2OZt1yGPSXAL0HTEfzjlbNpM7Q==",
- "dependencies": {
- "System.Diagnostics.Debug": "4.0.11",
- "System.Linq": "4.1.0",
- "System.Resources.ResourceManager": "4.0.1",
- "System.Runtime": "4.1.0",
- "System.Runtime.Extensions": "4.1.0",
- "System.Threading": "4.0.11",
- "System.Xml.ReaderWriter": "4.0.11",
- "System.Xml.XDocument": "4.0.11",
- "System.Xml.XPath": "4.0.1"
- }
- },
- "timeline.errorcodes": {
- "type": "Project"
- }
- }
- }
-}
\ No newline at end of file |