mirror of
https://github.com/bitwarden/desktop.git
synced 2024-11-23 11:45:40 +01:00
Apply Prettier (#1202)
This commit is contained in:
parent
b4df834b16
commit
521feae535
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -6,7 +6,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
|
||||
|
||||
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
|
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,4 +1,5 @@
|
||||
## Type of change
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature development
|
||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||
@ -6,27 +7,26 @@
|
||||
- [ ] Other
|
||||
|
||||
## Objective
|
||||
|
||||
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
|
||||
|
||||
|
||||
|
||||
## Code changes
|
||||
|
||||
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
|
||||
<!--Also refer to any related changes or PRs in other repositories-->
|
||||
|
||||
* **file.ext:** Description of what was changed and why
|
||||
- **file.ext:** Description of what was changed and why
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!--Required for any UI changes. Delete if not applicable-->
|
||||
|
||||
|
||||
|
||||
## Testing requirements
|
||||
|
||||
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||
|
||||
|
||||
|
||||
## Before you submit
|
||||
|
||||
- [ ] I have checked for **linting** errors (`npm run lint`) (required)
|
||||
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -128,8 +128,8 @@ jobs:
|
||||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
||||
# - name: Run linter
|
||||
# run: npm run lint
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
- name: Build application
|
||||
run: npm run dist:lin
|
||||
@ -224,8 +224,8 @@ jobs:
|
||||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
||||
# - name: Run linter
|
||||
# run: npm run lint
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
- name: Build & Sign (dev)
|
||||
env:
|
||||
@ -477,8 +477,8 @@ jobs:
|
||||
- name: Install Node dependencies
|
||||
run: npm ci
|
||||
|
||||
# - name: Run linter
|
||||
# run: npm run lint
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
|
||||
- name: Build application (dev)
|
||||
run: npm run build
|
||||
|
4
.github/workflows/crowndin-pull.yml
vendored
4
.github/workflows/crowndin-pull.yml
vendored
@ -5,7 +5,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
schedule:
|
||||
- cron: '0 0 * * 5'
|
||||
- cron: "0 0 * * 5"
|
||||
|
||||
jobs:
|
||||
crowdin-sync:
|
||||
@ -15,7 +15,7 @@ jobs:
|
||||
_CROWDIN_PROJECT_ID: "299360"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
|
58
.github/workflows/release.yml
vendored
58
.github/workflows/release.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Get Package Version
|
||||
id: retrieve-version
|
||||
@ -58,34 +58,34 @@ jobs:
|
||||
branch: ${{ steps.branch.outputs.branch-name }}
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
||||
env:
|
||||
PKG_VERSION: ${{ steps.retrieve-version.outputs.package_version }}
|
||||
with:
|
||||
artifacts: "Bitwarden-${{ env.PKG_VERSION }}-amd64.deb,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd,
|
||||
bitwarden_${{ env.PKG_VERSION }}_amd64.snap,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage,
|
||||
latest-linux.yml,
|
||||
Bitwarden-Portable-${{ env.PKG_VERSION }}.exe,
|
||||
Bitwarden-Installer-${{ env.PKG_VERSION }}.exe,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32.nsis.7z,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.nsis.7z,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64.nsis.7z,
|
||||
bitwarden.${{ env.PKG_VERSION }}.nupkg,
|
||||
latest.yml,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal-mac.zip,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg.blockmap,
|
||||
latest-mac.yml,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.pkg"
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd,
|
||||
bitwarden_${{ env.PKG_VERSION }}_amd64.snap,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage,
|
||||
latest-linux.yml,
|
||||
Bitwarden-Portable-${{ env.PKG_VERSION }}.exe,
|
||||
Bitwarden-Installer-${{ env.PKG_VERSION }}.exe,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-ia32.nsis.7z,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-x64.nsis.7z,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64-store.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64.appx,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-arm64.nsis.7z,
|
||||
bitwarden.${{ env.PKG_VERSION }}.nupkg,
|
||||
latest.yml,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal-mac.zip,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg.blockmap,
|
||||
latest-mac.yml,
|
||||
Bitwarden-${{ env.PKG_VERSION }}-universal.pkg"
|
||||
commit: ${{ github.sha }}
|
||||
tag: v${{ env.PKG_VERSION }}
|
||||
name: Version ${{ env.PKG_VERSION }}
|
||||
@ -93,7 +93,6 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
|
||||
|
||||
snap:
|
||||
name: Deploy Snap
|
||||
runs-on: ubuntu-20.04
|
||||
@ -102,10 +101,10 @@ jobs:
|
||||
_PKG_VERSION: ${{ needs.setup.outputs.package_version }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Install Snap
|
||||
uses: samuelmeuli/action-snapcraft@10d7d0a84d9d86098b19f872257df314b0bd8e2d # v1.2.0
|
||||
uses: samuelmeuli/action-snapcraft@10d7d0a84d9d86098b19f872257df314b0bd8e2d # v1.2.0
|
||||
with:
|
||||
snapcraft_token: ${{ secrets.SNAP_TOKEN }}
|
||||
|
||||
@ -129,7 +128,6 @@ jobs:
|
||||
snapcraft upload dist/bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
|
||||
snapcraft logout
|
||||
|
||||
|
||||
choco:
|
||||
name: Deploy Choco
|
||||
runs-on: windows-2019
|
||||
@ -138,7 +136,7 @@ jobs:
|
||||
_PKG_VERSION: ${{ needs.setup.outputs.package_version }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- name: Setup Chocolatey
|
||||
run: choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
|
||||
|
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -10,9 +10,7 @@
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"args": [
|
||||
"."
|
||||
]
|
||||
"args": ["."]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -6,17 +6,12 @@ Please visit our [Community Forums](https://community.bitwarden.com/) for genera
|
||||
|
||||
Here is how you can get involved:
|
||||
|
||||
* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
||||
|
||||
* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||
|
||||
* **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||
|
||||
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||
|
||||
* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
|
||||
* **Translate:** See the localization (l10n) section below
|
||||
- **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
||||
- **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
||||
- **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
||||
- **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
||||
- **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
||||
- **Translate:** See the localization (l10n) section below
|
||||
|
||||
## Contributor Agreement
|
||||
|
||||
@ -24,9 +19,9 @@ Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/deskt
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
* use `npm run lint` and fix any linting suggestions before submitting a pull request
|
||||
* commit any pull requests against the `master` branch
|
||||
* include a link to your Community Forums post
|
||||
- use `npm run lint` and fix any linting suggestions before submitting a pull request
|
||||
- commit any pull requests against the `master` branch
|
||||
- include a link to your Community Forums post
|
||||
|
||||
# Localization (l10n)
|
||||
|
||||
|
@ -16,8 +16,7 @@ The Bitwarden desktop app is written using Electron and Angular. The application
|
||||
|
||||
- [Node.js](https://nodejs.org) v16.13.1 (LTS) or greater
|
||||
- NPM v8
|
||||
- Windows users: To compile the native node modules used in the app you will need the *Visual C++ toolset*, available through the standard Visual Studio installer. You will also need to install the *Microsoft Build Tools 2015* and *Windows 10 SDK 17134* as additional dependencies in the Visual Studio installer.
|
||||
|
||||
- Windows users: To compile the native node modules used in the app you will need the _Visual C++ toolset_, available through the standard Visual Studio installer. You will also need to install the _Microsoft Build Tools 2015_ and _Windows 10 SDK 17134_ as additional dependencies in the Visual Studio installer.
|
||||
|
||||
**Run the app**
|
||||
|
||||
@ -31,10 +30,10 @@ npm run electron
|
||||
Native Messaging (communication with the browser extension) works by having the browser start a lightweight proxy application baked into our desktop binary. To setup an environment which allows
|
||||
for easy debugging you will need to build the application for distribution, i.e. `npm run dist:<platform>`, start the dist version and enable desktop integration. This will write some manifests
|
||||
to disk, Consult the [native manifests](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location) documentation for more details of the manifest
|
||||
format, and the exact locations for the different platforms. *Note* that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
||||
format, and the exact locations for the different platforms. _Note_ that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
||||
|
||||
The generated manifests are pre-configured with the production ID for the browser extensions. In order to use them with the development builds, the browser extension ID of the development build
|
||||
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
||||
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
||||
|
||||
It will then be possible to run the desktop application as usual using `npm run electron` and communicate with the browser.
|
||||
|
||||
|
@ -7,7 +7,7 @@ notify us. We welcome working with you to resolve the issue promptly. Thanks in
|
||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
||||
effort to quickly resolve the issue.
|
||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
||||
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
||||
account holder.
|
||||
|
@ -27,7 +27,7 @@
|
||||
"symlink:win": "rm -rf ./jslib && cmd /c mklink /J .\\jslib ..\\jslib",
|
||||
"symlink:mac": "npm run symlink:lin",
|
||||
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
|
||||
"lint": "tslint 'src/**/*.ts'",
|
||||
"lint": "tslint 'src/**/*.ts' && prettier --check .",
|
||||
"lint:fix": "tslint 'src/**/*.ts' --fix",
|
||||
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
|
||||
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
|
||||
|
@ -1,53 +1,60 @@
|
||||
require('dotenv').config();
|
||||
const path = require('path');
|
||||
const fse = require('fs-extra');
|
||||
const { notarize } = require('electron-notarize');
|
||||
const { deepAssign } = require('builder-util');
|
||||
require("dotenv").config();
|
||||
const path = require("path");
|
||||
const fse = require("fs-extra");
|
||||
const { notarize } = require("electron-notarize");
|
||||
const { deepAssign } = require("builder-util");
|
||||
|
||||
exports.default = run;
|
||||
|
||||
async function run(context) {
|
||||
console.log('## After sign');
|
||||
// console.log(context);
|
||||
console.log("## After sign");
|
||||
// console.log(context);
|
||||
|
||||
const appName = context.packager.appInfo.productFilename;
|
||||
const appPath = `${context.appOutDir}/${appName}.app`;
|
||||
const macBuild = context.electronPlatformName === 'darwin';
|
||||
const copyPlugIn = ['darwin', 'mas'].includes(context.electronPlatformName);
|
||||
const appName = context.packager.appInfo.productFilename;
|
||||
const appPath = `${context.appOutDir}/${appName}.app`;
|
||||
const macBuild = context.electronPlatformName === "darwin";
|
||||
const copyPlugIn = ["darwin", "mas"].includes(context.electronPlatformName);
|
||||
|
||||
if (copyPlugIn) {
|
||||
// Copy Safari plugin to work-around https://github.com/electron-userland/electron-builder/issues/5552
|
||||
const plugIn = path.join(__dirname, '../PlugIns');
|
||||
if (fse.existsSync(plugIn)) {
|
||||
fse.mkdirSync(path.join(appPath, 'Contents/PlugIns'));
|
||||
fse.copySync(path.join(plugIn, 'safari.appex'), path.join(appPath, 'Contents/PlugIns/safari.appex'));
|
||||
if (copyPlugIn) {
|
||||
// Copy Safari plugin to work-around https://github.com/electron-userland/electron-builder/issues/5552
|
||||
const plugIn = path.join(__dirname, "../PlugIns");
|
||||
if (fse.existsSync(plugIn)) {
|
||||
fse.mkdirSync(path.join(appPath, "Contents/PlugIns"));
|
||||
fse.copySync(
|
||||
path.join(plugIn, "safari.appex"),
|
||||
path.join(appPath, "Contents/PlugIns/safari.appex")
|
||||
);
|
||||
|
||||
// Resign to sign safari extension
|
||||
if (context.electronPlatformName === 'mas') {
|
||||
const masBuildOptions = deepAssign({}, context.packager.platformSpecificBuildOptions, context.packager.config.mas);
|
||||
if (context.targets.some(e => e.name === 'mas-dev')) {
|
||||
deepAssign(masBuildOptions, {
|
||||
type: 'development',
|
||||
});
|
||||
}
|
||||
if (context.packager.packagerOptions.prepackaged == null) {
|
||||
await context.packager.sign(appPath, context.appOutDir, masBuildOptions, context.arch);
|
||||
}
|
||||
} else {
|
||||
await context.packager.signApp(context, true);
|
||||
}
|
||||
// Resign to sign safari extension
|
||||
if (context.electronPlatformName === "mas") {
|
||||
const masBuildOptions = deepAssign(
|
||||
{},
|
||||
context.packager.platformSpecificBuildOptions,
|
||||
context.packager.config.mas
|
||||
);
|
||||
if (context.targets.some((e) => e.name === "mas-dev")) {
|
||||
deepAssign(masBuildOptions, {
|
||||
type: "development",
|
||||
});
|
||||
}
|
||||
if (context.packager.packagerOptions.prepackaged == null) {
|
||||
await context.packager.sign(appPath, context.appOutDir, masBuildOptions, context.arch);
|
||||
}
|
||||
} else {
|
||||
await context.packager.signApp(context, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (macBuild) {
|
||||
console.log('### Notarizing ' + appPath);
|
||||
const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID;
|
||||
const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`;
|
||||
return await notarize({
|
||||
appBundleId: 'com.bitwarden.desktop',
|
||||
appPath: appPath,
|
||||
appleId: appleId,
|
||||
appleIdPassword: appleIdPassword,
|
||||
});
|
||||
}
|
||||
if (macBuild) {
|
||||
console.log("### Notarizing " + appPath);
|
||||
const appleId = process.env.APPLE_ID_USERNAME || process.env.APPLEID;
|
||||
const appleIdPassword = process.env.APPLE_ID_PASSWORD || `@keychain:AC_PASSWORD`;
|
||||
return await notarize({
|
||||
appBundleId: "com.bitwarden.desktop",
|
||||
appPath: appPath,
|
||||
appleId: appleId,
|
||||
appleIdPassword: appleIdPassword,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
29
sign.js
29
sign.js
@ -1,22 +1,19 @@
|
||||
exports.default = async function(configuration) {
|
||||
if (
|
||||
parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 &&
|
||||
configuration.path.slice(-4) == ".exe"
|
||||
) {
|
||||
console.log(`[*] Signing file: ${configuration.path}`)
|
||||
exports.default = async function (configuration) {
|
||||
if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && configuration.path.slice(-4) == ".exe") {
|
||||
console.log(`[*] Signing file: ${configuration.path}`);
|
||||
require("child_process").execSync(
|
||||
`azuresigntool sign -v ` +
|
||||
`-kvu ${process.env.SIGNING_VAULT_URL} ` +
|
||||
`-kvi ${process.env.SIGNING_CLIENT_ID} ` +
|
||||
`-kvt ${process.env.SIGNING_TENANT_ID} ` +
|
||||
`-kvs ${process.env.SIGNING_CLIENT_SECRET} ` +
|
||||
`-kvc ${process.env.SIGNING_CERT_NAME} ` +
|
||||
`-fd ${configuration.hash} ` +
|
||||
`-du ${configuration.site} ` +
|
||||
`-tr http://timestamp.digicert.com ` +
|
||||
`${configuration.path}`,
|
||||
`-kvu ${process.env.SIGNING_VAULT_URL} ` +
|
||||
`-kvi ${process.env.SIGNING_CLIENT_ID} ` +
|
||||
`-kvt ${process.env.SIGNING_TENANT_ID} ` +
|
||||
`-kvs ${process.env.SIGNING_CLIENT_SECRET} ` +
|
||||
`-kvc ${process.env.SIGNING_CERT_NAME} ` +
|
||||
`-fd ${configuration.hash} ` +
|
||||
`-du ${configuration.site} ` +
|
||||
`-tr http://timestamp.digicert.com ` +
|
||||
`${configuration.path}`,
|
||||
{
|
||||
stdio: "inherit"
|
||||
stdio: "inherit",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,66 +1,97 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{'settings' | i18n}}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'selfHostedEnvironment' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="baseUrl">{{'baseUrl' | i18n}}</label>
|
||||
<input id="baseUrl" type="text" name="BaseUrl" [(ngModel)]="baseUrl"
|
||||
placeholder="{{'ex' | i18n}} https://bitwarden.company.com" appInputVerbatim>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'selfHostedEnvironmentFooter' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button type="button" (click)="toggleCustom()" appA11yTitle="{{'toggleVisibility' | i18n}}">
|
||||
<i class="fa fa-plus-square-o" [hidden]="showCustom" aria-hidden="true"></i>
|
||||
<i class="fa fa-minus-square-o" [hidden]="!showCustom" aria-hidden="true"></i>
|
||||
{{'customEnvironment' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content" [hidden]="!showCustom">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="webVaultUrl">{{'webVaultUrl' | i18n}}</label>
|
||||
<input id="webVaultUrl" type="text" name="WebVaultUrl" [(ngModel)]="webVaultUrl"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="apiUrl">{{'apiUrl' | i18n}}</label>
|
||||
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="identityUrl">{{'identityUrl' | i18n}}</label>
|
||||
<input id="identityUrl" type="text" name="IdentityUrl" [(ngModel)]="identityUrl"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notificationsUrl">{{'notificationsUrl' | i18n}}</label>
|
||||
<input id="notificationsUrl" type="text" name="NotificationsUrl"
|
||||
[(ngModel)]="notificationsUrl" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="iconsUrl">{{'iconsUrl' | i18n}}</label>
|
||||
<input id="iconsUrl" type="text" name="IconsUrl" [(ngModel)]="iconsUrl" appInputVerbatim>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" [hidden]="!showCustom">
|
||||
{{'customEnvironmentFooter' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{ 'settings' | i18n }}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "selfHostedEnvironment" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="baseUrl">{{ "baseUrl" | i18n }}</label>
|
||||
<input
|
||||
id="baseUrl"
|
||||
type="text"
|
||||
name="BaseUrl"
|
||||
[(ngModel)]="baseUrl"
|
||||
placeholder="{{ 'ex' | i18n }} https://bitwarden.company.com"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}">
|
||||
<i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "selfHostedEnvironmentFooter" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggleCustom()"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-plus-square-o" [hidden]="showCustom" aria-hidden="true"></i>
|
||||
<i class="fa fa-minus-square-o" [hidden]="!showCustom" aria-hidden="true"></i>
|
||||
{{ "customEnvironment" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content" [hidden]="!showCustom">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="webVaultUrl">{{ "webVaultUrl" | i18n }}</label>
|
||||
<input
|
||||
id="webVaultUrl"
|
||||
type="text"
|
||||
name="WebVaultUrl"
|
||||
[(ngModel)]="webVaultUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="apiUrl">{{ "apiUrl" | i18n }}</label>
|
||||
<input id="apiUrl" type="text" name="ApiUrl" [(ngModel)]="apiUrl" appInputVerbatim />
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="identityUrl">{{ "identityUrl" | i18n }}</label>
|
||||
<input
|
||||
id="identityUrl"
|
||||
type="text"
|
||||
name="IdentityUrl"
|
||||
[(ngModel)]="identityUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="notificationsUrl">{{ "notificationsUrl" | i18n }}</label>
|
||||
<input
|
||||
id="notificationsUrl"
|
||||
type="text"
|
||||
name="NotificationsUrl"
|
||||
[(ngModel)]="notificationsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="iconsUrl">{{ "iconsUrl" | i18n }}</label>
|
||||
<input
|
||||
id="iconsUrl"
|
||||
type="text"
|
||||
name="IconsUrl"
|
||||
[(ngModel)]="iconsUrl"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" [hidden]="!showCustom">
|
||||
{{ "customEnvironmentFooter" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
|
||||
<i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib-angular/components/environment.component';
|
||||
import { EnvironmentComponent as BaseEnvironmentComponent } from "jslib-angular/components/environment.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-environment',
|
||||
templateUrl: 'environment.component.html',
|
||||
selector: "app-environment",
|
||||
templateUrl: "environment.component.html",
|
||||
})
|
||||
export class EnvironmentComponent extends BaseEnvironmentComponent {
|
||||
constructor(platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
|
||||
i18nService: I18nService) {
|
||||
super(platformUtilsService, environmentService, i18nService);
|
||||
}
|
||||
constructor(
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
super(platformUtilsService, environmentService, i18nService);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,31 @@
|
||||
<form id="hint-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<h1>{{'passwordHint' | i18n}}</h1>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required appAutofocus
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'enterEmailToGetHint' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
||||
<div class="content">
|
||||
<h1>{{ "passwordHint" | i18n }}</h1>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "enterEmailToGetHint" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,20 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { HintComponent as BaseHintComponent } from 'jslib-angular/components/hint.component';
|
||||
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-hint',
|
||||
templateUrl: 'hint.component.html',
|
||||
selector: "app-hint",
|
||||
templateUrl: "hint.component.html",
|
||||
})
|
||||
export class HintComponent extends BaseHintComponent {
|
||||
constructor(router: Router, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService, apiService: ApiService, logService: LogService) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||
}
|
||||
constructor(
|
||||
router: Router,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
apiService: ApiService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService);
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +1,77 @@
|
||||
<form id="lock-page" (ngSubmit)="submit()">
|
||||
<div class="content">
|
||||
<p aria-hidden="true"><i class="fa fa-lock fa-4x text-muted"></i></p>
|
||||
<p>{{'yourVaultIsLocked' | i18n}}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||
<div class="row-main" *ngIf="pinLock">
|
||||
<label for="pin">{{'pin' | i18n}}</label>
|
||||
<input id="pin" type="{{showPassword ? 'text' : 'password'}}" name="PIN" class="monospaced"
|
||||
[(ngModel)]="pin" required appInputVerbatim>
|
||||
</div>
|
||||
<div class="row-main" *ngIf="!pinLock">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'loggedInAsOn' | i18n : email : webVaultHostname}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row" *ngIf="supportsBiometric && biometricLock">
|
||||
<button type="button" class="btn block" [ngClass]="{'primary font-weight-bold': hideInput}"
|
||||
appBlurClick (click)="unlockBiometric()">
|
||||
{{biometricText | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" appBlurClick *ngIf="!hideInput">
|
||||
<i class="fa fa-unlock-alt" aria-hidden="true"></i> <b>{{'unlock' | i18n}}</b>
|
||||
</button>
|
||||
<button type="button" class="btn block" appBlurClick (click)="logOut()">
|
||||
{{'logOut' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p aria-hidden="true"><i class="fa fa-lock fa-4x text-muted"></i></p>
|
||||
<p>{{ "yourVaultIsLocked" | i18n }}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput">
|
||||
<div class="row-main" *ngIf="pinLock">
|
||||
<label for="pin">{{ "pin" | i18n }}</label>
|
||||
<input
|
||||
id="pin"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="PIN"
|
||||
class="monospaced"
|
||||
[(ngModel)]="pin"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="row-main" *ngIf="!pinLock">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "loggedInAsOn" | i18n: email:webVaultHostname }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row" *ngIf="supportsBiometric && biometricLock">
|
||||
<button
|
||||
type="button"
|
||||
class="btn block"
|
||||
[ngClass]="{ 'primary font-weight-bold': hideInput }"
|
||||
appBlurClick
|
||||
(click)="unlockBiometric()"
|
||||
>
|
||||
{{ biometricText | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" appBlurClick *ngIf="!hideInput">
|
||||
<i class="fa fa-unlock-alt" aria-hidden="true"></i> <b>{{ "unlock" | i18n }}</b>
|
||||
</button>
|
||||
<button type="button" class="btn block" appBlurClick (click)="logOut()">
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,95 +1,108 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { LockComponent as BaseLockComponent } from 'jslib-angular/components/lock.component';
|
||||
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
|
||||
|
||||
const BroadcasterSubscriptionId = 'LockComponent';
|
||||
const BroadcasterSubscriptionId = "LockComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-lock',
|
||||
templateUrl: 'lock.component.html',
|
||||
selector: "app-lock",
|
||||
templateUrl: "lock.component.html",
|
||||
})
|
||||
export class LockComponent extends BaseLockComponent implements OnDestroy {
|
||||
private deferFocus: boolean = null;
|
||||
private deferFocus: boolean = null;
|
||||
|
||||
constructor(router: Router, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
|
||||
cryptoService: CryptoService, vaultTimeoutService: VaultTimeoutService,
|
||||
environmentService: EnvironmentService, stateService: StateService,
|
||||
apiService: ApiService, private route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService, ngZone: NgZone,
|
||||
logService: LogService, keyConnectorService: KeyConnectorService) {
|
||||
super(router, i18nService, platformUtilsService, messagingService, cryptoService,
|
||||
vaultTimeoutService, environmentService, stateService, apiService, logService,
|
||||
keyConnectorService, ngZone);
|
||||
}
|
||||
constructor(
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
messagingService: MessagingService,
|
||||
cryptoService: CryptoService,
|
||||
vaultTimeoutService: VaultTimeoutService,
|
||||
environmentService: EnvironmentService,
|
||||
stateService: StateService,
|
||||
apiService: ApiService,
|
||||
private route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone,
|
||||
logService: LogService,
|
||||
keyConnectorService: KeyConnectorService
|
||||
) {
|
||||
super(
|
||||
router,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
messagingService,
|
||||
cryptoService,
|
||||
vaultTimeoutService,
|
||||
environmentService,
|
||||
stateService,
|
||||
apiService,
|
||||
logService,
|
||||
keyConnectorService,
|
||||
ngZone
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const autoPromptBiometric = !await this.stateService.getNoAutoPromptBiometrics();
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
const autoPromptBiometric = !(await this.stateService.getNoAutoPromptBiometrics());
|
||||
|
||||
this.route.queryParams.subscribe(params => {
|
||||
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) {
|
||||
setTimeout(async() => {
|
||||
if (await ipcRenderer.invoke('windowVisible')) {
|
||||
this.unlockBiometric();
|
||||
}
|
||||
}, 1000);
|
||||
this.route.queryParams.subscribe((params) => {
|
||||
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) {
|
||||
setTimeout(async () => {
|
||||
if (await ipcRenderer.invoke("windowVisible")) {
|
||||
this.unlockBiometric();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case "windowIsFocused":
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
});
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case 'windowIsFocused':
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send('getWindowIsFocused');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send("getWindowIsFocused");
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
|
||||
private focusInput() {
|
||||
document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus();
|
||||
}
|
||||
private focusInput() {
|
||||
document.getElementById(this.pinLock ? "pin" : "masterPassword").focus();
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +1,96 @@
|
||||
<div class="login-header">
|
||||
<a href="#" appStopClick (click)="settings()" class="environment-urls-settings-icon" attr.aria-label="{{'settings' | i18n}}">
|
||||
{{'settings' | i18n}}
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="settings()"
|
||||
class="environment-urls-settings-icon"
|
||||
attr.aria-label="{{ 'settings' | i18n }}"
|
||||
>
|
||||
{{ "settings" | i18n }}
|
||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<form id="login-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise" attr.aria-hidden="{{showingModal}}">
|
||||
<div id="content" class="content">
|
||||
<img class="logo-image" alt="Bitwarden">
|
||||
<p class="lead">{{'loginOrCreateNewAccount' | i18n}}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required appInputVerbatim="false">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id="login-page"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
attr.aria-hidden="{{ showingModal }}"
|
||||
>
|
||||
<div id="content" class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
appInputVerbatim="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading"><i class="fa fa-sign-in" aria-hidden="true"></i> {{'logIn' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i> {{'createAccount' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||
<i class="fa fa-bank" aria-hidden="true"></i> {{'enterpriseSingleSignOn' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{'getMasterPasswordHint' | i18n}}</a>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons with-rows">
|
||||
<div class="buttons-row">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading"
|
||||
><i class="fa fa-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
|
||||
>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/register" class="btn block">
|
||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i> {{ "createAccount" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="buttons-row">
|
||||
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
|
||||
<i class="fa fa-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ng-template #environment></ng-template>
|
||||
|
@ -1,112 +1,129 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
|
||||
import { Router } from '@angular/router';
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { EnvironmentComponent } from './environment.component';
|
||||
import { EnvironmentComponent } from "./environment.component";
|
||||
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from 'jslib-angular/components/login.component';
|
||||
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
|
||||
|
||||
const BroadcasterSubscriptionId = 'LoginComponent';
|
||||
const BroadcasterSubscriptionId = "LoginComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: 'login.component.html',
|
||||
selector: "app-login",
|
||||
templateUrl: "login.component.html",
|
||||
})
|
||||
export class LoginComponent extends BaseLoginComponent implements OnDestroy {
|
||||
@ViewChild('environment', { read: ViewContainerRef, static: true }) environmentModal: ViewContainerRef;
|
||||
@ViewChild("environment", { read: ViewContainerRef, static: true })
|
||||
environmentModal: ViewContainerRef;
|
||||
|
||||
showingModal = false;
|
||||
showingModal = false;
|
||||
|
||||
private deferFocus: boolean = null;
|
||||
private deferFocus: boolean = null;
|
||||
|
||||
constructor(authService: AuthService, router: Router, i18nService: I18nService,
|
||||
syncService: SyncService, private modalService: ModalService,
|
||||
platformUtilsService: PlatformUtilsService, stateService: StateService,
|
||||
environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService,
|
||||
cryptoFunctionService: CryptoFunctionService, private broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone, private messagingService: MessagingService,
|
||||
logService: LogService) {
|
||||
super(authService, router, platformUtilsService, i18nService, stateService, environmentService,
|
||||
passwordGenerationService, cryptoFunctionService, logService, ngZone);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
syncService: SyncService,
|
||||
private modalService: ModalService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
stateService: StateService,
|
||||
environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone,
|
||||
private messagingService: MessagingService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
platformUtilsService,
|
||||
i18nService,
|
||||
stateService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
cryptoFunctionService,
|
||||
logService,
|
||||
ngZone
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case 'windowIsFocused':
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send('getWindowIsFocused');
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async settings() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(EnvironmentComponent, this.environmentModal);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onSaved.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await super.submit();
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById('content') as HTMLDivElement;
|
||||
content.setAttribute('style', 'width:335px');
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
case "windowIsFocused":
|
||||
if (this.deferFocus === null) {
|
||||
this.deferFocus = !message.windowIsFocused;
|
||||
if (!this.deferFocus) {
|
||||
this.focusInput();
|
||||
}
|
||||
} else if (this.deferFocus && message.windowIsFocused) {
|
||||
this.focusInput();
|
||||
this.deferFocus = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
this.messagingService.send("getWindowIsFocused");
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async settings() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
EnvironmentComponent,
|
||||
this.environmentModal
|
||||
);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onSaved.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await super.submit();
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById("content") as HTMLDivElement;
|
||||
content.setAttribute("style", "width:335px");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,70 +1,91 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="premiumTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="premiumTitle">
|
||||
{{'premiumMembership' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{'premiumNotCurrentMember' | i18n}}</p>
|
||||
<p>{{'premiumSignUpAndGet' | i18n}}</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpStorage' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpTwoStep' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpReports' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpTotp' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpSupport' | i18n}}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{'premiumSignUpFuture' | i18n}}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead no-margin">
|
||||
{{'premiumPrice' | i18n : (price | currency:'$')}}
|
||||
</p>
|
||||
</div>
|
||||
<div *ngIf="isPremium">
|
||||
<p class="text-center lead">{{'premiumCurrentMember' | i18n}}</p>
|
||||
<p class="text-center">{{'premiumCurrentMemberThanks' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="premiumTitle">
|
||||
{{ "premiumMembership" | i18n }}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div *ngIf="!isPremium">
|
||||
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
|
||||
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
|
||||
<ul class="fa-ul">
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpStorage" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpTwoStep" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpReports" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpTotp" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpSupport" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-li fa fa-check text-success" aria-hidden="true"></i>
|
||||
{{ "premiumSignUpFuture" | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-center lead no-margin">
|
||||
{{ "premiumPrice" | i18n: (price | currency: "$") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
||||
<b>{{'premiumManage' | i18n}}</b>
|
||||
</button>
|
||||
<button #purchaseBtn type="button" class="primary" appBlurClick (click)="purchase()" *ngIf="!isPremium"
|
||||
[disabled]="purchaseBtn.loading">
|
||||
<b>{{'premiumPurchase' | i18n}}</b>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="right" *ngIf="!isPremium">
|
||||
<button #refreshBtn type="button" appBlurClick (click)="refresh()" [disabled]="refreshBtn.loading"
|
||||
appA11yTitle="{{'premiumRefresh' | i18n}}" [appApiAction]="refreshPromise">
|
||||
<i class="fa fa-refresh fa-lg fa-fw" [hidden]="refreshBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!refreshBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="isPremium">
|
||||
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
|
||||
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
|
||||
<b>{{ "premiumManage" | i18n }}</b>
|
||||
</button>
|
||||
<button
|
||||
#purchaseBtn
|
||||
type="button"
|
||||
class="primary"
|
||||
appBlurClick
|
||||
(click)="purchase()"
|
||||
*ngIf="!isPremium"
|
||||
[disabled]="purchaseBtn.loading"
|
||||
>
|
||||
<b>{{ "premiumPurchase" | i18n }}</b>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
<div class="right" *ngIf="!isPremium">
|
||||
<button
|
||||
#refreshBtn
|
||||
type="button"
|
||||
appBlurClick
|
||||
(click)="refresh()"
|
||||
[disabled]="refreshBtn.loading"
|
||||
appA11yTitle="{{ 'premiumRefresh' | i18n }}"
|
||||
[appApiAction]="refreshPromise"
|
||||
>
|
||||
<i
|
||||
class="fa fa-refresh fa-lg fa-fw"
|
||||
[hidden]="refreshBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!refreshBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,21 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { PremiumComponent as BasePremiumComponent } from 'jslib-angular/components/premium.component';
|
||||
import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-premium',
|
||||
templateUrl: 'premium.component.html',
|
||||
selector: "app-premium",
|
||||
templateUrl: "premium.component.html",
|
||||
})
|
||||
export class PremiumComponent extends BasePremiumComponent {
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService, logService: LogService,
|
||||
stateService: StateService) {
|
||||
super(i18nService, platformUtilsService, apiService, logService, stateService);
|
||||
}
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
logService: LogService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(i18nService, platformUtilsService, apiService, logService, stateService);
|
||||
}
|
||||
}
|
||||
|
@ -1,93 +1,150 @@
|
||||
<form id="register-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<h1>{{'createAccount' | i18n}}</h1>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||
<input id="email" type="text" name="Email" [(ngModel)]="email" required
|
||||
[appAutofocus]="email === ''" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
||||
*ngIf="masterPasswordScoreText">
|
||||
{{masterPasswordScoreText}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
[appAutofocus]="email !== ''" (input)="updatePasswordStrength()" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1>{{ "createAccount" | i18n }}</h1>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="email">{{ "emailAddress" | i18n }}</label>
|
||||
<input
|
||||
id="email"
|
||||
type="text"
|
||||
name="Email"
|
||||
[(ngModel)]="email"
|
||||
required
|
||||
[appAutofocus]="email === ''"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="confirmMasterPassword" required
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||
*ngIf="masterPasswordScoreText"
|
||||
>
|
||||
{{ masterPasswordScoreText }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
[appAutofocus]="email !== ''"
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(false)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last" *ngIf="showTerms">
|
||||
<div class="box-footer checkbox">
|
||||
<input type="checkbox" id="acceptPolicies" [(ngModel)]="acceptPolicies" name="AcceptPolicies">
|
||||
<label for="acceptPolicies">
|
||||
{{'acceptPolicies' | i18n}}<br>
|
||||
<a href="https://bitwarden.com/terms/" target="_blank"
|
||||
rel="noopener">{{'termsOfService' | i18n}}</a>,
|
||||
<a href="https://bitwarden.com/privacy/" target="_blank"
|
||||
rel="noopener">{{'privacyPolicy' | i18n}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="confirmMasterPassword"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(true)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
<div class="box-content-row" [hidden]="!showCaptcha()">
|
||||
<iframe id="hcaptcha_iframe" height="80"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last" *ngIf="showTerms">
|
||||
<div class="box-footer checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="acceptPolicies"
|
||||
[(ngModel)]="acceptPolicies"
|
||||
name="AcceptPolicies"
|
||||
/>
|
||||
<label for="acceptPolicies">
|
||||
{{ "acceptPolicies" | i18n }}<br />
|
||||
<a href="https://bitwarden.com/terms/" target="_blank" rel="noopener">{{
|
||||
"termsOfService" | i18n
|
||||
}}</a
|
||||
>,
|
||||
<a href="https://bitwarden.com/privacy/" target="_blank" rel="noopener">{{
|
||||
"privacyPolicy" | i18n
|
||||
}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,61 +1,74 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { RegisterComponent as BaseRegisterComponent } from 'jslib-angular/components/register.component';
|
||||
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
|
||||
|
||||
const BroadcasterSubscriptionId = 'RegisterComponent';
|
||||
const BroadcasterSubscriptionId = "RegisterComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
templateUrl: 'register.component.html',
|
||||
selector: "app-register",
|
||||
templateUrl: "register.component.html",
|
||||
})
|
||||
export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, cryptoService: CryptoService,
|
||||
apiService: ApiService, stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService, passwordGenerationService: PasswordGenerationService,
|
||||
environmentService: EnvironmentService, private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone, logService: LogService) {
|
||||
super(authService, router, i18nService, cryptoService, apiService, stateService, platformUtilsService,
|
||||
passwordGenerationService, environmentService, logService);
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
apiService: ApiService,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
environmentService: EnvironmentService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
cryptoService,
|
||||
apiService,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
environmentService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
async ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
super.ngOnInit();
|
||||
}
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,28 @@
|
||||
<div id="remove-password-page" *ngIf="!loading">
|
||||
<div class="content">
|
||||
<h1>{{'removeMasterPassword' | i18n}}</h1>
|
||||
<p>{{'convertOrganizationEncryptionDesc' | i18n : organization.name}}</p>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="actionPromise" appBlurClick
|
||||
(click)="convert()">
|
||||
<b [hidden]="continuing">{{'removeMasterPassword' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="btn secondary block" [disabled]="actionPromise" appBlurClick
|
||||
(click)="leave()">
|
||||
<b [hidden]="leaving">{{'leaveOrganization' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h1>{{ "removeMasterPassword" | i18n }}</h1>
|
||||
<p>{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}</p>
|
||||
<div class="buttons">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn primary block"
|
||||
[disabled]="actionPromise"
|
||||
appBlurClick
|
||||
(click)="convert()"
|
||||
>
|
||||
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn secondary block"
|
||||
[disabled]="actionPromise"
|
||||
appBlurClick
|
||||
(click)="leave()"
|
||||
>
|
||||
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from 'jslib-angular/components/remove-password.component';
|
||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "jslib-angular/components/remove-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-remove-password',
|
||||
templateUrl: 'remove-password.component.html',
|
||||
selector: "app-remove-password",
|
||||
templateUrl: "remove-password.component.html",
|
||||
})
|
||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {
|
||||
}
|
||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
||||
|
@ -1,101 +1,159 @@
|
||||
<form id="set-password-page" #form>
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden">
|
||||
<p class="lead">{{'setMasterPassword' | i18n}}</p>
|
||||
<div class="box text-center" *ngIf="syncLoading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
{{'loading' | i18n}}
|
||||
</div>
|
||||
<div *ngIf="!syncLoading">
|
||||
<div class="box">
|
||||
<app-callout type="tip">{{'ssoCompleteRegistration' | i18n}}</app-callout>
|
||||
<app-callout type="warning" title="{{'resetPasswordPolicyAutoEnroll' | i18n}}"
|
||||
*ngIf="resetPasswordAutoEnroll">
|
||||
{{'resetPasswordAutoEnrollInviteWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
||||
</app-callout>
|
||||
</div>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreColor}}"
|
||||
*ngIf="masterPasswordScoreText">
|
||||
{{masterPasswordScoreText}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
(input)="updatePasswordStrength()" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreColor}}" role="progressbar"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
[ngStyle]="{width: (masterPasswordScoreWidth + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreWidth}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="password" name="MasterPasswordRetype"
|
||||
class="monospaced" [(ngModel)]="masterPasswordRetype" required appInputVerbatim
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<i *ngIf="form.loading" class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
<span>{{'submit' | i18n}}</span>
|
||||
</button>
|
||||
<button class="btn block" (click)="logOut()">
|
||||
<span>{{'logOut' | i18n}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<p class="lead">{{ "setMasterPassword" | i18n }}</p>
|
||||
<div class="box text-center" *ngIf="syncLoading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
{{ "loading" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="!syncLoading">
|
||||
<div class="box">
|
||||
<app-callout type="tip">{{ "ssoCompleteRegistration" | i18n }}</app-callout>
|
||||
<app-callout
|
||||
type="warning"
|
||||
title="{{ 'resetPasswordPolicyAutoEnroll' | i18n }}"
|
||||
*ngIf="resetPasswordAutoEnroll"
|
||||
>
|
||||
{{ "resetPasswordAutoEnrollInviteWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
>
|
||||
</app-callout>
|
||||
</div>
|
||||
<form
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
ngNativeValidate
|
||||
autocomplete="off"
|
||||
>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword"
|
||||
>{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
||||
*ngIf="masterPasswordScoreText"
|
||||
>
|
||||
{{ masterPasswordScoreText }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(false)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="password"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(true)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading">
|
||||
<i
|
||||
*ngIf="form.loading"
|
||||
class="fa fa-spinner fa-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span>{{ "submit" | i18n }}</span>
|
||||
</button>
|
||||
<button class="btn block" (click)="logOut()">
|
||||
<span>{{ "logOut" | i18n }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,97 +1,106 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
const BroadcasterSubscriptionId = 'SetPasswordComponent';
|
||||
const BroadcasterSubscriptionId = "SetPasswordComponent";
|
||||
|
||||
import {
|
||||
SetPasswordComponent as BaseSetPasswordComponent,
|
||||
} from 'jslib-angular/components/set-password.component';
|
||||
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-set-password',
|
||||
templateUrl: 'set-password.component.html',
|
||||
selector: "app-set-password",
|
||||
templateUrl: "set-password.component.html",
|
||||
})
|
||||
export class SetPasswordComponent extends BaseSetPasswordComponent implements OnDestroy {
|
||||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
cryptoService: CryptoService, messagingService: MessagingService,
|
||||
passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
||||
policyService: PolicyService, router: Router,
|
||||
syncService: SyncService, route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
||||
stateService: StateService) {
|
||||
super(i18nService, cryptoService, messagingService, passwordGenerationService,
|
||||
platformUtilsService, policyService, router, apiService, syncService, route,
|
||||
stateService);
|
||||
}
|
||||
constructor(
|
||||
apiService: ApiService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
policyService: PolicyService,
|
||||
router: Router,
|
||||
syncService: SyncService,
|
||||
route: ActivatedRoute,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
passwordGenerationService,
|
||||
platformUtilsService,
|
||||
policyService,
|
||||
router,
|
||||
apiService,
|
||||
syncService,
|
||||
route,
|
||||
stateService
|
||||
);
|
||||
}
|
||||
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
get masterPasswordScoreWidth() {
|
||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
}
|
||||
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return 'success';
|
||||
case 3:
|
||||
return 'primary';
|
||||
case 2:
|
||||
return 'warning';
|
||||
default:
|
||||
return 'danger';
|
||||
get masterPasswordScoreColor() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return "success";
|
||||
case 3:
|
||||
return "primary";
|
||||
case 2:
|
||||
return "warning";
|
||||
default:
|
||||
return "danger";
|
||||
}
|
||||
}
|
||||
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t("strong");
|
||||
case 3:
|
||||
return this.i18nService.t("good");
|
||||
case 2:
|
||||
return this.i18nService.t("weak");
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get masterPasswordScoreText() {
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return this.i18nService.t('strong');
|
||||
case 3:
|
||||
return this.i18nService.t('good');
|
||||
case 2:
|
||||
return this.i18nService.t('weak');
|
||||
default:
|
||||
return this.masterPasswordScore != null ? this.i18nService.t('weak') : null;
|
||||
}
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
}
|
||||
}
|
||||
|
@ -1,195 +1,285 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{'settings' | i18n}}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body form">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'security' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl></app-vault-timeout-input>
|
||||
<div class="form-group">
|
||||
<label>{{'vaultTimeoutAction' | i18n}}</label>
|
||||
<div class="radio radio-mt-2">
|
||||
<label for="vaultTimeoutActionLock">
|
||||
<input type="radio" name="VaultTimeoutAction" id="vaultTimeoutActionLock"
|
||||
value="lock" [(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()">
|
||||
{{'lock' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'vaultTimeoutActionLockDesc' | i18n}}</small>
|
||||
<div class="radio">
|
||||
<label for="vaultTimeoutActionLogOut">
|
||||
<input type="radio" name="VaultTimeoutAction" id="vaultTimeoutActionLogOut"
|
||||
value="logOut" [(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()">
|
||||
{{'logOut' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'vaultTimeoutActionLogOutDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="pin">
|
||||
<input id="pin" type="checkbox" name="PIN" [(ngModel)]="pin" (change)="updatePin()">
|
||||
{{'unlockWithPin' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="biometric">
|
||||
<input id="biometric" type="checkbox" name="biometric" [checked]="biometric" (change)="updateBiometric()">
|
||||
{{biometricText | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="noAutoPromptBiometrics">
|
||||
<input id="noAutoPromptBiometrics" type="checkbox" name="noAutoPromptBiometrics" [(ngModel)]="noAutoPromptBiometrics"
|
||||
[disabled]="!biometric" (change)="updateNoAutoPromptBiometrics()">
|
||||
{{noAutoPromptBiometricsText | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'options' | i18n}}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div class="form-group">
|
||||
<label for="clearClipboard">{{'clearClipboard' | i18n}}</label>
|
||||
<select id="clearClipboard" name="ClearClipboard" [(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()">
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="help-block">{{'clearClipboardDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="minimizeOnCopyToClipboard">
|
||||
<input id="minimizeOnCopyToClipboard" type="checkbox"
|
||||
name="MinimizeOnCopyToClipboard" [(ngModel)]="minimizeOnCopyToClipboard"
|
||||
(change)="saveMinOnCopyToClipboard()">
|
||||
{{'minimizeOnCopyToClipboard' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'minimizeOnCopyToClipboardDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="disableFavicons">
|
||||
<input id="disableFavicons" type="checkbox" name="DisableFavicons"
|
||||
[(ngModel)]="disableFavicons" (change)="saveFavicons()">
|
||||
{{'disableFavicon' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'disableFaviconDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegration">
|
||||
<input id="enableBrowserIntegration" type="checkbox" name="EnableBrowserIntegration"
|
||||
[(ngModel)]="enableBrowserIntegration" (change)="saveBrowserIntegration()">
|
||||
{{'enableBrowserIntegration' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'enableBrowserIntegrationDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegrationFingerprint">
|
||||
<input id="enableBrowserIntegrationFingerprint" type="checkbox" name="EnableBrowserIntegrationFingerprint"
|
||||
[(ngModel)]="enableBrowserIntegrationFingerprint" (change)="saveBrowserIntegrationFingerprint()" [disabled]="!enableBrowserIntegration">
|
||||
{{'enableBrowserIntegrationFingerprint' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'enableBrowserIntegrationFingerprintDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableTray">
|
||||
<input id="enableTray" type="checkbox" name="EnableTray" [(ngModel)]="enableTray"
|
||||
(change)="saveTray()">
|
||||
{{enableTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{enableTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showMinToTray">
|
||||
<div class="checkbox">
|
||||
<label for="enableMinToTray">
|
||||
<input id="enableMinToTray" type="checkbox" name="EnableMinToTray"
|
||||
[(ngModel)]="enableMinToTray" (change)="saveMinToTray()">
|
||||
{{enableMinToTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{enableMinToTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableCloseToTray">
|
||||
<input id="enableCloseToTray" type="checkbox" name="EnableCloseToTray"
|
||||
[(ngModel)]="enableCloseToTray" (change)="saveCloseToTray()">
|
||||
{{enableCloseToTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{enableCloseToTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="startToTray">
|
||||
<input id="startToTray" type="checkbox" name="StartToTray" [(ngModel)]="startToTray"
|
||||
(change)="saveStartToTray()">
|
||||
{{startToTrayText}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{startToTrayDescText}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="openAtLogin">
|
||||
<input id="openAtLogin" type="checkbox" name="OpenAtLogin" [(ngModel)]="openAtLogin"
|
||||
(change)="saveOpenAtLogin()">
|
||||
{{'openAtLogin' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'openAtLoginDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showAlwaysShowDock">
|
||||
<div class="checkbox">
|
||||
<label for="alwaysShowDock">
|
||||
<input id="alwaysShowDock" type="checkbox" name="AlwaysShowDock" [(ngModel)]="alwaysShowDock"
|
||||
(change)="saveAlwaysShowDock()">
|
||||
{{'alwaysShowDock' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{'alwaysShowDockDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="theme">{{'theme' | i18n}}</label>
|
||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="help-block">{{'themeDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="locale">{{'language' | i18n}}</label>
|
||||
<select id="locale" name="Locale" [(ngModel)]="locale" (change)="saveLocale()">
|
||||
<option *ngFor="let o of localeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="help-block">{{'languageDesc' | i18n}}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{ 'settings' | i18n }}">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body form">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "security" | i18n }}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<app-vault-timeout-input
|
||||
[vaultTimeouts]="vaultTimeouts"
|
||||
[formControl]="vaultTimeout"
|
||||
ngDefaultControl
|
||||
></app-vault-timeout-input>
|
||||
<div class="form-group">
|
||||
<label>{{ "vaultTimeoutAction" | i18n }}</label>
|
||||
<div class="radio radio-mt-2">
|
||||
<label for="vaultTimeoutActionLock">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLock"
|
||||
value="lock"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "lock" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
|
||||
<div class="radio">
|
||||
<label for="vaultTimeoutActionLogOut">
|
||||
<input
|
||||
type="radio"
|
||||
name="VaultTimeoutAction"
|
||||
id="vaultTimeoutActionLogOut"
|
||||
value="logOut"
|
||||
[(ngModel)]="vaultTimeoutAction"
|
||||
(change)="saveVaultTimeoutOptions()"
|
||||
/>
|
||||
{{ "logOut" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="pin">
|
||||
<input
|
||||
id="pin"
|
||||
type="checkbox"
|
||||
name="PIN"
|
||||
[(ngModel)]="pin"
|
||||
(change)="updatePin()"
|
||||
/>
|
||||
{{ "unlockWithPin" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="biometric">
|
||||
<input
|
||||
id="biometric"
|
||||
type="checkbox"
|
||||
name="biometric"
|
||||
[checked]="biometric"
|
||||
(change)="updateBiometric()"
|
||||
/>
|
||||
{{ biometricText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric">
|
||||
<div class="checkbox">
|
||||
<label for="noAutoPromptBiometrics">
|
||||
<input
|
||||
id="noAutoPromptBiometrics"
|
||||
type="checkbox"
|
||||
name="noAutoPromptBiometrics"
|
||||
[(ngModel)]="noAutoPromptBiometrics"
|
||||
[disabled]="!biometric"
|
||||
(change)="updateNoAutoPromptBiometrics()"
|
||||
/>
|
||||
{{ noAutoPromptBiometricsText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "options" | i18n }}
|
||||
</div>
|
||||
<div class="box-content box-content-padded">
|
||||
<div class="form-group">
|
||||
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
||||
<select
|
||||
id="clearClipboard"
|
||||
name="ClearClipboard"
|
||||
[(ngModel)]="clearClipboard"
|
||||
(change)="saveClearClipboard()"
|
||||
>
|
||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="minimizeOnCopyToClipboard">
|
||||
<input
|
||||
id="minimizeOnCopyToClipboard"
|
||||
type="checkbox"
|
||||
name="MinimizeOnCopyToClipboard"
|
||||
[(ngModel)]="minimizeOnCopyToClipboard"
|
||||
(change)="saveMinOnCopyToClipboard()"
|
||||
/>
|
||||
{{ "minimizeOnCopyToClipboard" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="disableFavicons">
|
||||
<input
|
||||
id="disableFavicons"
|
||||
type="checkbox"
|
||||
name="DisableFavicons"
|
||||
[(ngModel)]="disableFavicons"
|
||||
(change)="saveFavicons()"
|
||||
/>
|
||||
{{ "disableFavicon" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegration">
|
||||
<input
|
||||
id="enableBrowserIntegration"
|
||||
type="checkbox"
|
||||
name="EnableBrowserIntegration"
|
||||
[(ngModel)]="enableBrowserIntegration"
|
||||
(change)="saveBrowserIntegration()"
|
||||
/>
|
||||
{{ "enableBrowserIntegration" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "enableBrowserIntegrationDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableBrowserIntegrationFingerprint">
|
||||
<input
|
||||
id="enableBrowserIntegrationFingerprint"
|
||||
type="checkbox"
|
||||
name="EnableBrowserIntegrationFingerprint"
|
||||
[(ngModel)]="enableBrowserIntegrationFingerprint"
|
||||
(change)="saveBrowserIntegrationFingerprint()"
|
||||
[disabled]="!enableBrowserIntegration"
|
||||
/>
|
||||
{{ "enableBrowserIntegrationFingerprint" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{
|
||||
"enableBrowserIntegrationFingerprintDesc" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableTray">
|
||||
<input
|
||||
id="enableTray"
|
||||
type="checkbox"
|
||||
name="EnableTray"
|
||||
[(ngModel)]="enableTray"
|
||||
(change)="saveTray()"
|
||||
/>
|
||||
{{ enableTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ enableTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showMinToTray">
|
||||
<div class="checkbox">
|
||||
<label for="enableMinToTray">
|
||||
<input
|
||||
id="enableMinToTray"
|
||||
type="checkbox"
|
||||
name="EnableMinToTray"
|
||||
[(ngModel)]="enableMinToTray"
|
||||
(change)="saveMinToTray()"
|
||||
/>
|
||||
{{ enableMinToTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ enableMinToTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="enableCloseToTray">
|
||||
<input
|
||||
id="enableCloseToTray"
|
||||
type="checkbox"
|
||||
name="EnableCloseToTray"
|
||||
[(ngModel)]="enableCloseToTray"
|
||||
(change)="saveCloseToTray()"
|
||||
/>
|
||||
{{ enableCloseToTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ enableCloseToTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="startToTray">
|
||||
<input
|
||||
id="startToTray"
|
||||
type="checkbox"
|
||||
name="StartToTray"
|
||||
[(ngModel)]="startToTray"
|
||||
(change)="saveStartToTray()"
|
||||
/>
|
||||
{{ startToTrayText }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ startToTrayDescText }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="openAtLogin">
|
||||
<input
|
||||
id="openAtLogin"
|
||||
type="checkbox"
|
||||
name="OpenAtLogin"
|
||||
[(ngModel)]="openAtLogin"
|
||||
(change)="saveOpenAtLogin()"
|
||||
/>
|
||||
{{ "openAtLogin" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "openAtLoginDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="showAlwaysShowDock">
|
||||
<div class="checkbox">
|
||||
<label for="alwaysShowDock">
|
||||
<input
|
||||
id="alwaysShowDock"
|
||||
type="checkbox"
|
||||
name="AlwaysShowDock"
|
||||
[(ngModel)]="alwaysShowDock"
|
||||
(change)="saveAlwaysShowDock()"
|
||||
/>
|
||||
{{ "alwaysShowDock" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block">{{ "alwaysShowDockDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="theme">{{ "theme" | i18n }}</label>
|
||||
<select id="theme" name="Theme" [(ngModel)]="theme" (change)="saveTheme()">
|
||||
<option *ngFor="let o of themeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "themeDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="locale">{{ "language" | i18n }}</label>
|
||||
<select id="locale" name="Locale" [(ngModel)]="locale" (change)="saveLocale()">
|
||||
<option *ngFor="let o of localeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "languageDesc" | i18n }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,360 +1,393 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
import { debounceTime } from "rxjs/operators";
|
||||
|
||||
import { DeviceType } from 'jslib-common/enums/deviceType';
|
||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||
import { DeviceType } from "jslib-common/enums/deviceType";
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { SetPinComponent } from '../components/set-pin.component';
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
import { isWindowsStore } from 'jslib-electron/utils';
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { isWindowsStore } from "jslib-electron/utils";
|
||||
|
||||
import { StorageLocation } from 'jslib-common/enums/storageLocation';
|
||||
import { StorageLocation } from "jslib-common/enums/storageLocation";
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: 'settings.component.html',
|
||||
selector: "app-settings",
|
||||
templateUrl: "settings.component.html",
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
disableFavicons: boolean = false;
|
||||
enableBrowserIntegration: boolean = false;
|
||||
enableBrowserIntegrationFingerprint: boolean = false;
|
||||
enableMinToTray: boolean = false;
|
||||
enableCloseToTray: boolean = false;
|
||||
enableTray: boolean = false;
|
||||
showMinToTray: boolean = false;
|
||||
startToTray: boolean = false;
|
||||
minimizeOnCopyToClipboard: boolean = false;
|
||||
locale: string;
|
||||
vaultTimeouts: any[];
|
||||
localeOptions: any[];
|
||||
theme: string;
|
||||
themeOptions: any[];
|
||||
clearClipboard: number;
|
||||
clearClipboardOptions: any[];
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean;
|
||||
biometricText: string;
|
||||
noAutoPromptBiometrics: boolean;
|
||||
noAutoPromptBiometricsText: string;
|
||||
alwaysShowDock: boolean;
|
||||
showAlwaysShowDock: boolean = false;
|
||||
openAtLogin: boolean;
|
||||
requireEnableTray: boolean = false;
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
disableFavicons: boolean = false;
|
||||
enableBrowserIntegration: boolean = false;
|
||||
enableBrowserIntegrationFingerprint: boolean = false;
|
||||
enableMinToTray: boolean = false;
|
||||
enableCloseToTray: boolean = false;
|
||||
enableTray: boolean = false;
|
||||
showMinToTray: boolean = false;
|
||||
startToTray: boolean = false;
|
||||
minimizeOnCopyToClipboard: boolean = false;
|
||||
locale: string;
|
||||
vaultTimeouts: any[];
|
||||
localeOptions: any[];
|
||||
theme: string;
|
||||
themeOptions: any[];
|
||||
clearClipboard: number;
|
||||
clearClipboardOptions: any[];
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean;
|
||||
biometricText: string;
|
||||
noAutoPromptBiometrics: boolean;
|
||||
noAutoPromptBiometricsText: string;
|
||||
alwaysShowDock: boolean;
|
||||
showAlwaysShowDock: boolean = false;
|
||||
openAtLogin: boolean;
|
||||
requireEnableTray: boolean = false;
|
||||
|
||||
enableTrayText: string;
|
||||
enableTrayDescText: string;
|
||||
enableMinToTrayText: string;
|
||||
enableMinToTrayDescText: string;
|
||||
enableCloseToTrayText: string;
|
||||
enableCloseToTrayDescText: string;
|
||||
startToTrayText: string;
|
||||
startToTrayDescText: string;
|
||||
enableTrayText: string;
|
||||
enableTrayDescText: string;
|
||||
enableMinToTrayText: string;
|
||||
enableMinToTrayDescText: string;
|
||||
enableCloseToTrayText: string;
|
||||
enableCloseToTrayDescText: string;
|
||||
startToTrayText: string;
|
||||
startToTrayDescText: string;
|
||||
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
|
||||
constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
|
||||
private vaultTimeoutService: VaultTimeoutService, private stateService: StateService,
|
||||
private messagingService: MessagingService, private cryptoService: CryptoService,
|
||||
private modalService: ModalService) {
|
||||
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private stateService: StateService,
|
||||
private messagingService: MessagingService,
|
||||
private cryptoService: CryptoService,
|
||||
private modalService: ModalService
|
||||
) {
|
||||
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
|
||||
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
|
||||
this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
||||
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
|
||||
this.requireEnableTray = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
||||
|
||||
const trayKey = isMac ? 'enableMenuBar' : 'enableTray';
|
||||
this.enableTrayText = this.i18nService.t(trayKey);
|
||||
this.enableTrayDescText = this.i18nService.t(trayKey + 'Desc');
|
||||
const trayKey = isMac ? "enableMenuBar" : "enableTray";
|
||||
this.enableTrayText = this.i18nService.t(trayKey);
|
||||
this.enableTrayDescText = this.i18nService.t(trayKey + "Desc");
|
||||
|
||||
const minToTrayKey = isMac ? 'enableMinToMenuBar' : 'enableMinToTray';
|
||||
this.enableMinToTrayText = this.i18nService.t(minToTrayKey);
|
||||
this.enableMinToTrayDescText = this.i18nService.t(minToTrayKey + 'Desc');
|
||||
const minToTrayKey = isMac ? "enableMinToMenuBar" : "enableMinToTray";
|
||||
this.enableMinToTrayText = this.i18nService.t(minToTrayKey);
|
||||
this.enableMinToTrayDescText = this.i18nService.t(minToTrayKey + "Desc");
|
||||
|
||||
const closeToTrayKey = isMac ? 'enableCloseToMenuBar' : 'enableCloseToTray';
|
||||
this.enableCloseToTrayText = this.i18nService.t(closeToTrayKey);
|
||||
this.enableCloseToTrayDescText = this.i18nService.t(closeToTrayKey + 'Desc');
|
||||
const closeToTrayKey = isMac ? "enableCloseToMenuBar" : "enableCloseToTray";
|
||||
this.enableCloseToTrayText = this.i18nService.t(closeToTrayKey);
|
||||
this.enableCloseToTrayDescText = this.i18nService.t(closeToTrayKey + "Desc");
|
||||
|
||||
const startToTrayKey = isMac ? 'startToMenuBar' : 'startToTray';
|
||||
this.startToTrayText = this.i18nService.t(startToTrayKey);
|
||||
this.startToTrayDescText = this.i18nService.t(startToTrayKey + 'Desc');
|
||||
const startToTrayKey = isMac ? "startToMenuBar" : "startToTray";
|
||||
this.startToTrayText = this.i18nService.t(startToTrayKey);
|
||||
this.startToTrayDescText = this.i18nService.t(startToTrayKey + "Desc");
|
||||
|
||||
this.vaultTimeouts = [
|
||||
// { name: i18nService.t('immediately'), value: 0 },
|
||||
{ name: i18nService.t('oneMinute'), value: 1 },
|
||||
{ name: i18nService.t('fiveMinutes'), value: 5 },
|
||||
{ name: i18nService.t('fifteenMinutes'), value: 15 },
|
||||
{ name: i18nService.t('thirtyMinutes'), value: 30 },
|
||||
{ name: i18nService.t('oneHour'), value: 60 },
|
||||
{ name: i18nService.t('fourHours'), value: 240 },
|
||||
{ name: i18nService.t('onIdle'), value: -4 },
|
||||
{ name: i18nService.t('onSleep'), value: -3 },
|
||||
];
|
||||
this.vaultTimeouts = [
|
||||
// { name: i18nService.t('immediately'), value: 0 },
|
||||
{ name: i18nService.t("oneMinute"), value: 1 },
|
||||
{ name: i18nService.t("fiveMinutes"), value: 5 },
|
||||
{ name: i18nService.t("fifteenMinutes"), value: 15 },
|
||||
{ name: i18nService.t("thirtyMinutes"), value: 30 },
|
||||
{ name: i18nService.t("oneHour"), value: 60 },
|
||||
{ name: i18nService.t("fourHours"), value: 240 },
|
||||
{ name: i18nService.t("onIdle"), value: -4 },
|
||||
{ name: i18nService.t("onSleep"), value: -3 },
|
||||
];
|
||||
|
||||
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
|
||||
this.vaultTimeouts.push({ name: i18nService.t('onLocked'), value: -2 });
|
||||
}
|
||||
|
||||
this.vaultTimeouts = this.vaultTimeouts.concat([
|
||||
{ name: i18nService.t('onRestart'), value: -1 },
|
||||
{ name: i18nService.t('never'), value: null },
|
||||
]);
|
||||
|
||||
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
|
||||
this.saveVaultTimeoutOptions();
|
||||
});
|
||||
|
||||
const localeOptions: any[] = [];
|
||||
i18nService.supportedTranslationLocales.forEach(locale => {
|
||||
let name = locale;
|
||||
if (i18nService.localeNames.has(locale)) {
|
||||
name += (' - ' + i18nService.localeNames.get(locale));
|
||||
}
|
||||
localeOptions.push({ name: name, value: locale });
|
||||
});
|
||||
localeOptions.sort(Utils.getSortFunction(i18nService, 'name'));
|
||||
localeOptions.splice(0, 0, { name: i18nService.t('default'), value: null });
|
||||
this.localeOptions = localeOptions;
|
||||
|
||||
this.themeOptions = [
|
||||
{ name: i18nService.t('default'), value: null },
|
||||
{ name: i18nService.t('light'), value: ThemeType.Light },
|
||||
{ name: i18nService.t('dark'), value: ThemeType.Dark },
|
||||
{ name: 'Nord', value: ThemeType.Nord },
|
||||
];
|
||||
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t('never'), value: null },
|
||||
{ name: i18nService.t('tenSeconds'), value: 10 },
|
||||
{ name: i18nService.t('twentySeconds'), value: 20 },
|
||||
{ name: i18nService.t('thirtySeconds'), value: 30 },
|
||||
{ name: i18nService.t('oneMinute'), value: 60 },
|
||||
{ name: i18nService.t('twoMinutes'), value: 120 },
|
||||
{ name: i18nService.t('fiveMinutes'), value: 300 },
|
||||
];
|
||||
if (this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop) {
|
||||
this.vaultTimeouts.push({ name: i18nService.t("onLocked"), value: -2 });
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||
this.vaultTimeout.setValue(await this.stateService.getVaultTimeout());
|
||||
this.vaultTimeoutAction = await this.stateService.getVaultTimeoutAction();
|
||||
const pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pin = pinSet[0] || pinSet[1];
|
||||
this.disableFavicons = await this.stateService.getDisableFavicon();
|
||||
this.enableBrowserIntegration = await this.stateService.getEnableBrowserIntegration();
|
||||
this.enableBrowserIntegrationFingerprint = await this.stateService.getEnableBrowserIntegrationFingerprint();
|
||||
this.enableMinToTray = await this.stateService.getEnableMinimizeToTray();
|
||||
this.enableCloseToTray = await this.stateService.getEnableCloseToTray();
|
||||
this.enableTray = await this.stateService.getEnableTray();
|
||||
this.startToTray = await this.stateService.getEnableStartToTray();
|
||||
this.locale = await this.stateService.getLocale();
|
||||
this.theme = await this.stateService.getTheme();
|
||||
this.clearClipboard = await this.stateService.getClearClipboard();
|
||||
this.minimizeOnCopyToClipboard = await this.stateService.getMinimizeOnCopyToClipboard();
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.biometricText = await this.stateService.getBiometricText();
|
||||
this.noAutoPromptBiometrics = await this.stateService.getNoAutoPromptBiometrics();
|
||||
this.noAutoPromptBiometricsText = await this.stateService.getNoAutoPromptBiometricsText();
|
||||
this.alwaysShowDock = await this.stateService.getAlwaysShowDock();
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.openAtLogin = await this.stateService.getOpenAtLogin();
|
||||
this.vaultTimeouts = this.vaultTimeouts.concat([
|
||||
{ name: i18nService.t("onRestart"), value: -1 },
|
||||
{ name: i18nService.t("never"), value: null },
|
||||
]);
|
||||
|
||||
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
|
||||
this.saveVaultTimeoutOptions();
|
||||
});
|
||||
|
||||
const localeOptions: any[] = [];
|
||||
i18nService.supportedTranslationLocales.forEach((locale) => {
|
||||
let name = locale;
|
||||
if (i18nService.localeNames.has(locale)) {
|
||||
name += " - " + i18nService.localeNames.get(locale);
|
||||
}
|
||||
localeOptions.push({ name: name, value: locale });
|
||||
});
|
||||
localeOptions.sort(Utils.getSortFunction(i18nService, "name"));
|
||||
localeOptions.splice(0, 0, { name: i18nService.t("default"), value: null });
|
||||
this.localeOptions = localeOptions;
|
||||
|
||||
this.themeOptions = [
|
||||
{ name: i18nService.t("default"), value: null },
|
||||
{ name: i18nService.t("light"), value: ThemeType.Light },
|
||||
{ name: i18nService.t("dark"), value: ThemeType.Dark },
|
||||
{ name: "Nord", value: ThemeType.Nord },
|
||||
];
|
||||
|
||||
this.clearClipboardOptions = [
|
||||
{ name: i18nService.t("never"), value: null },
|
||||
{ name: i18nService.t("tenSeconds"), value: 10 },
|
||||
{ name: i18nService.t("twentySeconds"), value: 20 },
|
||||
{ name: i18nService.t("thirtySeconds"), value: 30 },
|
||||
{ name: i18nService.t("oneMinute"), value: 60 },
|
||||
{ name: i18nService.t("twoMinutes"), value: 120 },
|
||||
{ name: i18nService.t("fiveMinutes"), value: 300 },
|
||||
];
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||
this.vaultTimeout.setValue(await this.stateService.getVaultTimeout());
|
||||
this.vaultTimeoutAction = await this.stateService.getVaultTimeoutAction();
|
||||
const pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pin = pinSet[0] || pinSet[1];
|
||||
this.disableFavicons = await this.stateService.getDisableFavicon();
|
||||
this.enableBrowserIntegration = await this.stateService.getEnableBrowserIntegration();
|
||||
this.enableBrowserIntegrationFingerprint =
|
||||
await this.stateService.getEnableBrowserIntegrationFingerprint();
|
||||
this.enableMinToTray = await this.stateService.getEnableMinimizeToTray();
|
||||
this.enableCloseToTray = await this.stateService.getEnableCloseToTray();
|
||||
this.enableTray = await this.stateService.getEnableTray();
|
||||
this.startToTray = await this.stateService.getEnableStartToTray();
|
||||
this.locale = await this.stateService.getLocale();
|
||||
this.theme = await this.stateService.getTheme();
|
||||
this.clearClipboard = await this.stateService.getClearClipboard();
|
||||
this.minimizeOnCopyToClipboard = await this.stateService.getMinimizeOnCopyToClipboard();
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.biometricText = await this.stateService.getBiometricText();
|
||||
this.noAutoPromptBiometrics = await this.stateService.getNoAutoPromptBiometrics();
|
||||
this.noAutoPromptBiometricsText = await this.stateService.getNoAutoPromptBiometricsText();
|
||||
this.alwaysShowDock = await this.stateService.getAlwaysShowDock();
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.openAtLogin = await this.stateService.getOpenAtLogin();
|
||||
}
|
||||
|
||||
async saveVaultTimeoutOptions() {
|
||||
if (this.vaultTimeoutAction === "logOut") {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("vaultTimeoutLogOutConfirmation"),
|
||||
this.i18nService.t("vaultTimeoutLogOutConfirmationTitle"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("cancel"),
|
||||
"warning"
|
||||
);
|
||||
if (!confirmed) {
|
||||
this.vaultTimeoutAction = "lock";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async saveVaultTimeoutOptions() {
|
||||
if (this.vaultTimeoutAction === 'logOut') {
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('vaultTimeoutLogOutConfirmation'),
|
||||
this.i18nService.t('vaultTimeoutLogOutConfirmationTitle'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'), 'warning');
|
||||
if (!confirmed) {
|
||||
this.vaultTimeoutAction = 'lock';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid saving 0 since it's useless as a timeout value.
|
||||
if (this.vaultTimeout.value === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutTooLarge'));
|
||||
return;
|
||||
}
|
||||
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
// Avoid saving 0 since it's useless as a timeout value.
|
||||
if (this.vaultTimeout.value === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
}
|
||||
if (!this.pin) {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("vaultTimeoutTooLarge")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
async updateBiometric() {
|
||||
const current = this.biometric;
|
||||
if (this.biometric) {
|
||||
this.biometric = false;
|
||||
} else if (this.supportsBiometric) {
|
||||
this.biometric = await this.platformUtilsService.authenticateBiometric();
|
||||
}
|
||||
if (this.biometric === current) {
|
||||
return;
|
||||
}
|
||||
if (this.biometric) {
|
||||
await this.stateService.setBiometricUnlock(true);
|
||||
} else {
|
||||
await this.stateService.setBiometricUnlock(null);
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
await this.stateService.setBiometricLocked(false);
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(
|
||||
this.vaultTimeout.value,
|
||||
this.vaultTimeoutAction
|
||||
);
|
||||
}
|
||||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
}
|
||||
if (!this.pin) {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async updateBiometric() {
|
||||
const current = this.biometric;
|
||||
if (this.biometric) {
|
||||
this.biometric = false;
|
||||
} else if (this.supportsBiometric) {
|
||||
this.biometric = await this.platformUtilsService.authenticateBiometric();
|
||||
}
|
||||
if (this.biometric === current) {
|
||||
return;
|
||||
}
|
||||
if (this.biometric) {
|
||||
await this.stateService.setBiometricUnlock(true);
|
||||
} else {
|
||||
await this.stateService.setBiometricUnlock(null);
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
await this.stateService.setBiometricLocked(false);
|
||||
await this.cryptoService.toggleKey();
|
||||
}
|
||||
|
||||
async updateNoAutoPromptBiometrics() {
|
||||
if (!this.biometric) {
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
|
||||
async updateNoAutoPromptBiometrics() {
|
||||
if (!this.biometric) {
|
||||
this.noAutoPromptBiometrics = false;
|
||||
}
|
||||
if (this.noAutoPromptBiometrics) {
|
||||
await this.stateService.setNoAutoPromptBiometrics(true);
|
||||
} else {
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.noAutoPromptBiometrics) {
|
||||
await this.stateService.setNoAutoPromptBiometrics(true);
|
||||
} else {
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
}
|
||||
async saveFavicons() {
|
||||
await this.stateService.setDisableFavicon(this.disableFavicons);
|
||||
await this.stateService.setDisableFavicon(this.disableFavicons, {
|
||||
storageLocation: StorageLocation.Disk,
|
||||
});
|
||||
this.messagingService.send("refreshCiphers");
|
||||
}
|
||||
|
||||
async saveMinToTray() {
|
||||
await this.stateService.setEnableMinimizeToTray(this.enableMinToTray);
|
||||
}
|
||||
|
||||
async saveCloseToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
}
|
||||
|
||||
async saveFavicons() {
|
||||
await this.stateService.setDisableFavicon(this.disableFavicons);
|
||||
await this.stateService.setDisableFavicon(this.disableFavicons, { storageLocation: StorageLocation.Disk });
|
||||
this.messagingService.send('refreshCiphers');
|
||||
}
|
||||
await this.stateService.setEnableCloseToTray(this.enableCloseToTray);
|
||||
}
|
||||
|
||||
async saveMinToTray() {
|
||||
await this.stateService.setEnableMinimizeToTray(this.enableMinToTray);
|
||||
}
|
||||
|
||||
async saveCloseToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
}
|
||||
|
||||
await this.stateService.setEnableCloseToTray(this.enableCloseToTray);
|
||||
}
|
||||
|
||||
async saveTray() {
|
||||
if (this.requireEnableTray && !this.enableTray && (this.startToTray || this.enableCloseToTray)) {
|
||||
const confirm = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('confirmTrayDesc'), this.i18nService.t('confirmTrayTitle'),
|
||||
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||
|
||||
if (confirm) {
|
||||
this.startToTray = false;
|
||||
await this.stateService.setEnableStartToTray(this.startToTray);
|
||||
this.enableCloseToTray = false;
|
||||
await this.stateService.setEnableCloseToTray(this.enableCloseToTray);
|
||||
} else {
|
||||
this.enableTray = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
this.messagingService.send(this.enableTray ? 'showTray' : 'removeTray');
|
||||
}
|
||||
|
||||
async saveStartToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
}
|
||||
async saveTray() {
|
||||
if (
|
||||
this.requireEnableTray &&
|
||||
!this.enableTray &&
|
||||
(this.startToTray || this.enableCloseToTray)
|
||||
) {
|
||||
const confirm = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("confirmTrayDesc"),
|
||||
this.i18nService.t("confirmTrayTitle"),
|
||||
this.i18nService.t("yes"),
|
||||
this.i18nService.t("no"),
|
||||
"warning"
|
||||
);
|
||||
|
||||
if (confirm) {
|
||||
this.startToTray = false;
|
||||
await this.stateService.setEnableStartToTray(this.startToTray);
|
||||
this.enableCloseToTray = false;
|
||||
await this.stateService.setEnableCloseToTray(this.enableCloseToTray);
|
||||
} else {
|
||||
this.enableTray = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async saveLocale() {
|
||||
await this.stateService.setLocale(this.locale);
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
this.messagingService.send(this.enableTray ? "showTray" : "removeTray");
|
||||
}
|
||||
|
||||
async saveStartToTray() {
|
||||
if (this.requireEnableTray) {
|
||||
this.enableTray = true;
|
||||
await this.stateService.setEnableTray(this.enableTray);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
await this.stateService.setTheme(this.theme);
|
||||
window.setTimeout(() => window.location.reload(), 200);
|
||||
await this.stateService.setEnableStartToTray(this.startToTray);
|
||||
}
|
||||
|
||||
async saveLocale() {
|
||||
await this.stateService.setLocale(this.locale);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
await this.stateService.setTheme(this.theme);
|
||||
window.setTimeout(() => window.location.reload(), 200);
|
||||
}
|
||||
|
||||
async saveMinOnCopyToClipboard() {
|
||||
await this.stateService.setMinimizeOnCopyToClipboard(this.minimizeOnCopyToClipboard);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
await this.stateService.setClearClipboard(this.clearClipboard);
|
||||
}
|
||||
|
||||
async saveAlwaysShowDock() {
|
||||
await this.stateService.setAlwaysShowDock(this.alwaysShowDock);
|
||||
}
|
||||
|
||||
async saveOpenAtLogin() {
|
||||
this.stateService.setOpenAtLogin(this.openAtLogin);
|
||||
this.messagingService.send(this.openAtLogin ? "addOpenAtLogin" : "removeOpenAtLogin");
|
||||
}
|
||||
|
||||
async saveBrowserIntegration() {
|
||||
if (process.platform === "darwin" && !this.platformUtilsService.isMacAppStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("browserIntegrationMasOnlyDesc"),
|
||||
this.i18nService.t("browserIntegrationMasOnlyTitle"),
|
||||
this.i18nService.t("ok"),
|
||||
null,
|
||||
"warning"
|
||||
);
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
} else if (isWindowsStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("browserIntegrationWindowsStoreDesc"),
|
||||
this.i18nService.t("browserIntegrationWindowsStoreTitle"),
|
||||
this.i18nService.t("ok"),
|
||||
null,
|
||||
"warning"
|
||||
);
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
}
|
||||
|
||||
async saveMinOnCopyToClipboard() {
|
||||
await this.stateService.setMinimizeOnCopyToClipboard(this.minimizeOnCopyToClipboard);
|
||||
await this.stateService.setEnableBrowserIntegration(this.enableBrowserIntegration);
|
||||
this.messagingService.send(
|
||||
this.enableBrowserIntegration ? "enableBrowserIntegration" : "disableBrowserIntegration"
|
||||
);
|
||||
|
||||
if (!this.enableBrowserIntegration) {
|
||||
this.enableBrowserIntegrationFingerprint = false;
|
||||
this.saveBrowserIntegrationFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
await this.stateService.setClearClipboard(this.clearClipboard);
|
||||
}
|
||||
|
||||
async saveAlwaysShowDock() {
|
||||
await this.stateService.setAlwaysShowDock(this.alwaysShowDock);
|
||||
}
|
||||
|
||||
async saveOpenAtLogin() {
|
||||
this.stateService.setOpenAtLogin(this.openAtLogin);
|
||||
this.messagingService.send(this.openAtLogin ? 'addOpenAtLogin' : 'removeOpenAtLogin');
|
||||
}
|
||||
|
||||
async saveBrowserIntegration() {
|
||||
if (process.platform === 'darwin' && !this.platformUtilsService.isMacAppStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('browserIntegrationMasOnlyDesc'),
|
||||
this.i18nService.t('browserIntegrationMasOnlyTitle'),
|
||||
this.i18nService.t('ok'), null, 'warning');
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
} else if (isWindowsStore()) {
|
||||
await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('browserIntegrationWindowsStoreDesc'),
|
||||
this.i18nService.t('browserIntegrationWindowsStoreTitle'),
|
||||
this.i18nService.t('ok'), null, 'warning');
|
||||
|
||||
this.enableBrowserIntegration = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.stateService.setEnableBrowserIntegration(this.enableBrowserIntegration);
|
||||
this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration');
|
||||
|
||||
if (!this.enableBrowserIntegration) {
|
||||
this.enableBrowserIntegrationFingerprint = false;
|
||||
this.saveBrowserIntegrationFingerprint();
|
||||
}
|
||||
}
|
||||
|
||||
async saveBrowserIntegrationFingerprint() {
|
||||
await this.stateService.setEnableBrowserIntegrationFingerprint(this.enableBrowserIntegrationFingerprint);
|
||||
}
|
||||
async saveBrowserIntegrationFingerprint() {
|
||||
await this.stateService.setEnableBrowserIntegrationFingerprint(
|
||||
this.enableBrowserIntegrationFingerprint
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
<form id="sso-page" (ngSubmit)="submit()">
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden">
|
||||
<div class="box">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
{{'loading' | i18n}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<img class="logo-image" alt="Bitwarden" />
|
||||
<div class="box">
|
||||
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
{{ "loading" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,40 +1,56 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component';
|
||||
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-sso',
|
||||
templateUrl: 'sso.component.html',
|
||||
selector: "app-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
})
|
||||
export class SsoComponent extends BaseSsoComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, syncService: SyncService, route: ActivatedRoute,
|
||||
stateService: StateService, platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService, cryptoFunctionService: CryptoFunctionService,
|
||||
environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService,
|
||||
logService: LogService) {
|
||||
super(authService, router, i18nService, route, stateService, platformUtilsService,
|
||||
apiService, cryptoFunctionService, environmentService, passwordGenerationService, logService);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
this.redirectUri = 'bitwarden://sso-callback';
|
||||
this.clientId = 'desktop';
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
syncService: SyncService,
|
||||
route: ActivatedRoute,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
environmentService: EnvironmentService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
route,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
apiService,
|
||||
cryptoFunctionService,
|
||||
environmentService,
|
||||
passwordGenerationService,
|
||||
logService
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
this.redirectUri = "bitwarden://sso-callback";
|
||||
this.clientId = "desktop";
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,33 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="twoStepTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="twoStepTitle">
|
||||
{{'twoStepOptions' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a href="#" appStopClick *ngFor="let p of providers" class="box-content-row"
|
||||
(click)="choose(p)">
|
||||
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right">
|
||||
<span class="text">{{p.name}}</span>
|
||||
<span class="detail">{{p.description}}</span>
|
||||
</a>
|
||||
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{'recoveryCodeTitle' | i18n}}</span>
|
||||
<span class="detail">{{'recoveryCodeDesc' | i18n}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="twoStepTitle">
|
||||
{{ "twoStepOptions" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
*ngFor="let p of providers"
|
||||
class="box-content-row"
|
||||
(click)="choose(p)"
|
||||
>
|
||||
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right" />
|
||||
<span class="text">{{ p.name }}</span>
|
||||
<span class="detail">{{ p.description }}</span>
|
||||
</a>
|
||||
<a href="#" appStopClick class="box-content-row" (click)="recover()">
|
||||
<span class="text">{{ "recoveryCodeTitle" | i18n }}</span>
|
||||
<span class="detail">{{ "recoveryCodeDesc" | i18n }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,21 +1,23 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent,
|
||||
} from 'jslib-angular/components/two-factor-options.component';
|
||||
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor-options',
|
||||
templateUrl: 'two-factor-options.component.html',
|
||||
selector: "app-two-factor-options",
|
||||
templateUrl: "two-factor-options.component.html",
|
||||
})
|
||||
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
|
||||
super(authService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService
|
||||
) {
|
||||
super(authService, router, i18nService, platformUtilsService, window);
|
||||
}
|
||||
}
|
||||
|
@ -1,89 +1,139 @@
|
||||
<form id="two-factor-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise" attr.aria-hidden="{{showingModal}}">
|
||||
<div class="content">
|
||||
<h1>{{title}}</h1>
|
||||
<p *ngIf="selectedProviderType === providerType.Authenticator">{{'enterVerificationCodeApp' | i18n}}</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email">
|
||||
{{'enterVerificationCodeEmail' | i18n : twoFactorEmail}}
|
||||
</p>
|
||||
<div class="box last"
|
||||
*ngIf="selectedProviderType === providerType.Email || selectedProviderType === providerType.Authenticator">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code">{{'verificationCode' | i18n}}</label>
|
||||
<input id="code" type="text" name="Code" [(ngModel)]="token" required appAutofocus appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
id="two-factor-page"
|
||||
#form
|
||||
(ngSubmit)="submit()"
|
||||
[appApiAction]="formPromise"
|
||||
attr.aria-hidden="{{ showingModal }}"
|
||||
>
|
||||
<div class="content">
|
||||
<h1>{{ title }}</h1>
|
||||
<p *ngIf="selectedProviderType === providerType.Authenticator">
|
||||
{{ "enterVerificationCodeApp" | i18n }}
|
||||
</p>
|
||||
<p *ngIf="selectedProviderType === providerType.Email">
|
||||
{{ "enterVerificationCodeEmail" | i18n: twoFactorEmail }}
|
||||
</p>
|
||||
<div
|
||||
class="box last"
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Email ||
|
||||
selectedProviderType === providerType.Authenticator
|
||||
"
|
||||
>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="code"
|
||||
type="text"
|
||||
name="Code"
|
||||
[(ngModel)]="token"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<p>{{'insertYubiKey' | i18n}}</p>
|
||||
<img src="../../images/yubikey.jpg" alt="">
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code" class="sr-only">{{'verificationCode' | i18n}}</label>
|
||||
<input id="code" type="password" name="Code" [(ngModel)]="token" required appAutofocus
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn">
|
||||
<div id="web-authn-frame">
|
||||
<iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe>
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo">
|
||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{'rememberMe' | i18n}}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box last" *ngIf="selectedProviderType == null">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<p>{{'noTwoStepProviders' | i18n}}</p>
|
||||
<p>{{'noTwoStepProviders2' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick *ngIf="selectedProviderType != null && selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo">
|
||||
<span [hidden]="form.loading"><i class="fa fa-sign-in" aria-hidden="true"></i>
|
||||
{{'continue' | i18n}}</span>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{'cancel' | i18n}}</a>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{'useAnotherTwoStepMethod' | i18n}}</a>
|
||||
<a href="#" appStopClick (click)="sendEmail(true)" [appApiAction]="emailPromise" role="button"
|
||||
*ngIf="selectedProviderType === providerType.Email">
|
||||
{{'sendVerificationCodeEmailAgain' | i18n}}
|
||||
</a>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||
<p>{{ "insertYubiKey" | i18n }}</p>
|
||||
<img src="../../images/yubikey.jpg" alt="" />
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="code" class="sr-only">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="code"
|
||||
type="password"
|
||||
name="Code"
|
||||
[(ngModel)]="token"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn">
|
||||
<div id="web-authn-frame">
|
||||
<iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe>
|
||||
</div>
|
||||
<div class="box first">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
*ngIf="
|
||||
selectedProviderType === providerType.Duo ||
|
||||
selectedProviderType === providerType.OrganizationDuo
|
||||
"
|
||||
>
|
||||
<div id="duo-frame"><iframe id="duo_iframe"></iframe></div>
|
||||
<div class="box last">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="remember">{{ "rememberMe" | i18n }}</label>
|
||||
<input id="remember" type="checkbox" name="Remember" [(ngModel)]="remember" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="box last" *ngIf="selectedProviderType == null">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<p>{{ "noTwoStepProviders" | i18n }}</p>
|
||||
<p>{{ "noTwoStepProviders2" | i18n }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn primary block"
|
||||
[disabled]="form.loading"
|
||||
appBlurClick
|
||||
*ngIf="
|
||||
selectedProviderType != null &&
|
||||
selectedProviderType !== providerType.Duo &&
|
||||
selectedProviderType !== providerType.OrganizationDuo
|
||||
"
|
||||
>
|
||||
<span [hidden]="form.loading"
|
||||
><i class="fa fa-sign-in" aria-hidden="true"></i> {{ "continue" | i18n }}</span
|
||||
>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
|
||||
</div>
|
||||
<div class="sub-options">
|
||||
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{
|
||||
"useAnotherTwoStepMethod" | i18n
|
||||
}}</a>
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="sendEmail(true)"
|
||||
[appApiAction]="emailPromise"
|
||||
role="button"
|
||||
*ngIf="selectedProviderType === providerType.Email"
|
||||
>
|
||||
{{ "sendVerificationCodeEmailAgain" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ng-template #twoFactorOptions></ng-template>
|
||||
|
@ -1,70 +1,84 @@
|
||||
import {
|
||||
Component,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
|
||||
import {
|
||||
ActivatedRoute,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { TwoFactorOptionsComponent } from './two-factor-options.component';
|
||||
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
|
||||
|
||||
import { TwoFactorProviderType } from 'jslib-common/enums/twoFactorProviderType';
|
||||
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
import { ModalService } from "jslib-angular/services/modal.service";
|
||||
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib-angular/components/two-factor.component';
|
||||
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor',
|
||||
templateUrl: 'two-factor.component.html',
|
||||
selector: "app-two-factor",
|
||||
templateUrl: "two-factor.component.html",
|
||||
})
|
||||
export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
@ViewChild('twoFactorOptions', { read: ViewContainerRef, static: true }) twoFactorOptionsModal: ViewContainerRef;
|
||||
@ViewChild("twoFactorOptions", { read: ViewContainerRef, static: true })
|
||||
twoFactorOptionsModal: ViewContainerRef;
|
||||
|
||||
showingModal = false;
|
||||
showingModal = false;
|
||||
|
||||
constructor(authService: AuthService, router: Router,
|
||||
i18nService: I18nService, apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService, syncService: SyncService,
|
||||
environmentService: EnvironmentService, private modalService: ModalService,
|
||||
stateService: StateService, route: ActivatedRoute,
|
||||
logService: LogService) {
|
||||
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService,
|
||||
stateService, route, logService);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
constructor(
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
apiService: ApiService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
syncService: SyncService,
|
||||
environmentService: EnvironmentService,
|
||||
private modalService: ModalService,
|
||||
stateService: StateService,
|
||||
route: ActivatedRoute,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
apiService,
|
||||
platformUtilsService,
|
||||
window,
|
||||
environmentService,
|
||||
stateService,
|
||||
route,
|
||||
logService
|
||||
);
|
||||
super.onSuccessfulLogin = () => {
|
||||
return syncService.fullSync(true);
|
||||
};
|
||||
}
|
||||
|
||||
async anotherMethod() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(TwoFactorOptionsComponent, this.twoFactorOptionsModal);
|
||||
async anotherMethod() {
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
TwoFactorOptionsComponent,
|
||||
this.twoFactorOptionsModal
|
||||
);
|
||||
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
modal.onShown.subscribe(() => {
|
||||
this.showingModal = true;
|
||||
});
|
||||
modal.onClosed.subscribe(() => {
|
||||
this.showingModal = false;
|
||||
});
|
||||
|
||||
childComponent.onProviderSelected.subscribe(async (provider: TwoFactorProviderType) => {
|
||||
modal.close();
|
||||
this.selectedProviderType = provider;
|
||||
await this.init();
|
||||
});
|
||||
childComponent.onRecoverSelected.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
childComponent.onProviderSelected.subscribe(async (provider: TwoFactorProviderType) => {
|
||||
modal.close();
|
||||
this.selectedProviderType = provider;
|
||||
await this.init();
|
||||
});
|
||||
childComponent.onRecoverSelected.subscribe(() => {
|
||||
modal.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +1,124 @@
|
||||
<form id="update-temp-password-page" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<app-callout type="warning" title="{{'updateMasterPassword' | i18n}}">
|
||||
{{'updateMasterPasswordWarning' | i18n}}
|
||||
</app-callout>
|
||||
<app-callout type="info" [enforcedPolicyOptions]="enforcedPolicyOptions" *ngIf="enforcedPolicyOptions">
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{'masterPass' | i18n}}
|
||||
<strong class="sub-label text-{{masterPasswordScoreStyle.Color}}"
|
||||
*ngIf="masterPasswordScoreStyle.Text">
|
||||
{{masterPasswordScoreStyle.Text}}
|
||||
</strong>
|
||||
</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
|
||||
[appAutofocus]="masterPassword === ''" (input)="updatePasswordStrength()"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{masterPasswordScoreStyle.Color}}" role="progressbar"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
|
||||
[ngStyle]="{width: (masterPasswordScoreStyle.Width + '%')}"
|
||||
attr.aria-valuenow="{{masterPasswordScoreStyle.Width}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<app-callout type="warning" title="{{ 'updateMasterPassword' | i18n }}">
|
||||
{{ "updateMasterPasswordWarning" | i18n }}
|
||||
</app-callout>
|
||||
<app-callout
|
||||
type="info"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
*ngIf="enforcedPolicyOptions"
|
||||
>
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<div class="box-content-row-flex">
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">
|
||||
{{ "masterPass" | i18n }}
|
||||
<strong
|
||||
class="sub-label text-{{ masterPasswordScoreStyle.Color }}"
|
||||
*ngIf="masterPasswordScoreStyle.Text"
|
||||
>
|
||||
{{ masterPasswordScoreStyle.Text }}
|
||||
</strong>
|
||||
</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
[appAutofocus]="masterPassword === ''"
|
||||
(input)="updatePasswordStrength()"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
|
||||
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="masterPasswordRetype" required
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(false)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar bg-{{ masterPasswordScoreStyle.Color }}"
|
||||
role="progressbar"
|
||||
aria-valuenow="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
[ngStyle]="{ width: masterPasswordScoreStyle.Width + '%' }"
|
||||
attr.aria-valuenow="{{ masterPasswordScoreStyle.Width }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{'masterPassHint' | i18n}}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'masterPassHintDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{'submit' | i18n}}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a (click)="logOut()" class="btn block">{{'logOut' | i18n}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPasswordRetype"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPasswordRetype"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPasswordRetype"
|
||||
required
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword(true)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="hint">{{ "masterPassHint" | i18n }}</label>
|
||||
<input id="hint" type="text" name="Hint" [(ngModel)]="hint" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "masterPassHintDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
|
||||
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
|
||||
<i class="fa fa-spinner fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<a (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,65 +1,81 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from 'jslib-angular/components/update-temp-password.component';
|
||||
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
|
||||
|
||||
interface MasterPasswordScore {
|
||||
Color: string;
|
||||
Text: string;
|
||||
Width: number;
|
||||
Color: string;
|
||||
Text: string;
|
||||
Width: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-update-temp-password',
|
||||
templateUrl: 'update-temp-password.component.html',
|
||||
selector: "app-update-temp-password",
|
||||
templateUrl: "update-temp-password.component.html",
|
||||
})
|
||||
|
||||
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return {
|
||||
Color: 'bg-success',
|
||||
Text: 'strong',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
Color: 'bg-primary',
|
||||
Text: 'good',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
Color: 'bg-warning',
|
||||
Text: 'weak',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
Color: 'bg-danger',
|
||||
Text: 'weak',
|
||||
Width: scoreWidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
|
||||
cryptoService: CryptoService, messagingService: MessagingService,
|
||||
apiService: ApiService, syncService: SyncService,
|
||||
logService: LogService, stateService: StateService) {
|
||||
super(i18nService, platformUtilsService, passwordGenerationService, policyService, cryptoService,
|
||||
messagingService, apiService, stateService, syncService, logService);
|
||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
switch (this.masterPasswordScore) {
|
||||
case 4:
|
||||
return {
|
||||
Color: "bg-success",
|
||||
Text: "strong",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 3:
|
||||
return {
|
||||
Color: "bg-primary",
|
||||
Text: "good",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
case 2:
|
||||
return {
|
||||
Color: "bg-warning",
|
||||
Text: "weak",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
Color: "bg-danger",
|
||||
Text: "weak",
|
||||
Width: scoreWidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
policyService: PolicyService,
|
||||
cryptoService: CryptoService,
|
||||
messagingService: MessagingService,
|
||||
apiService: ApiService,
|
||||
syncService: SyncService,
|
||||
logService: LogService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
policyService,
|
||||
cryptoService,
|
||||
messagingService,
|
||||
apiService,
|
||||
stateService,
|
||||
syncService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,46 @@
|
||||
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
|
||||
{{'vaultTimeoutPolicyInEffect' | i18n : vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes}}
|
||||
{{ "vaultTimeoutPolicyInEffect" | i18n: vaultTimeoutPolicyHours:vaultTimeoutPolicyMinutes }}
|
||||
</app-callout>
|
||||
|
||||
<div [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
||||
<select id="vaultTimeout" name="VaultTimeout" formControlName="vaultTimeout" class="form-control" appAutofocus>
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">{{'vaultTimeoutDesc' | i18n}}</small>
|
||||
<div class="form-group">
|
||||
<label for="vaultTimeout">{{ "vaultTimeout" | i18n }}</label>
|
||||
<select
|
||||
id="vaultTimeout"
|
||||
name="VaultTimeout"
|
||||
formControlName="vaultTimeout"
|
||||
class="form-control"
|
||||
appAutofocus
|
||||
>
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">{{ "vaultTimeoutDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
|
||||
<div class="col">
|
||||
<label for="hours">{{ "hours" | i18n }}</label>
|
||||
<input
|
||||
id="hours"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
name="hours"
|
||||
formControlName="hours"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
|
||||
<div class="col">
|
||||
<label for="hours">{{'hours' | i18n}}</label>
|
||||
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="minutes">{{'minutes' | i18n}}</label>
|
||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes" formControlName="minutes">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="minutes">{{ "minutes" | i18n }}</label>
|
||||
<input
|
||||
id="minutes"
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0"
|
||||
max="59"
|
||||
name="minutes"
|
||||
formControlName="minutes"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group"></div> <!-- Styling fix -->
|
||||
</div>
|
||||
<div class="form-group"></div>
|
||||
<!-- Styling fix -->
|
||||
</div>
|
||||
|
@ -1,28 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
} from '@angular/forms';
|
||||
import { Component } from "@angular/core";
|
||||
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import {
|
||||
VaultTimeoutInputComponent as VaultTimeoutInputComponentBase
|
||||
} from 'jslib-angular/components/settings/vault-timeout-input.component';
|
||||
import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "jslib-angular/components/settings/vault-timeout-input.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-timeout-input',
|
||||
templateUrl: 'vault-timeout-input.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
selector: "app-vault-timeout-input",
|
||||
templateUrl: "vault-timeout-input.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {
|
||||
}
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
|
||||
|
@ -1,72 +1,71 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import {
|
||||
RouterModule,
|
||||
Routes,
|
||||
} from '@angular/router';
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuardService } from 'jslib-angular/services/auth-guard.service';
|
||||
import { LockGuardService } from 'jslib-angular/services/lock-guard.service';
|
||||
import { LoginGuardService } from '../services/loginGuard.service';
|
||||
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
|
||||
import { LockGuardService } from "jslib-angular/services/lock-guard.service";
|
||||
import { LoginGuardService } from "../services/loginGuard.service";
|
||||
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
||||
import { SsoComponent } from './accounts/sso.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
||||
import { HintComponent } from "./accounts/hint.component";
|
||||
import { LockComponent } from "./accounts/lock.component";
|
||||
import { LoginComponent } from "./accounts/login.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||
|
||||
import { SendComponent } from './send/send.component';
|
||||
import { SendComponent } from "./send/send.component";
|
||||
|
||||
import { VaultComponent } from './vault/vault.component';
|
||||
import { VaultComponent } from "./vault/vault.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: '/vault', pathMatch: 'full' },
|
||||
{
|
||||
path: 'lock',
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuardService],
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent,
|
||||
canActivate: [LoginGuardService],
|
||||
},
|
||||
{ path: '2fa', component: TwoFactorComponent },
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{
|
||||
path: 'vault',
|
||||
component: VaultComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{ path: 'hint', component: HintComponent },
|
||||
{ path: 'set-password', component: SetPasswordComponent },
|
||||
{ path: 'sso', component: SsoComponent },
|
||||
{
|
||||
path: 'send',
|
||||
component: SendComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: 'update-temp-password',
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: 'remove-password',
|
||||
component: RemovePasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { titleId: 'removeMasterPassword' },
|
||||
},
|
||||
{ path: "", redirectTo: "/vault", pathMatch: "full" },
|
||||
{
|
||||
path: "lock",
|
||||
component: LockComponent,
|
||||
canActivate: [LockGuardService],
|
||||
},
|
||||
{
|
||||
path: "login",
|
||||
component: LoginComponent,
|
||||
canActivate: [LoginGuardService],
|
||||
},
|
||||
{ path: "2fa", component: TwoFactorComponent },
|
||||
{ path: "register", component: RegisterComponent },
|
||||
{
|
||||
path: "vault",
|
||||
component: VaultComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{ path: "hint", component: HintComponent },
|
||||
{ path: "set-password", component: SetPasswordComponent },
|
||||
{ path: "sso", component: SsoComponent },
|
||||
{
|
||||
path: "send",
|
||||
component: SendComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: "update-temp-password",
|
||||
component: UpdateTempPasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
},
|
||||
{
|
||||
path: "remove-password",
|
||||
component: RemovePasswordComponent,
|
||||
canActivate: [AuthGuardService],
|
||||
data: { titleId: "removeMasterPassword" },
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
/*enableTracing: true,*/
|
||||
})],
|
||||
exports: [RouterModule],
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
useHash: true,
|
||||
/*enableTracing: true,*/
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,257 +1,257 @@
|
||||
import 'zone.js/dist/zone';
|
||||
import "zone.js/dist/zone";
|
||||
|
||||
import { A11yModule } from '@angular/cdk/a11y';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import 'zone.js/dist/zone';
|
||||
import { A11yModule } from "@angular/cdk/a11y";
|
||||
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||
import { OverlayModule } from "@angular/cdk/overlay";
|
||||
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||
import { DatePipe } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { BrowserModule } from "@angular/platform-browser";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import "zone.js/dist/zone";
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { ServicesModule } from './services.module';
|
||||
import { AppRoutingModule } from "./app-routing.module";
|
||||
import { ServicesModule } from "./services.module";
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppComponent } from "./app.component";
|
||||
|
||||
import { EnvironmentComponent } from './accounts/environment.component';
|
||||
import { HintComponent } from './accounts/hint.component';
|
||||
import { LockComponent } from './accounts/lock.component';
|
||||
import { LoginComponent } from './accounts/login.component';
|
||||
import { PremiumComponent } from './accounts/premium.component';
|
||||
import { RegisterComponent } from './accounts/register.component';
|
||||
import { RemovePasswordComponent } from './accounts/remove-password.component';
|
||||
import { SetPasswordComponent } from './accounts/set-password.component';
|
||||
import { SettingsComponent } from './accounts/settings.component';
|
||||
import { SsoComponent } from './accounts/sso.component';
|
||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
|
||||
import { VaultTimeoutInputComponent } from './accounts/vault-timeout-input.component';
|
||||
import { EnvironmentComponent } from "./accounts/environment.component";
|
||||
import { HintComponent } from "./accounts/hint.component";
|
||||
import { LockComponent } from "./accounts/lock.component";
|
||||
import { LoginComponent } from "./accounts/login.component";
|
||||
import { PremiumComponent } from "./accounts/premium.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SettingsComponent } from "./accounts/settings.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
import { TwoFactorOptionsComponent } from "./accounts/two-factor-options.component";
|
||||
import { TwoFactorComponent } from "./accounts/two-factor.component";
|
||||
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
|
||||
import { VaultTimeoutInputComponent } from "./accounts/vault-timeout-input.component";
|
||||
|
||||
import { AvatarComponent } from 'jslib-angular/components/avatar.component';
|
||||
import { CalloutComponent } from 'jslib-angular/components/callout.component';
|
||||
import { IconComponent } from 'jslib-angular/components/icon.component';
|
||||
import { BitwardenToastModule } from 'jslib-angular/components/toastr.component';
|
||||
import { AvatarComponent } from "jslib-angular/components/avatar.component";
|
||||
import { CalloutComponent } from "jslib-angular/components/callout.component";
|
||||
import { IconComponent } from "jslib-angular/components/icon.component";
|
||||
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
|
||||
|
||||
import { A11yTitleDirective } from 'jslib-angular/directives/a11y-title.directive';
|
||||
import { ApiActionDirective } from 'jslib-angular/directives/api-action.directive';
|
||||
import { AutofocusDirective } from 'jslib-angular/directives/autofocus.directive';
|
||||
import { BlurClickDirective } from 'jslib-angular/directives/blur-click.directive';
|
||||
import { BoxRowDirective } from 'jslib-angular/directives/box-row.directive';
|
||||
import { CipherListVirtualScroll } from 'jslib-angular/directives/cipherListVirtualScroll.directive';
|
||||
import { FallbackSrcDirective } from 'jslib-angular/directives/fallback-src.directive';
|
||||
import { InputVerbatimDirective } from 'jslib-angular/directives/input-verbatim.directive';
|
||||
import { SelectCopyDirective } from 'jslib-angular/directives/select-copy.directive';
|
||||
import { StopClickDirective } from 'jslib-angular/directives/stop-click.directive';
|
||||
import { StopPropDirective } from 'jslib-angular/directives/stop-prop.directive';
|
||||
import { TrueFalseValueDirective } from 'jslib-angular/directives/true-false-value.directive';
|
||||
import { A11yTitleDirective } from "jslib-angular/directives/a11y-title.directive";
|
||||
import { ApiActionDirective } from "jslib-angular/directives/api-action.directive";
|
||||
import { AutofocusDirective } from "jslib-angular/directives/autofocus.directive";
|
||||
import { BlurClickDirective } from "jslib-angular/directives/blur-click.directive";
|
||||
import { BoxRowDirective } from "jslib-angular/directives/box-row.directive";
|
||||
import { CipherListVirtualScroll } from "jslib-angular/directives/cipherListVirtualScroll.directive";
|
||||
import { FallbackSrcDirective } from "jslib-angular/directives/fallback-src.directive";
|
||||
import { InputVerbatimDirective } from "jslib-angular/directives/input-verbatim.directive";
|
||||
import { SelectCopyDirective } from "jslib-angular/directives/select-copy.directive";
|
||||
import { StopClickDirective } from "jslib-angular/directives/stop-click.directive";
|
||||
import { StopPropDirective } from "jslib-angular/directives/stop-prop.directive";
|
||||
import { TrueFalseValueDirective } from "jslib-angular/directives/true-false-value.directive";
|
||||
|
||||
import { ColorPasswordPipe } from 'jslib-angular/pipes/color-password.pipe';
|
||||
import { I18nPipe } from 'jslib-angular/pipes/i18n.pipe';
|
||||
import { SearchCiphersPipe } from 'jslib-angular/pipes/search-ciphers.pipe';
|
||||
import { ColorPasswordPipe } from "jslib-angular/pipes/color-password.pipe";
|
||||
import { I18nPipe } from "jslib-angular/pipes/i18n.pipe";
|
||||
import { SearchCiphersPipe } from "jslib-angular/pipes/search-ciphers.pipe";
|
||||
|
||||
import { AddEditCustomFieldsComponent } from './vault/add-edit-custom-fields.component';
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CollectionsComponent } from './vault/collections.component';
|
||||
import { ExportComponent } from './vault/export.component';
|
||||
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
|
||||
import { PasswordGeneratorComponent } from './vault/password-generator.component';
|
||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { VaultComponent } from './vault/vault.component';
|
||||
import { ViewCustomFieldsComponent } from './vault/view-custom-fields.component';
|
||||
import { ViewComponent } from './vault/view.component';
|
||||
import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component";
|
||||
import { AddEditComponent } from "./vault/add-edit.component";
|
||||
import { AttachmentsComponent } from "./vault/attachments.component";
|
||||
import { CiphersComponent } from "./vault/ciphers.component";
|
||||
import { CollectionsComponent } from "./vault/collections.component";
|
||||
import { ExportComponent } from "./vault/export.component";
|
||||
import { FolderAddEditComponent } from "./vault/folder-add-edit.component";
|
||||
import { GroupingsComponent } from "./vault/groupings.component";
|
||||
import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-history.component";
|
||||
import { PasswordGeneratorComponent } from "./vault/password-generator.component";
|
||||
import { PasswordHistoryComponent } from "./vault/password-history.component";
|
||||
import { ShareComponent } from "./vault/share.component";
|
||||
import { VaultComponent } from "./vault/vault.component";
|
||||
import { ViewCustomFieldsComponent } from "./vault/view-custom-fields.component";
|
||||
import { ViewComponent } from "./vault/view.component";
|
||||
|
||||
import { AddEditComponent as SendAddEditComponent } from './send/add-edit.component';
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from './send/efflux-dates.component';
|
||||
import { SendComponent } from './send/send.component';
|
||||
import { AddEditComponent as SendAddEditComponent } from "./send/add-edit.component";
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from "./send/efflux-dates.component";
|
||||
import { SendComponent } from "./send/send.component";
|
||||
|
||||
import { AccountSwitcherComponent } from './layout/account-switcher.component';
|
||||
import { HeaderComponent } from './layout/header.component';
|
||||
import { NavComponent } from './layout/nav.component';
|
||||
import { SearchComponent } from './layout/search/search.component';
|
||||
import { AccountSwitcherComponent } from "./layout/account-switcher.component";
|
||||
import { HeaderComponent } from "./layout/header.component";
|
||||
import { NavComponent } from "./layout/nav.component";
|
||||
import { SearchComponent } from "./layout/search/search.component";
|
||||
|
||||
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
||||
import { SetPinComponent } from './components/set-pin.component';
|
||||
import { VerifyMasterPasswordComponent } from './components/verify-master-password.component';
|
||||
import { PasswordRepromptComponent } from "./components/password-reprompt.component";
|
||||
import { SetPinComponent } from "./components/set-pin.component";
|
||||
import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component";
|
||||
|
||||
import { registerLocaleData } from '@angular/common';
|
||||
import localeAf from '@angular/common/locales/af';
|
||||
import localeAz from '@angular/common/locales/az';
|
||||
import localeBe from '@angular/common/locales/be';
|
||||
import localeBg from '@angular/common/locales/bg';
|
||||
import localeBn from '@angular/common/locales/bn';
|
||||
import localeCa from '@angular/common/locales/ca';
|
||||
import localeCs from '@angular/common/locales/cs';
|
||||
import localeDa from '@angular/common/locales/da';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
import localeEl from '@angular/common/locales/el';
|
||||
import localeEnGb from '@angular/common/locales/en-GB';
|
||||
import localeEnIn from '@angular/common/locales/en-IN';
|
||||
import localeEs from '@angular/common/locales/es';
|
||||
import localeEt from '@angular/common/locales/et';
|
||||
import localeFa from '@angular/common/locales/fa';
|
||||
import localeFi from '@angular/common/locales/fi';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import localeHe from '@angular/common/locales/he';
|
||||
import localeHr from '@angular/common/locales/hr';
|
||||
import localeHu from '@angular/common/locales/hu';
|
||||
import localeId from '@angular/common/locales/id';
|
||||
import localeIt from '@angular/common/locales/it';
|
||||
import localeJa from '@angular/common/locales/ja';
|
||||
import localeKn from '@angular/common/locales/kn';
|
||||
import localeKo from '@angular/common/locales/ko';
|
||||
import localeLv from '@angular/common/locales/lv';
|
||||
import localeMl from '@angular/common/locales/ml';
|
||||
import localeNb from '@angular/common/locales/nb';
|
||||
import localeNl from '@angular/common/locales/nl';
|
||||
import localePl from '@angular/common/locales/pl';
|
||||
import localePtBr from '@angular/common/locales/pt';
|
||||
import localePtPt from '@angular/common/locales/pt-PT';
|
||||
import localeRo from '@angular/common/locales/ro';
|
||||
import localeRu from '@angular/common/locales/ru';
|
||||
import localeSk from '@angular/common/locales/sk';
|
||||
import localeSr from '@angular/common/locales/sr';
|
||||
import localeMe from '@angular/common/locales/sr-Latn-ME';
|
||||
import localeSv from '@angular/common/locales/sv';
|
||||
import localeTh from '@angular/common/locales/th';
|
||||
import localeTr from '@angular/common/locales/tr';
|
||||
import localeUk from '@angular/common/locales/uk';
|
||||
import localeVi from '@angular/common/locales/vi';
|
||||
import localeZhCn from '@angular/common/locales/zh-Hans';
|
||||
import localeZhTw from '@angular/common/locales/zh-Hant';
|
||||
import { registerLocaleData } from "@angular/common";
|
||||
import localeAf from "@angular/common/locales/af";
|
||||
import localeAz from "@angular/common/locales/az";
|
||||
import localeBe from "@angular/common/locales/be";
|
||||
import localeBg from "@angular/common/locales/bg";
|
||||
import localeBn from "@angular/common/locales/bn";
|
||||
import localeCa from "@angular/common/locales/ca";
|
||||
import localeCs from "@angular/common/locales/cs";
|
||||
import localeDa from "@angular/common/locales/da";
|
||||
import localeDe from "@angular/common/locales/de";
|
||||
import localeEl from "@angular/common/locales/el";
|
||||
import localeEnGb from "@angular/common/locales/en-GB";
|
||||
import localeEnIn from "@angular/common/locales/en-IN";
|
||||
import localeEs from "@angular/common/locales/es";
|
||||
import localeEt from "@angular/common/locales/et";
|
||||
import localeFa from "@angular/common/locales/fa";
|
||||
import localeFi from "@angular/common/locales/fi";
|
||||
import localeFr from "@angular/common/locales/fr";
|
||||
import localeHe from "@angular/common/locales/he";
|
||||
import localeHr from "@angular/common/locales/hr";
|
||||
import localeHu from "@angular/common/locales/hu";
|
||||
import localeId from "@angular/common/locales/id";
|
||||
import localeIt from "@angular/common/locales/it";
|
||||
import localeJa from "@angular/common/locales/ja";
|
||||
import localeKn from "@angular/common/locales/kn";
|
||||
import localeKo from "@angular/common/locales/ko";
|
||||
import localeLv from "@angular/common/locales/lv";
|
||||
import localeMl from "@angular/common/locales/ml";
|
||||
import localeNb from "@angular/common/locales/nb";
|
||||
import localeNl from "@angular/common/locales/nl";
|
||||
import localePl from "@angular/common/locales/pl";
|
||||
import localePtBr from "@angular/common/locales/pt";
|
||||
import localePtPt from "@angular/common/locales/pt-PT";
|
||||
import localeRo from "@angular/common/locales/ro";
|
||||
import localeRu from "@angular/common/locales/ru";
|
||||
import localeSk from "@angular/common/locales/sk";
|
||||
import localeSr from "@angular/common/locales/sr";
|
||||
import localeMe from "@angular/common/locales/sr-Latn-ME";
|
||||
import localeSv from "@angular/common/locales/sv";
|
||||
import localeTh from "@angular/common/locales/th";
|
||||
import localeTr from "@angular/common/locales/tr";
|
||||
import localeUk from "@angular/common/locales/uk";
|
||||
import localeVi from "@angular/common/locales/vi";
|
||||
import localeZhCn from "@angular/common/locales/zh-Hans";
|
||||
import localeZhTw from "@angular/common/locales/zh-Hant";
|
||||
|
||||
registerLocaleData(localeAf, 'af');
|
||||
registerLocaleData(localeAz, 'az');
|
||||
registerLocaleData(localeBe, 'be');
|
||||
registerLocaleData(localeBg, 'bg');
|
||||
registerLocaleData(localeBn, 'bn');
|
||||
registerLocaleData(localeCa, 'ca');
|
||||
registerLocaleData(localeCs, 'cs');
|
||||
registerLocaleData(localeDa, 'da');
|
||||
registerLocaleData(localeDe, 'de');
|
||||
registerLocaleData(localeEl, 'el');
|
||||
registerLocaleData(localeEnGb, 'en-GB');
|
||||
registerLocaleData(localeEnIn, 'en-IN');
|
||||
registerLocaleData(localeEs, 'es');
|
||||
registerLocaleData(localeEt, 'et');
|
||||
registerLocaleData(localeFa, 'fa');
|
||||
registerLocaleData(localeFi, 'fi');
|
||||
registerLocaleData(localeFr, 'fr');
|
||||
registerLocaleData(localeHe, 'he');
|
||||
registerLocaleData(localeHr, 'hr');
|
||||
registerLocaleData(localeHu, 'hu');
|
||||
registerLocaleData(localeId, 'id');
|
||||
registerLocaleData(localeIt, 'it');
|
||||
registerLocaleData(localeJa, 'ja');
|
||||
registerLocaleData(localeKn, 'kn');
|
||||
registerLocaleData(localeKo, 'ko');
|
||||
registerLocaleData(localeLv, 'lv');
|
||||
registerLocaleData(localeMe, 'me');
|
||||
registerLocaleData(localeMl, 'ml');
|
||||
registerLocaleData(localeNb, 'nb');
|
||||
registerLocaleData(localeNl, 'nl');
|
||||
registerLocaleData(localePl, 'pl');
|
||||
registerLocaleData(localePtBr, 'pt-BR');
|
||||
registerLocaleData(localePtPt, 'pt-PT');
|
||||
registerLocaleData(localeRo, 'ro');
|
||||
registerLocaleData(localeRu, 'ru');
|
||||
registerLocaleData(localeSk, 'sk');
|
||||
registerLocaleData(localeSr, 'sr');
|
||||
registerLocaleData(localeSv, 'sv');
|
||||
registerLocaleData(localeTh, 'th');
|
||||
registerLocaleData(localeTr, 'tr');
|
||||
registerLocaleData(localeUk, 'uk');
|
||||
registerLocaleData(localeVi, 'vi');
|
||||
registerLocaleData(localeZhCn, 'zh-CN');
|
||||
registerLocaleData(localeZhTw, 'zh-TW');
|
||||
registerLocaleData(localeAf, "af");
|
||||
registerLocaleData(localeAz, "az");
|
||||
registerLocaleData(localeBe, "be");
|
||||
registerLocaleData(localeBg, "bg");
|
||||
registerLocaleData(localeBn, "bn");
|
||||
registerLocaleData(localeCa, "ca");
|
||||
registerLocaleData(localeCs, "cs");
|
||||
registerLocaleData(localeDa, "da");
|
||||
registerLocaleData(localeDe, "de");
|
||||
registerLocaleData(localeEl, "el");
|
||||
registerLocaleData(localeEnGb, "en-GB");
|
||||
registerLocaleData(localeEnIn, "en-IN");
|
||||
registerLocaleData(localeEs, "es");
|
||||
registerLocaleData(localeEt, "et");
|
||||
registerLocaleData(localeFa, "fa");
|
||||
registerLocaleData(localeFi, "fi");
|
||||
registerLocaleData(localeFr, "fr");
|
||||
registerLocaleData(localeHe, "he");
|
||||
registerLocaleData(localeHr, "hr");
|
||||
registerLocaleData(localeHu, "hu");
|
||||
registerLocaleData(localeId, "id");
|
||||
registerLocaleData(localeIt, "it");
|
||||
registerLocaleData(localeJa, "ja");
|
||||
registerLocaleData(localeKn, "kn");
|
||||
registerLocaleData(localeKo, "ko");
|
||||
registerLocaleData(localeLv, "lv");
|
||||
registerLocaleData(localeMe, "me");
|
||||
registerLocaleData(localeMl, "ml");
|
||||
registerLocaleData(localeNb, "nb");
|
||||
registerLocaleData(localeNl, "nl");
|
||||
registerLocaleData(localePl, "pl");
|
||||
registerLocaleData(localePtBr, "pt-BR");
|
||||
registerLocaleData(localePtPt, "pt-PT");
|
||||
registerLocaleData(localeRo, "ro");
|
||||
registerLocaleData(localeRu, "ru");
|
||||
registerLocaleData(localeSk, "sk");
|
||||
registerLocaleData(localeSr, "sr");
|
||||
registerLocaleData(localeSv, "sv");
|
||||
registerLocaleData(localeTh, "th");
|
||||
registerLocaleData(localeTr, "tr");
|
||||
registerLocaleData(localeUk, "uk");
|
||||
registerLocaleData(localeVi, "vi");
|
||||
registerLocaleData(localeZhCn, "zh-CN");
|
||||
registerLocaleData(localeZhTw, "zh-TW");
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ServicesModule,
|
||||
BitwardenToastModule.forRoot({
|
||||
maxOpened: 5,
|
||||
autoDismiss: true,
|
||||
closeButton: true,
|
||||
}),
|
||||
ScrollingModule,
|
||||
A11yModule,
|
||||
OverlayModule,
|
||||
],
|
||||
declarations: [
|
||||
A11yTitleDirective,
|
||||
AddEditComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AttachmentsComponent,
|
||||
AutofocusDirective,
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CalloutComponent,
|
||||
CipherListVirtualScroll,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
ColorPasswordPipe,
|
||||
EnvironmentComponent,
|
||||
ExportComponent,
|
||||
FallbackSrcDirective,
|
||||
FolderAddEditComponent,
|
||||
GroupingsComponent,
|
||||
HintComponent,
|
||||
I18nPipe,
|
||||
IconComponent,
|
||||
InputVerbatimDirective,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
NavComponent,
|
||||
PasswordGeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PasswordHistoryComponent,
|
||||
PasswordRepromptComponent,
|
||||
PremiumComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
SearchCiphersPipe,
|
||||
SelectCopyDirective,
|
||||
SendAddEditComponent,
|
||||
SendComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SetPasswordComponent,
|
||||
SetPinComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
SsoComponent,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
TrueFalseValueDirective,
|
||||
TwoFactorComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
VaultComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyMasterPasswordComponent,
|
||||
ViewComponent,
|
||||
ViewCustomFieldsComponent,
|
||||
HeaderComponent,
|
||||
AccountSwitcherComponent,
|
||||
AvatarComponent,
|
||||
SearchComponent,
|
||||
],
|
||||
providers: [DatePipe],
|
||||
bootstrap: [AppComponent],
|
||||
imports: [
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ServicesModule,
|
||||
BitwardenToastModule.forRoot({
|
||||
maxOpened: 5,
|
||||
autoDismiss: true,
|
||||
closeButton: true,
|
||||
}),
|
||||
ScrollingModule,
|
||||
A11yModule,
|
||||
OverlayModule,
|
||||
],
|
||||
declarations: [
|
||||
A11yTitleDirective,
|
||||
AddEditComponent,
|
||||
AddEditCustomFieldsComponent,
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AttachmentsComponent,
|
||||
AutofocusDirective,
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CalloutComponent,
|
||||
CipherListVirtualScroll,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
ColorPasswordPipe,
|
||||
EnvironmentComponent,
|
||||
ExportComponent,
|
||||
FallbackSrcDirective,
|
||||
FolderAddEditComponent,
|
||||
GroupingsComponent,
|
||||
HintComponent,
|
||||
I18nPipe,
|
||||
IconComponent,
|
||||
InputVerbatimDirective,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
NavComponent,
|
||||
PasswordGeneratorComponent,
|
||||
PasswordGeneratorHistoryComponent,
|
||||
PasswordHistoryComponent,
|
||||
PasswordRepromptComponent,
|
||||
PremiumComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
SearchCiphersPipe,
|
||||
SelectCopyDirective,
|
||||
SendAddEditComponent,
|
||||
SendComponent,
|
||||
SendEffluxDatesComponent,
|
||||
SetPasswordComponent,
|
||||
SetPinComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
SsoComponent,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
TrueFalseValueDirective,
|
||||
TwoFactorComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
VaultComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
VerifyMasterPasswordComponent,
|
||||
ViewComponent,
|
||||
ViewCustomFieldsComponent,
|
||||
HeaderComponent,
|
||||
AccountSwitcherComponent,
|
||||
AvatarComponent,
|
||||
SearchComponent,
|
||||
],
|
||||
providers: [DatePipe],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {}
|
||||
|
@ -1,38 +1,55 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">{{'passwordConfirmation' | i18n}}</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required appAutofocus>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'passwordConfirmationDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">{{ "passwordConfirmation" | i18n }}</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="monospaced"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appAutofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{'ok' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "passwordConfirmationDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{ "ok" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { PasswordRepromptComponent as BasePasswordRepromptComponent } from 'jslib-angular/components/password-reprompt.component';
|
||||
import { PasswordRepromptComponent as BasePasswordRepromptComponent } from "jslib-angular/components/password-reprompt.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: 'password-reprompt.component.html',
|
||||
templateUrl: "password-reprompt.component.html",
|
||||
})
|
||||
export class PasswordRepromptComponent extends BasePasswordRepromptComponent {}
|
||||
|
@ -1,44 +1,65 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable set-pin-modal" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{'setYourPinCode' | i18n}}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="pin">{{'pin' | i18n}}</label>
|
||||
<input id="pin" type="{{showPin ? 'text' : 'password'}}" name="Pin" class="monospaced"
|
||||
[(ngModel)]="pin" required appInputVerbatim appAutofocus>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleVisibility()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPin, 'fa-eye-slash': showPin}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
||||
<label for="masterPasswordOnRestart">
|
||||
<input type="checkbox" id="masterPasswordOnRestart" name="MasterPasswordOnRestart"
|
||||
[(ngModel)]="masterPassOnRestart">
|
||||
<span>{{'lockWithMasterPassOnRestart' | i18n}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="modal-dialog modal-dialog-scrollable set-pin-modal" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{ "setYourPinCode" | i18n }}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="pin">{{ "pin" | i18n }}</label>
|
||||
<input
|
||||
id="pin"
|
||||
type="{{ showPin ? 'text' : 'password' }}"
|
||||
name="Pin"
|
||||
class="monospaced"
|
||||
[(ngModel)]="pin"
|
||||
required
|
||||
appInputVerbatim
|
||||
appAutofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleVisibility()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPin, 'fa-eye-slash': showPin }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{'ok' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
||||
<label for="masterPasswordOnRestart">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="masterPasswordOnRestart"
|
||||
name="MasterPasswordOnRestart"
|
||||
[(ngModel)]="masterPassOnRestart"
|
||||
/>
|
||||
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{ "ok" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { SetPinComponent as BaseSetPinComponent } from 'jslib-angular/components/set-pin.component';
|
||||
import { SetPinComponent as BaseSetPinComponent } from "jslib-angular/components/set-pin.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: 'set-pin.component.html',
|
||||
templateUrl: "set-pin.component.html",
|
||||
})
|
||||
export class SetPinComponent extends BaseSetPinComponent { }
|
||||
export class SetPinComponent extends BaseSetPinComponent {}
|
||||
|
@ -1,25 +1,46 @@
|
||||
<ng-container *ngIf="!usesKeyConnector">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control"
|
||||
[formControl]="secret" required appAutofocus appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="password"
|
||||
name="MasterPasswordHash"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="usesKeyConnector">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label class="d-block">{{'sendVerificationCode' | i18n}}</label>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="requestOTP()" [disabled]="disableRequestOTP">
|
||||
{{'sendCode' | i18n}}
|
||||
</button>
|
||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
|
||||
{{'codeSent' | i18n}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="requestOTP()"
|
||||
[disabled]="disableRequestOTP"
|
||||
>
|
||||
{{ "sendCode" | i18n }}
|
||||
</button>
|
||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
|
||||
{{ "codeSent" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="verificationCode">{{'verificationCode' | i18n}}</label>
|
||||
<input id="verificationCode" type="input" name="verificationCode" class="form-control"
|
||||
[formControl]="secret" required appAutofocus appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="verificationCode"
|
||||
type="input"
|
||||
name="verificationCode"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@ -1,31 +1,23 @@
|
||||
import {
|
||||
animate,
|
||||
style,
|
||||
transition,
|
||||
trigger,
|
||||
} from '@angular/animations';
|
||||
import { Component } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component } from "@angular/core";
|
||||
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import { VerifyMasterPasswordComponent as BaseComponent } from 'jslib-angular/components/verify-master-password.component';
|
||||
import { VerifyMasterPasswordComponent as BaseComponent } from "jslib-angular/components/verify-master-password.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-verify-master-password',
|
||||
templateUrl: 'verify-master-password.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VerifyMasterPasswordComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger('sent', [
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate('100ms', style({ opacity: 1 })),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
selector: "app-verify-master-password",
|
||||
templateUrl: "verify-master-password.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VerifyMasterPasswordComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger("sent", [
|
||||
transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class VerifyMasterPasswordComponent extends BaseComponent { }
|
||||
export class VerifyMasterPasswordComponent extends BaseComponent {}
|
||||
|
@ -1,22 +1,50 @@
|
||||
<a class="account-switcher" (click)="toggle()" cdkOverlayOrigin #trigger="cdkOverlayOrigin" [hidden]="!showSwitcher">
|
||||
<app-avatar [data]="activeAccountEmail" size="25" [circle]="true" [fontSize]="14" [dynamic]="true" *ngIf="activeAccountEmail != null"></app-avatar>
|
||||
<span>{{activeAccountEmail}}</span>
|
||||
<i class="fa" aria-hidden="true" [ngClass]="{'fa-chevron-down': !isOpen, 'fa-chevron-up': isOpen}"></i>
|
||||
<a
|
||||
class="account-switcher"
|
||||
(click)="toggle()"
|
||||
cdkOverlayOrigin
|
||||
#trigger="cdkOverlayOrigin"
|
||||
[hidden]="!showSwitcher"
|
||||
>
|
||||
<app-avatar
|
||||
[data]="activeAccountEmail"
|
||||
size="25"
|
||||
[circle]="true"
|
||||
[fontSize]="14"
|
||||
[dynamic]="true"
|
||||
*ngIf="activeAccountEmail != null"
|
||||
></app-avatar>
|
||||
<span>{{ activeAccountEmail }}</span>
|
||||
<i
|
||||
class="fa"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-chevron-down': !isOpen, 'fa-chevron-up': isOpen }"
|
||||
></i>
|
||||
</a>
|
||||
|
||||
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="trigger" [cdkConnectedOverlayOpen]="isOpen" cdkConnectedOverlayMinWidth="250px">
|
||||
<div class="account-switcher-dropdown" [@transformPanel]="'open'">
|
||||
<div class="accounts">
|
||||
<a *ngFor="let a of accounts | keyvalue" class="account" [ngClass]="{'active': a.value.profile.authenticationStatus == 'active'}"
|
||||
href="#" appStopClick (click)="switch(a.key)">
|
||||
<span class="email">{{a.value.profile.email}}</span>
|
||||
<span class="server">{{a.value.settings.environmentUrls.server}}</span>
|
||||
<span class="status">{{a.value.profile.authenticationStatus}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="border"></div>
|
||||
<a class="add" routerLink="/login" (click)="toggle()">
|
||||
<i class="fa fa-plus" aria-hidden="true"></i> {{'addAccount' | i18n}}
|
||||
</a>
|
||||
<ng-template
|
||||
cdkConnectedOverlay
|
||||
[cdkConnectedOverlayOrigin]="trigger"
|
||||
[cdkConnectedOverlayOpen]="isOpen"
|
||||
cdkConnectedOverlayMinWidth="250px"
|
||||
>
|
||||
<div class="account-switcher-dropdown" [@transformPanel]="'open'">
|
||||
<div class="accounts">
|
||||
<a
|
||||
*ngFor="let a of accounts | keyvalue"
|
||||
class="account"
|
||||
[ngClass]="{ active: a.value.profile.authenticationStatus == 'active' }"
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="switch(a.key)"
|
||||
>
|
||||
<span class="email">{{ a.value.profile.email }}</span>
|
||||
<span class="server">{{ a.value.settings.environmentUrls.server }}</span>
|
||||
<span class="status">{{ a.value.profile.authenticationStatus }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="border"></div>
|
||||
<a class="add" routerLink="/login" (click)="toggle()">
|
||||
<i class="fa fa-plus" aria-hidden="true"></i> {{ "addAccount" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
@ -1,78 +1,87 @@
|
||||
import {
|
||||
animate,
|
||||
state,
|
||||
style,
|
||||
transition,
|
||||
trigger,
|
||||
} from '@angular/animations';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { animate, state, style, transition, trigger } from "@angular/animations";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { AuthenticationStatus } from 'jslib-common/enums/authenticationStatus';
|
||||
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
|
||||
|
||||
import { Account } from 'jslib-common/models/domain/account';
|
||||
import { Account } from "jslib-common/models/domain/account";
|
||||
|
||||
@Component({
|
||||
selector: 'app-account-switcher',
|
||||
templateUrl: 'account-switcher.component.html',
|
||||
animations: [
|
||||
trigger('transformPanel', [
|
||||
state('void', style({
|
||||
opacity: 0,
|
||||
})),
|
||||
transition('void => open', animate('100ms linear', style({
|
||||
opacity: 1,
|
||||
}))),
|
||||
transition('* => void', animate('100ms linear', style({opacity: 0}))),
|
||||
]),
|
||||
],
|
||||
selector: "app-account-switcher",
|
||||
templateUrl: "account-switcher.component.html",
|
||||
animations: [
|
||||
trigger("transformPanel", [
|
||||
state(
|
||||
"void",
|
||||
style({
|
||||
opacity: 0,
|
||||
})
|
||||
),
|
||||
transition(
|
||||
"void => open",
|
||||
animate(
|
||||
"100ms linear",
|
||||
style({
|
||||
opacity: 1,
|
||||
})
|
||||
)
|
||||
),
|
||||
transition("* => void", animate("100ms linear", style({ opacity: 0 }))),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class AccountSwitcherComponent implements OnInit {
|
||||
isOpen: boolean = false;
|
||||
accounts: { [userId: string]: Account } = {};
|
||||
activeAccountEmail: string;
|
||||
isOpen: boolean = false;
|
||||
accounts: { [userId: string]: Account } = {};
|
||||
activeAccountEmail: string;
|
||||
|
||||
get showSwitcher() {
|
||||
return this.accounts != null && Object.keys(this.accounts).length > 0;
|
||||
}
|
||||
get showSwitcher() {
|
||||
return this.accounts != null && Object.keys(this.accounts).length > 0;
|
||||
}
|
||||
|
||||
constructor(private stateService: StateService, private vaultTimeoutService: VaultTimeoutService,
|
||||
private messagingService: MessagingService, private router: Router) {}
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private messagingService: MessagingService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.stateService.accounts.subscribe(async accounts => {
|
||||
for (const userId in accounts) {
|
||||
if (userId === await this.stateService.getUserId()) {
|
||||
accounts[userId].profile.authenticationStatus = AuthenticationStatus.Active;
|
||||
} else {
|
||||
accounts[userId].profile.authenticationStatus = await this.vaultTimeoutService.isLocked(userId) ?
|
||||
AuthenticationStatus.Locked :
|
||||
AuthenticationStatus.Unlocked;
|
||||
}
|
||||
}
|
||||
|
||||
this.accounts = accounts;
|
||||
this.activeAccountEmail = await this.stateService.getEmail();
|
||||
});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
}
|
||||
|
||||
async switch(userId: string) {
|
||||
await this.stateService.setActiveUser(userId);
|
||||
const locked = await this.vaultTimeoutService.isLocked(userId);
|
||||
if (locked) {
|
||||
this.messagingService.send('locked', { userId: userId });
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.stateService.accounts.subscribe(async (accounts) => {
|
||||
for (const userId in accounts) {
|
||||
if (userId === (await this.stateService.getUserId())) {
|
||||
accounts[userId].profile.authenticationStatus = AuthenticationStatus.Active;
|
||||
} else {
|
||||
this.messagingService.send('unlocked');
|
||||
this.messagingService.send('syncVault');
|
||||
this.router.navigate(['vault']);
|
||||
accounts[userId].profile.authenticationStatus = (await this.vaultTimeoutService.isLocked(
|
||||
userId
|
||||
))
|
||||
? AuthenticationStatus.Locked
|
||||
: AuthenticationStatus.Unlocked;
|
||||
}
|
||||
}
|
||||
|
||||
this.accounts = accounts;
|
||||
this.activeAccountEmail = await this.stateService.getEmail();
|
||||
});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
}
|
||||
|
||||
async switch(userId: string) {
|
||||
await this.stateService.setActiveUser(userId);
|
||||
const locked = await this.vaultTimeoutService.isLocked(userId);
|
||||
if (locked) {
|
||||
this.messagingService.send("locked", { userId: userId });
|
||||
} else {
|
||||
this.messagingService.send("unlocked");
|
||||
this.messagingService.send("syncVault");
|
||||
this.router.navigate(["vault"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="header">
|
||||
<app-search></app-search>
|
||||
<app-account-switcher></app-account-switcher>
|
||||
<app-search></app-search>
|
||||
<app-account-switcher></app-account-switcher>
|
||||
</div>
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: 'header.component.html',
|
||||
selector: "app-header",
|
||||
templateUrl: "header.component.html",
|
||||
})
|
||||
export class HeaderComponent {
|
||||
}
|
||||
export class HeaderComponent {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<ng-container *ngFor="let item of items">
|
||||
<a [routerLink]="item.link" class="btn primary" routerLinkActive="active" [title]="item.label">
|
||||
<i class="fa" [ngClass]="item.icon"></i>{{ item.label }}
|
||||
</a>
|
||||
<a [routerLink]="item.link" class="btn primary" routerLinkActive="active" [title]="item.label">
|
||||
<i class="fa" [ngClass]="item.icon"></i>{{ item.label }}
|
||||
</a>
|
||||
</ng-container>
|
||||
|
@ -1,23 +1,23 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { Component } from "@angular/core";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-nav',
|
||||
templateUrl: 'nav.component.html',
|
||||
selector: "app-nav",
|
||||
templateUrl: "nav.component.html",
|
||||
})
|
||||
export class NavComponent {
|
||||
items: any[] = [
|
||||
{
|
||||
link: '/vault',
|
||||
icon: 'fa-lock',
|
||||
label: this.i18nService.translate('myVault'),
|
||||
},
|
||||
{
|
||||
link: '/send',
|
||||
icon: 'fa-paper-plane',
|
||||
label: 'Send',
|
||||
},
|
||||
];
|
||||
items: any[] = [
|
||||
{
|
||||
link: "/vault",
|
||||
icon: "fa-lock",
|
||||
label: this.i18nService.translate("myVault"),
|
||||
},
|
||||
{
|
||||
link: "/send",
|
||||
icon: "fa-paper-plane",
|
||||
label: "Send",
|
||||
},
|
||||
];
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
constructor(private i18nService: I18nService) {}
|
||||
}
|
||||
|
@ -1,39 +1,38 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Injectable } from "@angular/core";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
export type SearchBarState = {
|
||||
enabled: boolean;
|
||||
placeholderText: string;
|
||||
enabled: boolean;
|
||||
placeholderText: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class SearchBarService {
|
||||
searchText = new BehaviorSubject<string>(null);
|
||||
|
||||
searchText = new BehaviorSubject<string>(null);
|
||||
private _state = {
|
||||
enabled: false,
|
||||
placeholderText: "",
|
||||
};
|
||||
|
||||
private _state = {
|
||||
enabled: false,
|
||||
placeholderText: '',
|
||||
};
|
||||
// tslint:disable-next-line:member-ordering
|
||||
state = new BehaviorSubject<SearchBarState>(this._state);
|
||||
|
||||
// tslint:disable-next-line:member-ordering
|
||||
state = new BehaviorSubject<SearchBarState>(this._state);
|
||||
setEnabled(enabled: boolean) {
|
||||
this._state.enabled = enabled;
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
setEnabled(enabled: boolean) {
|
||||
this._state.enabled = enabled;
|
||||
this.updateState();
|
||||
}
|
||||
setPlaceholderText(placeholderText: string) {
|
||||
this._state.placeholderText = placeholderText;
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
setPlaceholderText(placeholderText: string) {
|
||||
this._state.placeholderText = placeholderText;
|
||||
this.updateState();
|
||||
}
|
||||
setSearchText(value: string) {
|
||||
this.searchText.next(value);
|
||||
}
|
||||
|
||||
setSearchText(value: string) {
|
||||
this.searchText.next(value);
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
this.state.next(this._state);
|
||||
}
|
||||
private updateState() {
|
||||
this.state.next(this._state);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
<div class="search" *ngIf="state.enabled">
|
||||
<input type="search" [placeholder]="state.placeholderText" id="search" autocomplete="off" [formControl]="searchText" appAutofocus>
|
||||
<i class="fa fa-search" aria-hidden="true"></i>
|
||||
<input
|
||||
type="search"
|
||||
[placeholder]="state.placeholderText"
|
||||
id="search"
|
||||
autocomplete="off"
|
||||
[formControl]="searchText"
|
||||
appAutofocus
|
||||
/>
|
||||
<i class="fa fa-search" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
@ -1,24 +1,23 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Component } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
|
||||
import { SearchBarService, SearchBarState } from './search-bar.service';
|
||||
import { SearchBarService, SearchBarState } from "./search-bar.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-search',
|
||||
templateUrl: 'search.component.html',
|
||||
selector: "app-search",
|
||||
templateUrl: "search.component.html",
|
||||
})
|
||||
export class SearchComponent {
|
||||
state: SearchBarState;
|
||||
searchText: FormControl = new FormControl(null);
|
||||
|
||||
state: SearchBarState;
|
||||
searchText: FormControl = new FormControl(null);
|
||||
constructor(private searchBarService: SearchBarService) {
|
||||
this.searchBarService.state.subscribe((state) => {
|
||||
this.state = state;
|
||||
});
|
||||
|
||||
constructor(private searchBarService: SearchBarService) {
|
||||
this.searchBarService.state.subscribe(state => {
|
||||
this.state = state;
|
||||
});
|
||||
|
||||
this.searchText.valueChanges.subscribe(value => {
|
||||
this.searchBarService.setSearchText(value);
|
||||
});
|
||||
}
|
||||
this.searchText.valueChanges.subscribe((value) => {
|
||||
this.searchBarService.setSearchText(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
import { isDev } from 'jslib-electron/utils';
|
||||
import { isDev } from "jslib-electron/utils";
|
||||
|
||||
// tslint:disable-next-line
|
||||
require('../scss/styles.scss');
|
||||
require("../scss/styles.scss");
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
import { AppModule } from "./app.module";
|
||||
|
||||
if (!isDev()) {
|
||||
enableProdMode();
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
||||
|
||||
// Disable drag and drop to prevent malicious links from executing in the context of the app
|
||||
document.addEventListener('dragover', event => event.preventDefault());
|
||||
document.addEventListener('drop', event => event.preventDefault());
|
||||
document.addEventListener("dragover", (event) => event.preventDefault());
|
||||
document.addEventListener("drop", (event) => event.preventDefault());
|
||||
|
@ -1,176 +1,297 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="send">
|
||||
<div class="box">
|
||||
<app-callout *ngIf="disableSend">
|
||||
<span>{{'sendDisabledWarning' | i18n}}</span>
|
||||
</app-callout>
|
||||
<app-callout type="info" *ngIf="disableHideEmail && !disableSend">
|
||||
{{'sendOptionsPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="send.name" appAutofocus [readOnly]="disableSend">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-radio" *ngIf="!editMode">
|
||||
<label class="radio-header">{{'whatTypeOfSend' | i18n}}</label>
|
||||
<div class="item" *ngFor="let o of typeOptions">
|
||||
<input type="radio" class="radio" [(ngModel)]="send.type" name="Type_{{o.value}}"
|
||||
id="type_{{o.value}}" [value]="o.value" (change)="typeChanged(o)"
|
||||
[checked]="send.type === o.value" [disabled]="disableSend">
|
||||
<label class="unstyled" for="type_{{o.value}}">
|
||||
{{o.name}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBowRow *ngIf="!editMode && send.type === sendType.File">
|
||||
<label for="file">{{'file' | i18n}}</label>
|
||||
<input type="file" id="file" class="form-control-file" name="file" required [disabled]="disableSend">
|
||||
</div>
|
||||
<div class="box-content-row" appBowRow *ngIf="editMode && send.type === sendType.File">
|
||||
<label for="file">{{'file' | i18n}}</label>
|
||||
<div class="row-main">{{send.file.fileName}} ({{send.file.sizeName}})</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="send.type === sendType.Text">
|
||||
<label for="text">{{'text' | i18n}}</label>
|
||||
<textarea id="text" name="text" [(ngModel)]="send.text.text" rows="6" [readOnly]="disableSend"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="!editMode && send.type === sendType.File">
|
||||
{{'sendFileDesc' | i18n}} {{'maxFileSize' | i18n}}
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="send.type === sendType.Text">
|
||||
{{'sendTextDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="send.type === sendType.Text">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideText">{{'textHiddenByDefault' | i18n}}</label>
|
||||
<input id="hideText" name="hideText" type="checkbox" [(ngModel)]="send.text.hidden" [disabled]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'options' | i18n}}
|
||||
<a class="toggle" href="#" appStopClick appBlurClick role="button" (click)="toggleOptions()">
|
||||
<i class="fa fa-lg" aria-hidden="true" [ngClass]="{'fa-chevron-down': !showOptions, 'fa-chevron-up': showOptions}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="!showOptions">
|
||||
<app-send-efflux-dates
|
||||
[initialDeletionDate]="send.deletionDate" [initialExpirationDate]="send.expirationDate"
|
||||
[editMode]="editMode" [disabled]="disableSend" (datesChanged)="setDates($event)">
|
||||
</app-send-efflux-dates>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="maxAccessCount">{{'maxAccessCount' | i18n}}</label>
|
||||
<input id="maxAccessCount" type="number" name="maxAccessCount" [(ngModel)]="send.maxAccessCount" [readOnly]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="!editMode">
|
||||
{{'maxAccessCountDesc' | i18n}}
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="editMode">
|
||||
<p>{{'maxAccessCountDesc' | i18n}}</p>
|
||||
{{'currentAccessCount' | i18n}}: <strong>{{send.accessCount}}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="password">{{(hasPassword ? 'newPassword' : 'password') | i18n}}</label>
|
||||
<input id="password" name="password" type="{{showPassword ? 'text' : 'password'}}"
|
||||
[(ngModel)]="password" [readOnly]="disableSend" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePasswordVisible()" [disabled]="disableSend">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'sendPasswordDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'notes' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<textarea id="notes" name="notes" [(ngModel)]="send.notes" rows="6" [readOnly]="disableSend"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'sendNotesDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideEmail">{{'hideEmail' | i18n}}</label>
|
||||
<input id="hideEmail" type="checkbox" name="HideEmail" [(ngModel)]="send.hideEmail"
|
||||
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="disabled">{{'disableSend' | i18n}}</label>
|
||||
<input id="disabled" type="checkbox" name="disabled" [(ngModel)]="send.disabled" [disabled]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'share' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
||||
<label for="link">{{'sendLinkLabel' | i18n}}</label>
|
||||
<input id="link" name="link" [ngModel]="link" readonly>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="copyLink">{{'copySendLinkOnSave' | i18n}}</label>
|
||||
<input id="copyLink" name="copyLink" [(ngModel)]="copyLink" type="checkbox" [disabled]="disableSend">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="send">
|
||||
<div class="box">
|
||||
<app-callout *ngIf="disableSend">
|
||||
<span>{{ "sendDisabledWarning" | i18n }}</span>
|
||||
</app-callout>
|
||||
<app-callout type="info" *ngIf="disableHideEmail && !disableSend">
|
||||
{{ "sendOptionsPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button appBlurClick type="submit" class="primary btn-submit" appA11yTitle="{{'save' | i18n}}" [disabled]="form.loading" *ngIf="!disableSend">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span><i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i></span>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()" [disabled]="form.loading">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button appBlurClick type="button" (click)="copyLinkToClipboard(link)" appA11yTitle="{{'copySendLinkToClipboard' | i18n}}" *ngIf="editMode">
|
||||
<i class="fa fa-copy fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{ "name" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="send.name"
|
||||
appAutofocus
|
||||
[readOnly]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-radio" *ngIf="!editMode">
|
||||
<label class="radio-header">{{ "whatTypeOfSend" | i18n }}</label>
|
||||
<div class="item" *ngFor="let o of typeOptions">
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="send.type"
|
||||
name="Type_{{ o.value }}"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged(o)"
|
||||
[checked]="send.type === o.value"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
<label class="unstyled" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBowRow *ngIf="!editMode && send.type === sendType.File">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
class="form-control-file"
|
||||
name="file"
|
||||
required
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBowRow *ngIf="editMode && send.type === sendType.File">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<div class="row-main">{{ send.file.fileName }} ({{ send.file.sizeName }})</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="send.type === sendType.Text">
|
||||
<label for="text">{{ "text" | i18n }}</label>
|
||||
<textarea
|
||||
id="text"
|
||||
name="text"
|
||||
[(ngModel)]="send.text.text"
|
||||
rows="6"
|
||||
[readOnly]="disableSend"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="!editMode && send.type === sendType.File">
|
||||
{{ "sendFileDesc" | i18n }} {{ "maxFileSize" | i18n }}
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="send.type === sendType.Text">
|
||||
{{ "sendTextDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="send.type === sendType.Text">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideText">{{ "textHiddenByDefault" | i18n }}</label>
|
||||
<input
|
||||
id="hideText"
|
||||
name="hideText"
|
||||
type="checkbox"
|
||||
[(ngModel)]="send.text.hidden"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "options" | i18n }}
|
||||
<a
|
||||
class="toggle"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
(click)="toggleOptions()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-chevron-down': !showOptions, 'fa-chevron-up': showOptions }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="!showOptions">
|
||||
<app-send-efflux-dates
|
||||
[initialDeletionDate]="send.deletionDate"
|
||||
[initialExpirationDate]="send.expirationDate"
|
||||
[editMode]="editMode"
|
||||
[disabled]="disableSend"
|
||||
(datesChanged)="setDates($event)"
|
||||
>
|
||||
</app-send-efflux-dates>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="maxAccessCount">{{ "maxAccessCount" | i18n }}</label>
|
||||
<input
|
||||
id="maxAccessCount"
|
||||
type="number"
|
||||
name="maxAccessCount"
|
||||
[(ngModel)]="send.maxAccessCount"
|
||||
[readOnly]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="!editMode">
|
||||
{{ "maxAccessCountDesc" | i18n }}
|
||||
</div>
|
||||
<div class="box-footer" *ngIf="editMode">
|
||||
<p>{{ "maxAccessCountDesc" | i18n }}</p>
|
||||
{{ "currentAccessCount" | i18n }}: <strong>{{ send.accessCount }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="password">{{
|
||||
(hasPassword ? "newPassword" : "password") | i18n
|
||||
}}</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
[(ngModel)]="password"
|
||||
[readOnly]="disableSend"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePasswordVisible()"
|
||||
[disabled]="disableSend"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "sendPasswordDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "notes" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<textarea
|
||||
id="notes"
|
||||
name="notes"
|
||||
[(ngModel)]="send.notes"
|
||||
rows="6"
|
||||
[readOnly]="disableSend"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "sendNotesDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="hideEmail">{{ "hideEmail" | i18n }}</label>
|
||||
<input
|
||||
id="hideEmail"
|
||||
type="checkbox"
|
||||
name="HideEmail"
|
||||
[(ngModel)]="send.hideEmail"
|
||||
[disabled]="(disableHideEmail && !send.hideEmail) || disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="disabled">{{ "disableSend" | i18n }}</label>
|
||||
<input
|
||||
id="disabled"
|
||||
type="checkbox"
|
||||
name="disabled"
|
||||
[(ngModel)]="send.disabled"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "share" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="editMode">
|
||||
<label for="link">{{ "sendLinkLabel" | i18n }}</label>
|
||||
<input id="link" name="link" [ngModel]="link" readonly />
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="copyLink">{{ "copySendLinkOnSave" | i18n }}</label>
|
||||
<input
|
||||
id="copyLink"
|
||||
name="copyLink"
|
||||
[(ngModel)]="copyLink"
|
||||
type="checkbox"
|
||||
[disabled]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary btn-submit"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading"
|
||||
*ngIf="!disableSend"
|
||||
>
|
||||
<i class="fa fa-spinner fa-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span><i class="fa fa-save fa-lg fa-fw" aria-hidden="true"></i></span>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()" [disabled]="form.loading">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="copyLinkToClipboard(link)"
|
||||
appA11yTitle="{{ 'copySendLinkToClipboard' | i18n }}"
|
||||
*ngIf="editMode"
|
||||
>
|
||||
<i class="fa fa-copy fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
#deleteBtn
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
*ngIf="editMode"
|
||||
>
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,47 +1,64 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DatePipe } from "@angular/common";
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SendService } from "jslib-common/abstractions/send.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/send/add-edit.component';
|
||||
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/send/add-edit.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-add-edit',
|
||||
templateUrl: 'add-edit.component.html',
|
||||
selector: "app-send-add-edit",
|
||||
templateUrl: "add-edit.component.html",
|
||||
})
|
||||
export class AddEditComponent extends BaseAddEditComponent {
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService, datePipe: DatePipe,
|
||||
sendService: SendService, stateService: StateService,
|
||||
messagingService: MessagingService, policyService: PolicyService,
|
||||
logService: LogService) {
|
||||
super(i18nService, platformUtilsService, environmentService,
|
||||
datePipe, sendService, messagingService, policyService,
|
||||
logService, stateService);
|
||||
}
|
||||
constructor(
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
datePipe: DatePipe,
|
||||
sendService: SendService,
|
||||
stateService: StateService,
|
||||
messagingService: MessagingService,
|
||||
policyService: PolicyService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
datePipe,
|
||||
sendService,
|
||||
messagingService,
|
||||
policyService,
|
||||
logService,
|
||||
stateService
|
||||
);
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
this.password = null;
|
||||
const send = await this.loadSend();
|
||||
this.send = await send.decrypt();
|
||||
this.hasPassword = this.send.password != null && this.send.password.trim() !== '';
|
||||
}
|
||||
async refresh() {
|
||||
this.password = null;
|
||||
const send = await this.loadSend();
|
||||
this.send = await send.decrypt();
|
||||
this.hasPassword = this.send.password != null && this.send.password.trim() !== "";
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.onCancelled.emit(this.send);
|
||||
}
|
||||
cancel() {
|
||||
this.onCancelled.emit(this.send);
|
||||
}
|
||||
|
||||
async copyLinkToClipboard(link: string) {
|
||||
super.copyLinkToClipboard(link);
|
||||
this.platformUtilsService.showToast('success', null,
|
||||
this.i18nService.t('valueCopied', this.i18nService.t('sendLink')));
|
||||
}
|
||||
async copyLinkToClipboard(link: string) {
|
||||
super.copyLinkToClipboard(link);
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("valueCopied", this.i18nService.t("sendLink"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,55 @@
|
||||
<ng-container [formGroup]="datesForm">
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="deletionDate">{{'deletionDate' | i18n}}</label>
|
||||
<select id="deletionDate" name="DeletionDateSelect" formControlName="selectedDeletionDatePreset" required>
|
||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{o.name}}
|
||||
</option>
|
||||
</select>
|
||||
<small class="help-block">{{'deletionDateDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedDeletionDatePreset.value === 0 || editMode">
|
||||
<label *ngIf="editMode" for="deletionDateCustom">{{'deletionDate' | i18n}}</label>
|
||||
<input id="deletionDateCustom" type="datetime-local" name="deletionDate"
|
||||
formControlName="defaultDeletionDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM">
|
||||
<small class="help-block" *ngIf="editMode">{{'deletionDateDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="expirationDate">{{'expirationDate' | i18n}}</label>
|
||||
<select id="expirationDate" name="expirationDateSelect" formControlName="selectedExpirationDatePreset" required>
|
||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{o.name}}
|
||||
</option>
|
||||
</select>
|
||||
<small class="help-block">{{'expirationDateDesc' | i18n}}</small>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0 || editMode">
|
||||
<label *ngIf="editMode" for="expirationDateCustom">{{'expirationDate' | i18n}}</label>
|
||||
<input id="expirationDateCustom" type="datetime-local" name="expirationDate"
|
||||
formControlName="defaultExpirationDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM" [readOnly]="disableSend">
|
||||
<small *ngIf="editMode" class="help-block">{{'expirationDateDesc' | i18n}}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="deletionDate">{{ "deletionDate" | i18n }}</label>
|
||||
<select
|
||||
id="deletionDate"
|
||||
name="DeletionDateSelect"
|
||||
formControlName="selectedDeletionDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of deletionDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "deletionDateDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedDeletionDatePreset.value === 0 || editMode">
|
||||
<label *ngIf="editMode" for="deletionDateCustom">{{ "deletionDate" | i18n }}</label>
|
||||
<input
|
||||
id="deletionDateCustom"
|
||||
type="datetime-local"
|
||||
name="deletionDate"
|
||||
formControlName="defaultDeletionDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
/>
|
||||
<small class="help-block" *ngIf="editMode">{{ "deletionDateDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow *ngIf="!editMode">
|
||||
<label for="expirationDate">{{ "expirationDate" | i18n }}</label>
|
||||
<select
|
||||
id="expirationDate"
|
||||
name="expirationDateSelect"
|
||||
formControlName="selectedExpirationDatePreset"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of expirationDatePresets" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<small class="help-block">{{ "expirationDateDesc" | i18n }}</small>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="selectedExpirationDatePreset.value === 0 || editMode">
|
||||
<label *ngIf="editMode" for="expirationDateCustom">{{ "expirationDate" | i18n }}</label>
|
||||
<input
|
||||
id="expirationDateCustom"
|
||||
type="datetime-local"
|
||||
name="expirationDate"
|
||||
formControlName="defaultExpirationDateTime"
|
||||
required
|
||||
placeholder="MM/DD/YYYY HH:MM AM/PM"
|
||||
[readOnly]="disableSend"
|
||||
/>
|
||||
<small *ngIf="editMode" class="help-block">{{ "expirationDateDesc" | i18n }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
@ -1,38 +1,41 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DatePipe } from "@angular/common";
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnChanges,
|
||||
} from '@angular/core';
|
||||
import { Component, OnChanges } from "@angular/core";
|
||||
|
||||
import { ControlContainer, NgForm } from '@angular/forms';
|
||||
import { ControlContainer, NgForm } from "@angular/forms";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { EffluxDatesComponent as BaseEffluxDatesComponent } from 'jslib-angular/components/send/efflux-dates.component';
|
||||
import { EffluxDatesComponent as BaseEffluxDatesComponent } from "jslib-angular/components/send/efflux-dates.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send-efflux-dates',
|
||||
templateUrl: 'efflux-dates.component.html',
|
||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
||||
selector: "app-send-efflux-dates",
|
||||
templateUrl: "efflux-dates.component.html",
|
||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
|
||||
})
|
||||
export class EffluxDatesComponent extends BaseEffluxDatesComponent implements OnChanges {
|
||||
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected datePipe: DatePipe) {
|
||||
super(i18nService, platformUtilsService, datePipe);
|
||||
}
|
||||
constructor(
|
||||
protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected datePipe: DatePipe
|
||||
) {
|
||||
super(i18nService, platformUtilsService, datePipe);
|
||||
}
|
||||
|
||||
// We reuse the same form on desktop and just swap content, so need to watch these to maintin proper values.
|
||||
ngOnChanges() {
|
||||
this.selectedExpirationDatePreset.setValue(0);
|
||||
this.selectedDeletionDatePreset.setValue(0);
|
||||
this.defaultDeletionDateTime.setValue(this.datePipe.transform(new Date(this.initialDeletionDate), 'yyyy-MM-ddTHH:mm'));
|
||||
if (this.initialExpirationDate) {
|
||||
this.defaultExpirationDateTime.setValue(
|
||||
this.datePipe.transform(new Date(this.initialExpirationDate), 'yyyy-MM-ddTHH:mm'));
|
||||
} else {
|
||||
this.defaultExpirationDateTime.setValue(null);
|
||||
}
|
||||
// We reuse the same form on desktop and just swap content, so need to watch these to maintin proper values.
|
||||
ngOnChanges() {
|
||||
this.selectedExpirationDatePreset.setValue(0);
|
||||
this.selectedDeletionDatePreset.setValue(0);
|
||||
this.defaultDeletionDateTime.setValue(
|
||||
this.datePipe.transform(new Date(this.initialDeletionDate), "yyyy-MM-ddTHH:mm")
|
||||
);
|
||||
if (this.initialExpirationDate) {
|
||||
this.defaultExpirationDateTime.setValue(
|
||||
this.datePipe.transform(new Date(this.initialExpirationDate), "yyyy-MM-ddTHH:mm")
|
||||
);
|
||||
} else {
|
||||
this.defaultExpirationDateTime.setValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +1,141 @@
|
||||
<div id="sends" class="vault">
|
||||
<div class="groupings">
|
||||
<div class="mac-bar"></div>
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<h2 class="sr-only">{{'filters' | i18n}}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{active: selectedAll}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{'allSends' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>{{'types' | i18n}}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{active: selectedType === sendType.Text}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.Text)">
|
||||
<i class="fa fa-fw fa-file-text-o" aria-hidden="true"></i> {{'sendTypeText' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{active: selectedType === sendType.File}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.File)">
|
||||
<i class="fa fa-fw fa-file-o" aria-hidden="true"></i> {{'sendTypeFile' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<app-nav class="nav"></app-nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="groupings">
|
||||
<div class="mac-bar"></div>
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedAll }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{ "allSends" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>{{ "types" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedType === sendType.Text }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.Text)">
|
||||
<i class="fa fa-fw fa-file-text-o" aria-hidden="true"></i> {{
|
||||
"sendTypeText" | i18n
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === sendType.File }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.File)">
|
||||
<i class="fa fa-fw fa-file-o" aria-hidden="true"></i> {{ "sendTypeFile" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<app-nav class="nav"></app-nav>
|
||||
</div>
|
||||
</div>
|
||||
<div id="items" class="items">
|
||||
<div class="content">
|
||||
<div class="list" *ngIf="filteredSends.length">
|
||||
<a *ngFor="let s of filteredSends" appStopClick (click)="selectSend(s.id)"
|
||||
title="{{'viewItem' | i18n}}" (contextmenu)="viewSendMenu(s)"
|
||||
[ngClass]="{'active': s.id === sendId}" class="flex-list-item">
|
||||
<div class="item-icon" aria-hidden="true">
|
||||
<i class="fa fa-fw fa-lg" [ngClass]="s.type == 0 ? 'fa-file-text-o' : 'fa-file-o'"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">
|
||||
{{s.name}}
|
||||
<span class="title-badges">
|
||||
<ng-container *ngIf="s.disabled">
|
||||
<i class="fa fa-warning" appStopProp title="{{'disabled' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'disabled' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.password">
|
||||
<i class="fa fa-key" appStopProp title="{{'password' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'password' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.maxAccessCountReached">
|
||||
<i class="fa fa-ban" appStopProp title="{{'maxAccessCountReached' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'maxAccessCountReached' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.expired">
|
||||
<i class="fa fa-clock-o" appStopProp title="{{'expired' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'expired' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i class="fa fa-trash" appStopProp title="{{'pendingDeletion' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'pendingDeletion' | i18n}}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
<span class="item-details">{{s.deletionDate | date}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!filteredSends.length">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||
<ng-container *ngIf="loaded">
|
||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||
<p>{{'noItemsInList' | i18n}}</p>
|
||||
</div>
|
||||
<div id="items" class="items">
|
||||
<div class="content">
|
||||
<div class="list" *ngIf="filteredSends.length">
|
||||
<a
|
||||
*ngFor="let s of filteredSends"
|
||||
appStopClick
|
||||
(click)="selectSend(s.id)"
|
||||
title="{{ 'viewItem' | i18n }}"
|
||||
(contextmenu)="viewSendMenu(s)"
|
||||
[ngClass]="{ active: s.id === sendId }"
|
||||
class="flex-list-item"
|
||||
>
|
||||
<div class="item-icon" aria-hidden="true">
|
||||
<i class="fa fa-fw fa-lg" [ngClass]="s.type == 0 ? 'fa-file-text-o' : 'fa-file-o'"></i>
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="item-title">
|
||||
{{ s.name }}
|
||||
<span class="title-badges">
|
||||
<ng-container *ngIf="s.disabled">
|
||||
<i
|
||||
class="fa fa-warning"
|
||||
appStopProp
|
||||
title="{{ 'disabled' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "disabled" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.password">
|
||||
<i
|
||||
class="fa fa-key"
|
||||
appStopProp
|
||||
title="{{ 'password' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "password" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.maxAccessCountReached">
|
||||
<i
|
||||
class="fa fa-ban"
|
||||
appStopProp
|
||||
title="{{ 'maxAccessCountReached' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "maxAccessCountReached" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.expired">
|
||||
<i
|
||||
class="fa fa-clock-o"
|
||||
appStopProp
|
||||
title="{{ 'expired' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "expired" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="s.pendingDelete">
|
||||
<i
|
||||
class="fa fa-trash"
|
||||
appStopProp
|
||||
title="{{ 'pendingDeletion' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button appBlurClick (click)="addSend()" class="block primary" appA11yTitle="{{'addItem' | i18n}}">
|
||||
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<span class="item-details">{{ s.deletionDate | date }}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="no-items" *ngIf="!filteredSends.length">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||
<ng-container *ngIf="loaded">
|
||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||
<p>{{ "noItemsInList" | i18n }}</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<app-send-add-edit id="addEdit" class="details" *ngIf="action == 'add' || action == 'edit'" [sendId]="sendId" [type]="selectedSendType"
|
||||
(onSavedSend)="savedSend($event)" (onCancelled)="cancel($event)" (onDeletedSend)="deletedSend($event)"></app-send-add-edit>
|
||||
<div class="logo" *ngIf="!action">
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<img class="logo-image" alt="Bitwarden" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
(click)="addSend()"
|
||||
class="block primary"
|
||||
appA11yTitle="{{ 'addItem' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<app-send-add-edit
|
||||
id="addEdit"
|
||||
class="details"
|
||||
*ngIf="action == 'add' || action == 'edit'"
|
||||
[sendId]="sendId"
|
||||
[type]="selectedSendType"
|
||||
(onSavedSend)="savedSend($event)"
|
||||
(onCancelled)="cancel($event)"
|
||||
(onDeletedSend)="deletedSend($event)"
|
||||
></app-send-add-edit>
|
||||
<div class="logo" *ngIf="!action">
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<img class="logo-image" alt="Bitwarden" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,138 +1,146 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Component, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { SendService } from "jslib-common/abstractions/send.service";
|
||||
|
||||
import { SendComponent as BaseSendComponent } from 'jslib-angular/components/send/send.component';
|
||||
import { SendComponent as BaseSendComponent } from "jslib-angular/components/send/send.component";
|
||||
|
||||
import { invokeMenu, RendererMenuItem } from 'jslib-electron/utils';
|
||||
import { invokeMenu, RendererMenuItem } from "jslib-electron/utils";
|
||||
|
||||
import { SendView } from 'jslib-common/models/view/sendView';
|
||||
import { SendView } from "jslib-common/models/view/sendView";
|
||||
|
||||
import { SearchBarService } from '../layout/search/search-bar.service';
|
||||
import { AddEditComponent } from './add-edit.component';
|
||||
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||
import { AddEditComponent } from "./add-edit.component";
|
||||
|
||||
enum Action {
|
||||
None = '',
|
||||
Add = 'add',
|
||||
Edit = 'edit',
|
||||
None = "",
|
||||
Add = "add",
|
||||
Edit = "edit",
|
||||
}
|
||||
|
||||
const BroadcasterSubscriptionId = 'SendComponent';
|
||||
const BroadcasterSubscriptionId = "SendComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-send',
|
||||
templateUrl: 'send.component.html',
|
||||
selector: "app-send",
|
||||
templateUrl: "send.component.html",
|
||||
})
|
||||
export class SendComponent extends BaseSendComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
|
||||
@ViewChild(AddEditComponent) addEditComponent: AddEditComponent;
|
||||
|
||||
sendId: string;
|
||||
action: Action = Action.None;
|
||||
sendId: string;
|
||||
action: Action = Action.None;
|
||||
|
||||
constructor(sendService: SendService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
|
||||
private broadcasterService: BroadcasterService, ngZone: NgZone,
|
||||
searchService: SearchService, policyService: PolicyService,
|
||||
private searchBarService: SearchBarService, logService: LogService) {
|
||||
super(sendService, i18nService, platformUtilsService,
|
||||
environmentService, ngZone, searchService,
|
||||
policyService, logService);
|
||||
this.searchBarService.searchText.subscribe(searchText => {
|
||||
this.searchText = searchText;
|
||||
this.searchTextChanged();
|
||||
});
|
||||
}
|
||||
constructor(
|
||||
sendService: SendService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
environmentService: EnvironmentService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone,
|
||||
searchService: SearchService,
|
||||
policyService: PolicyService,
|
||||
private searchBarService: SearchBarService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
sendService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
ngZone,
|
||||
searchService,
|
||||
policyService,
|
||||
logService
|
||||
);
|
||||
this.searchBarService.searchText.subscribe((searchText) => {
|
||||
this.searchText = searchText;
|
||||
this.searchTextChanged();
|
||||
});
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.searchBarService.setEnabled(true);
|
||||
this.searchBarService.setPlaceholderText(this.i18nService.t('searchSends'));
|
||||
async ngOnInit() {
|
||||
this.searchBarService.setEnabled(true);
|
||||
this.searchBarService.setPlaceholderText(this.i18nService.t("searchSends"));
|
||||
|
||||
super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
switch (message.command) {
|
||||
case 'syncCompleted':
|
||||
await this.load();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
await this.load();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
this.searchBarService.setEnabled(false);
|
||||
}
|
||||
|
||||
addSend() {
|
||||
this.action = Action.Add;
|
||||
if (this.addEditComponent != null) {
|
||||
this.addEditComponent.sendId = null;
|
||||
this.addEditComponent.send = null;
|
||||
this.addEditComponent.load();
|
||||
super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
switch (message.command) {
|
||||
case "syncCompleted":
|
||||
await this.load();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
await this.load();
|
||||
}
|
||||
|
||||
cancel(s: SendView) {
|
||||
this.action = Action.None;
|
||||
this.sendId = null;
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
this.searchBarService.setEnabled(false);
|
||||
}
|
||||
|
||||
async deletedSend(s: SendView) {
|
||||
await this.refresh();
|
||||
this.action = Action.None;
|
||||
this.sendId = null;
|
||||
addSend() {
|
||||
this.action = Action.Add;
|
||||
if (this.addEditComponent != null) {
|
||||
this.addEditComponent.sendId = null;
|
||||
this.addEditComponent.send = null;
|
||||
this.addEditComponent.load();
|
||||
}
|
||||
}
|
||||
|
||||
async savedSend(s: SendView) {
|
||||
await this.refresh();
|
||||
this.selectSend(s.id);
|
||||
cancel(s: SendView) {
|
||||
this.action = Action.None;
|
||||
this.sendId = null;
|
||||
}
|
||||
|
||||
async deletedSend(s: SendView) {
|
||||
await this.refresh();
|
||||
this.action = Action.None;
|
||||
this.sendId = null;
|
||||
}
|
||||
|
||||
async savedSend(s: SendView) {
|
||||
await this.refresh();
|
||||
this.selectSend(s.id);
|
||||
}
|
||||
|
||||
async selectSend(sendId: string) {
|
||||
if (sendId === this.sendId && this.action === Action.Edit) {
|
||||
return;
|
||||
}
|
||||
|
||||
async selectSend(sendId: string) {
|
||||
if (sendId === this.sendId && this.action === Action.Edit) {
|
||||
return;
|
||||
}
|
||||
this.action = Action.Edit;
|
||||
this.sendId = sendId;
|
||||
if (this.addEditComponent != null) {
|
||||
this.addEditComponent.sendId = sendId;
|
||||
await this.addEditComponent.refresh();
|
||||
}
|
||||
this.action = Action.Edit;
|
||||
this.sendId = sendId;
|
||||
if (this.addEditComponent != null) {
|
||||
this.addEditComponent.sendId = sendId;
|
||||
await this.addEditComponent.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
get selectedSendType() {
|
||||
return this.sends.find(s => s.id === this.sendId)?.type;
|
||||
}
|
||||
get selectedSendType() {
|
||||
return this.sends.find((s) => s.id === this.sendId)?.type;
|
||||
}
|
||||
|
||||
viewSendMenu(send: SendView) {
|
||||
const menu: RendererMenuItem[] = [];
|
||||
menu.push({
|
||||
label: this.i18nService.t('copyLink'),
|
||||
click: () => this.copy(send),
|
||||
});
|
||||
menu.push({
|
||||
label: this.i18nService.t('delete'),
|
||||
click: async () => {
|
||||
await this.delete(send);
|
||||
await this.deletedSend(send);
|
||||
},
|
||||
});
|
||||
viewSendMenu(send: SendView) {
|
||||
const menu: RendererMenuItem[] = [];
|
||||
menu.push({
|
||||
label: this.i18nService.t("copyLink"),
|
||||
click: () => this.copy(send),
|
||||
});
|
||||
menu.push({
|
||||
label: this.i18nService.t("delete"),
|
||||
click: async () => {
|
||||
await this.delete(send);
|
||||
await this.deletedSend(send);
|
||||
},
|
||||
});
|
||||
|
||||
invokeMenu(menu);
|
||||
}
|
||||
invokeMenu(menu);
|
||||
}
|
||||
}
|
||||
|
@ -1,179 +1,173 @@
|
||||
import {
|
||||
APP_INITIALIZER,
|
||||
NgModule,
|
||||
} from '@angular/core';
|
||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
|
||||
import { ElectronLogService } from 'jslib-electron/services/electronLog.service';
|
||||
import { ElectronPlatformUtilsService } from 'jslib-electron/services/electronPlatformUtils.service';
|
||||
import { ElectronRendererMessagingService } from 'jslib-electron/services/electronRendererMessaging.service';
|
||||
import { ElectronRendererSecureStorageService } from 'jslib-electron/services/electronRendererSecureStorage.service';
|
||||
import { ElectronRendererStorageService } from 'jslib-electron/services/electronRendererStorage.service';
|
||||
import { ElectronLogService } from "jslib-electron/services/electronLog.service";
|
||||
import { ElectronPlatformUtilsService } from "jslib-electron/services/electronPlatformUtils.service";
|
||||
import { ElectronRendererMessagingService } from "jslib-electron/services/electronRendererMessaging.service";
|
||||
import { ElectronRendererSecureStorageService } from "jslib-electron/services/electronRendererSecureStorage.service";
|
||||
import { ElectronRendererStorageService } from "jslib-electron/services/electronRendererStorage.service";
|
||||
|
||||
import { I18nService } from '../services/i18n.service';
|
||||
import { LoginGuardService } from '../services/loginGuard.service';
|
||||
import { NativeMessagingService } from '../services/nativeMessaging.service';
|
||||
import { PasswordRepromptService } from '../services/passwordReprompt.service';
|
||||
import { SearchBarService } from './layout/search/search-bar.service';
|
||||
import { I18nService } from "../services/i18n.service";
|
||||
import { LoginGuardService } from "../services/loginGuard.service";
|
||||
import { NativeMessagingService } from "../services/nativeMessaging.service";
|
||||
import { PasswordRepromptService } from "../services/passwordReprompt.service";
|
||||
import { SearchBarService } from "./layout/search/search-bar.service";
|
||||
|
||||
import { JslibServicesModule } from 'jslib-angular/services/jslib-services.module';
|
||||
import { JslibServicesModule } from "jslib-angular/services/jslib-services.module";
|
||||
|
||||
import { AuthService } from 'jslib-common/services/auth.service';
|
||||
import { ContainerService } from 'jslib-common/services/container.service';
|
||||
import { EventService } from 'jslib-common/services/event.service';
|
||||
import { SystemService } from 'jslib-common/services/system.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/services/vaultTimeout.service';
|
||||
import { AuthService } from "jslib-common/services/auth.service";
|
||||
import { ContainerService } from "jslib-common/services/container.service";
|
||||
import { EventService } from "jslib-common/services/event.service";
|
||||
import { SystemService } from "jslib-common/services/system.service";
|
||||
import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service";
|
||||
|
||||
import { ElectronCryptoService } from 'jslib-electron/services/electronCrypto.service';
|
||||
import { ElectronCryptoService } from "jslib-electron/services/electronCrypto.service";
|
||||
|
||||
import { AuthService as AuthServiceAbstraction } from 'jslib-common/abstractions/auth.service';
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CryptoService as CryptoServiceAbstraction } from 'jslib-common/abstractions/crypto.service';
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib-common/abstractions/environment.service';
|
||||
import { EventService as EventServiceAbstraction } from 'jslib-common/abstractions/event.service';
|
||||
import { I18nService as I18nServiceAbstraction } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService as LogServiceAbstraction } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService as MessagingServiceAbstraction } from 'jslib-common/abstractions/messaging.service';
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from 'jslib-common/abstractions/notifications.service';
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService as StateServiceAbstraction } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService as StorageServiceAbstraction } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService as SyncServiceAbstraction } from 'jslib-common/abstractions/sync.service';
|
||||
import { SystemService as SystemServiceAbstraction } from 'jslib-common/abstractions/system.service';
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { AuthService as AuthServiceAbstraction } from "jslib-common/abstractions/auth.service";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service";
|
||||
import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||
|
||||
import { ThemeType } from 'jslib-common/enums/themeType';
|
||||
import { ThemeType } from "jslib-common/enums/themeType";
|
||||
|
||||
export function initFactory(window: Window, environmentService: EnvironmentServiceAbstraction,
|
||||
syncService: SyncServiceAbstraction, vaultTimeoutService: VaultTimeoutService,
|
||||
i18nService: I18nService, eventService: EventService,
|
||||
authService: AuthService, notificationsService: NotificationsServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction, stateService: StateServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction): Function {
|
||||
export function initFactory(
|
||||
window: Window,
|
||||
environmentService: EnvironmentServiceAbstraction,
|
||||
syncService: SyncServiceAbstraction,
|
||||
vaultTimeoutService: VaultTimeoutService,
|
||||
i18nService: I18nService,
|
||||
eventService: EventService,
|
||||
authService: AuthService,
|
||||
notificationsService: NotificationsServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
stateService: StateServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction
|
||||
): Function {
|
||||
return async () => {
|
||||
await stateService.init();
|
||||
await environmentService.setUrlsFromStorage();
|
||||
syncService.fullSync(true);
|
||||
await vaultTimeoutService.init(true);
|
||||
const locale = await stateService.getLocale();
|
||||
await i18nService.init(locale);
|
||||
eventService.init(true);
|
||||
authService.init();
|
||||
setTimeout(() => notificationsService.init(), 3000);
|
||||
const htmlEl = window.document.documentElement;
|
||||
htmlEl.classList.add("os_" + platformUtilsService.getDeviceString());
|
||||
htmlEl.classList.add("locale_" + i18nService.translationLocale);
|
||||
const theme = await platformUtilsService.getEffectiveTheme();
|
||||
htmlEl.classList.add("theme_" + theme);
|
||||
platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||
const bwTheme = await stateService.getTheme();
|
||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + sysTheme);
|
||||
}
|
||||
});
|
||||
|
||||
return async () => {
|
||||
await stateService.init();
|
||||
await environmentService.setUrlsFromStorage();
|
||||
syncService.fullSync(true);
|
||||
await vaultTimeoutService.init(true);
|
||||
const locale = await stateService.getLocale();
|
||||
await i18nService.init(locale);
|
||||
eventService.init(true);
|
||||
authService.init();
|
||||
setTimeout(() => notificationsService.init(), 3000);
|
||||
const htmlEl = window.document.documentElement;
|
||||
htmlEl.classList.add('os_' + platformUtilsService.getDeviceString());
|
||||
htmlEl.classList.add('locale_' + i18nService.translationLocale);
|
||||
const theme = await platformUtilsService.getEffectiveTheme();
|
||||
htmlEl.classList.add('theme_' + theme);
|
||||
platformUtilsService.onDefaultSystemThemeChange(async sysTheme => {
|
||||
const bwTheme = await stateService.getTheme();
|
||||
if (bwTheme == null || bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove('theme_' + ThemeType.Light, 'theme_' + ThemeType.Dark);
|
||||
htmlEl.classList.add('theme_' + sysTheme);
|
||||
}
|
||||
});
|
||||
let installAction = null;
|
||||
const installedVersion = await stateService.getInstalledVersion();
|
||||
const currentVersion = await platformUtilsService.getApplicationVersion();
|
||||
if (installedVersion == null) {
|
||||
installAction = "install";
|
||||
} else if (installedVersion !== currentVersion) {
|
||||
installAction = "update";
|
||||
}
|
||||
|
||||
let installAction = null;
|
||||
const installedVersion = await stateService.getInstalledVersion();
|
||||
const currentVersion = await platformUtilsService.getApplicationVersion();
|
||||
if (installedVersion == null) {
|
||||
installAction = 'install';
|
||||
} else if (installedVersion !== currentVersion) {
|
||||
installAction = 'update';
|
||||
}
|
||||
if (installAction != null) {
|
||||
await stateService.setInstalledVersion(currentVersion);
|
||||
}
|
||||
|
||||
if (installAction != null) {
|
||||
await stateService.setInstalledVersion(currentVersion);
|
||||
}
|
||||
|
||||
const containerService = new ContainerService(cryptoService);
|
||||
containerService.attachToGlobal(window);
|
||||
};
|
||||
const containerService = new ContainerService(cryptoService);
|
||||
containerService.attachToGlobal(window);
|
||||
};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
JslibServicesModule,
|
||||
],
|
||||
declarations: [],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [
|
||||
'WINDOW',
|
||||
EnvironmentServiceAbstraction,
|
||||
SyncServiceAbstraction,
|
||||
VaultTimeoutServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
EventServiceAbstraction,
|
||||
AuthServiceAbstraction,
|
||||
NotificationsServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
],
|
||||
multi: true,
|
||||
},
|
||||
{ provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] },
|
||||
{
|
||||
provide: PlatformUtilsServiceAbstraction,
|
||||
useFactory: (i18nService: I18nServiceAbstraction, messagingService: MessagingServiceAbstraction,
|
||||
stateService: StateServiceAbstraction) => new ElectronPlatformUtilsService(i18nService,
|
||||
messagingService, true, stateService),
|
||||
deps: [
|
||||
I18nServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useFactory: (window: Window) => new I18nService(window.navigator.language, './locales'),
|
||||
deps: [ 'WINDOW' ],
|
||||
},
|
||||
{
|
||||
provide: MessagingServiceAbstraction,
|
||||
useClass: ElectronRendererMessagingService,
|
||||
deps: [ BroadcasterServiceAbstraction ],
|
||||
},
|
||||
{ provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService },
|
||||
{ provide: 'SECURE_STORAGE', useClass: ElectronRendererSecureStorageService },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: SystemServiceAbstraction,
|
||||
useClass: SystemService,
|
||||
deps: [
|
||||
VaultTimeoutServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
NativeMessagingService,
|
||||
SearchBarService,
|
||||
{
|
||||
provide: LoginGuardService,
|
||||
useClass: LoginGuardService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
],
|
||||
},
|
||||
],
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
deps: [
|
||||
"WINDOW",
|
||||
EnvironmentServiceAbstraction,
|
||||
SyncServiceAbstraction,
|
||||
VaultTimeoutServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
EventServiceAbstraction,
|
||||
AuthServiceAbstraction,
|
||||
NotificationsServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
],
|
||||
multi: true,
|
||||
},
|
||||
{ provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] },
|
||||
{
|
||||
provide: PlatformUtilsServiceAbstraction,
|
||||
useFactory: (
|
||||
i18nService: I18nServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
stateService: StateServiceAbstraction
|
||||
) => new ElectronPlatformUtilsService(i18nService, messagingService, true, stateService),
|
||||
deps: [I18nServiceAbstraction, MessagingServiceAbstraction, StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useFactory: (window: Window) => new I18nService(window.navigator.language, "./locales"),
|
||||
deps: ["WINDOW"],
|
||||
},
|
||||
{
|
||||
provide: MessagingServiceAbstraction,
|
||||
useClass: ElectronRendererMessagingService,
|
||||
deps: [BroadcasterServiceAbstraction],
|
||||
},
|
||||
{ provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService },
|
||||
{ provide: "SECURE_STORAGE", useClass: ElectronRendererSecureStorageService },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: SystemServiceAbstraction,
|
||||
useClass: SystemService,
|
||||
deps: [
|
||||
VaultTimeoutServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
NativeMessagingService,
|
||||
SearchBarService,
|
||||
{
|
||||
provide: LoginGuardService,
|
||||
useClass: LoginGuardService,
|
||||
deps: [StateServiceAbstraction, PlatformUtilsServiceAbstraction, I18nServiceAbstraction],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ServicesModule {
|
||||
}
|
||||
export class ServicesModule {}
|
||||
|
@ -1,64 +1,120 @@
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'customFields' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||
<div class="box-content-row box-content-row-multi box-draggable-row" cdkDrag
|
||||
*ngFor="let f of cipher.fields; let i = index; trackBy:trackByFunction"
|
||||
[ngClass]="{'box-content-row-checkbox': f.type === fieldType.Boolean}">
|
||||
<a href="#" appStopClick (click)="removeField(f)" appA11yTitle="{{'remove' | i18n}}"
|
||||
role="button">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</a>
|
||||
<label for="fieldName{{i}}" class="sr-only">{{'name' | i18n}}</label>
|
||||
<label for="fieldValue{{i}}" class="sr-only">{{'value' | i18n}}</label>
|
||||
<div class="row-main">
|
||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name"
|
||||
class="row-label" placeholder="{{'name' | i18n}}" appInputVerbatim>
|
||||
<!-- Text -->
|
||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}" appInputVerbatim>
|
||||
<!-- Password -->
|
||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
||||
name="Field.Value{{i}}" [(ngModel)]="f.value" class="monospaced"
|
||||
*ngIf="f.type === fieldType.Hidden" placeholder="{{'value' | i18n}}"
|
||||
[disabled]="!cipher.viewPassword && !f.newField" appInputVerbatim>
|
||||
<!-- Linked -->
|
||||
<select id="fieldValue{{i}}" name="Field.Value{{i}}" [(ngModel)]="f.linkedId"
|
||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null">
|
||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Boolean -->
|
||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Boolean" appTrueFalseValue trueValue="true"
|
||||
falseValue="false">
|
||||
<div class="action-buttons"
|
||||
*ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleFieldValue(f)">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="drag-handle" appA11yTitle="{{'dragToSort' | i18n}}" cdkDragHandle>
|
||||
<i class="fa fa-bars" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-header">
|
||||
{{ "customFields" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div cdkDropList (cdkDropListDropped)="drop($event)" *ngIf="cipher.hasFields">
|
||||
<div
|
||||
class="box-content-row box-content-row-multi box-draggable-row"
|
||||
cdkDrag
|
||||
*ngFor="let f of cipher.fields; let i = index; trackBy: trackByFunction"
|
||||
[ngClass]="{ 'box-content-row-checkbox': f.type === fieldType.Boolean }"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="removeField(f)"
|
||||
appA11yTitle="{{ 'remove' | i18n }}"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true"></i>
|
||||
</a>
|
||||
<label for="fieldName{{ i }}" class="sr-only">{{ "name" | i18n }}</label>
|
||||
<label for="fieldValue{{ i }}" class="sr-only">{{ "value" | i18n }}</label>
|
||||
<div class="row-main">
|
||||
<input
|
||||
id="fieldName{{ i }}"
|
||||
type="text"
|
||||
name="Field.Name{{ i }}"
|
||||
[(ngModel)]="f.name"
|
||||
class="row-label"
|
||||
placeholder="{{ 'name' | i18n }}"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<!-- Text -->
|
||||
<input
|
||||
id="fieldValue{{ i }}"
|
||||
type="text"
|
||||
name="Field.Value{{ i }}"
|
||||
[(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text"
|
||||
placeholder="{{ 'value' | i18n }}"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<!-- Password -->
|
||||
<input
|
||||
id="fieldValue{{ i }}"
|
||||
type="{{ f.showValue ? 'text' : 'password' }}"
|
||||
name="Field.Value{{ i }}"
|
||||
[(ngModel)]="f.value"
|
||||
class="monospaced"
|
||||
*ngIf="f.type === fieldType.Hidden"
|
||||
placeholder="{{ 'value' | i18n }}"
|
||||
[disabled]="!cipher.viewPassword && !f.newField"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<!-- Linked -->
|
||||
<select
|
||||
id="fieldValue{{ i }}"
|
||||
name="Field.Value{{ i }}"
|
||||
[(ngModel)]="f.linkedId"
|
||||
*ngIf="f.type === fieldType.Linked && cipher.linkedFieldOptions != null"
|
||||
>
|
||||
<option *ngFor="let o of linkedFieldOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Add new custom field -->
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<a href="#" appStopClick (click)="addField()" role="button">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{'newCustomField' | i18n}}
|
||||
</a>
|
||||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
<option *ngIf="cipher.linkedFieldOptions != null" [ngValue]="addFieldLinkedTypeOption.value">
|
||||
{{addFieldLinkedTypeOption.name}}
|
||||
</option>
|
||||
</select>
|
||||
<!-- Boolean -->
|
||||
<input
|
||||
id="fieldValue{{ i }}"
|
||||
name="Field.Value{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Boolean"
|
||||
appTrueFalseValue
|
||||
trueValue="true"
|
||||
falseValue="false"
|
||||
/>
|
||||
<div
|
||||
class="action-buttons"
|
||||
*ngIf="f.type === fieldType.Hidden && (cipher.viewPassword || f.newField)"
|
||||
>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleFieldValue(f)"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="drag-handle" appA11yTitle="{{ 'dragToSort' | i18n }}" cdkDragHandle>
|
||||
<i class="fa fa-bars" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add new custom field -->
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<a href="#" appStopClick (click)="addField()" role="button">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ "newCustomField" | i18n }}
|
||||
</a>
|
||||
<label for="addFieldType" class="sr-only">{{ "type" | i18n }}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
<option
|
||||
*ngIf="cipher.linkedFieldOptions != null"
|
||||
[ngValue]="addFieldLinkedTypeOption.value"
|
||||
>
|
||||
{{ addFieldLinkedTypeOption.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,18 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import {
|
||||
AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent
|
||||
} from 'jslib-angular/components/add-edit-custom-fields.component';
|
||||
import { AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent } from "jslib-angular/components/add-edit-custom-fields.component";
|
||||
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-add-edit-custom-fields',
|
||||
templateUrl: 'add-edit-custom-fields.component.html',
|
||||
selector: "app-vault-add-edit-custom-fields",
|
||||
templateUrl: "add-edit-custom-fields.component.html",
|
||||
})
|
||||
export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsComponent {
|
||||
constructor(i18nService: I18nService, eventService: EventService) {
|
||||
super(i18nService, eventService);
|
||||
}
|
||||
constructor(i18nService: I18nService, eventService: EventService) {
|
||||
super(i18nService, eventService);
|
||||
}
|
||||
}
|
||||
|
@ -1,343 +1,603 @@
|
||||
<form #form="ngForm" (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="cipher">
|
||||
<div class="box">
|
||||
<app-callout type="info" *ngIf="allowOwnershipOptions() && !allowPersonal">
|
||||
{{'personalOwnershipPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<div class="box-header">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||
<label for="type">{{'type' | i18n}}</label>
|
||||
<select id="type" name="Type" [(ngModel)]="cipher.type">
|
||||
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="cipher.name" [appAutofocus]="!editMode">
|
||||
</div>
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUsername">{{'username' | i18n}}</label>
|
||||
<input id="loginUsername" type="text" name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="loginPassword">{{'password' | i18n}}</label>
|
||||
<input id="loginPassword" class="monospaced"
|
||||
type="{{showPassword ? 'text' : 'password'}}" name="Login.Password"
|
||||
[(ngModel)]="cipher.login.password" [disabled]="!cipher.viewPassword"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons" *ngIf=cipher.viewPassword>
|
||||
<button type="button" #checkPasswordBtn class="row-btn btn" appBlurClick
|
||||
appA11yTitle="{{'checkPassword' | i18n}}" (click)="checkPassword()"
|
||||
[appApiAction]="checkPasswordPromise" [disabled]="checkPasswordBtn.loading">
|
||||
<i class="fa fa-lg fa-check-circle" [hidden]="checkPasswordBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
<i class="fa fa-lg fa-spinner fa-spin" [hidden]="!checkPasswordBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'generatePassword' | i18n}}" (click)="generatePassword()">
|
||||
<i class="fa fa-lg fa-refresh" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
||||
<input id="loginTotp" type="{{cipher.viewPassword ? 'text' : 'password'}}" name="Login.Totp"
|
||||
class="monospaced" [(ngModel)]="cipher.login.totp" [disabled]="!cipher.viewPassword"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.type === cipherType.Card">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardCardholderName">{{'cardholderName' | i18n}}</label>
|
||||
<input id="cardCardholderName" type="text" name="Card.CardCardholderName"
|
||||
[(ngModel)]="cipher.card.cardholderName">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="cardNumber">{{'number' | i18n}}</label>
|
||||
<input id="cardNumber" class="monospaced" type="{{showCardNumber ? 'text' : 'password'}}"
|
||||
name="Card.Number" [(ngModel)]="cipher.card.number" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleCardNumber()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardBrand">{{'brand' | i18n}}</label>
|
||||
<select id="cardBrand" name="Card.Brand" [(ngModel)]="cipher.card.brand">
|
||||
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpMonth">{{'expirationMonth' | i18n}}</label>
|
||||
<select id="cardExpMonth" name="Card.ExpMonth" [(ngModel)]="cipher.card.expMonth">
|
||||
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpYear">{{'expirationYear' | i18n}}</label>
|
||||
<input id="cardExpYear" type="text" name="Card.ExpYear" [(ngModel)]="cipher.card.expYear"
|
||||
placeholder="{{'ex' | i18n}} {{currentDate | date: 'yyyy'}}">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="cardCode">{{'securityCode' | i18n}}</label>
|
||||
<input id="cardCode" class="monospaced" type="{{showCardCode ? 'text' : 'password'}}"
|
||||
name="Card.Code" [(ngModel)]="cipher.card.code" appInputVerbatim>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="toggleCardCode()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Identity -->
|
||||
<div *ngIf="cipher.type === cipherType.Identity">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idTitle">{{'title' | i18n}}</label>
|
||||
<select id="idTitle" name="Identity.Title" [(ngModel)]="cipher.identity.title">
|
||||
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idFirstName">{{'firstName' | i18n}}</label>
|
||||
<input id="idFirstName" type="text" name="Identity.FirstName"
|
||||
[(ngModel)]="cipher.identity.firstName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idMiddleName">{{'middleName' | i18n}}</label>
|
||||
<input id="idMiddleName" type="text" name="Identity.MiddleName"
|
||||
[(ngModel)]="cipher.identity.middleName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLastName">{{'lastName' | i18n}}</label>
|
||||
<input id="idLastName" type="text" name="Identity.LastName"
|
||||
[(ngModel)]="cipher.identity.lastName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idUsername">{{'username' | i18n}}</label>
|
||||
<input id="idUsername" type="text" name="Identity.Username"
|
||||
[(ngModel)]="cipher.identity.username" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCompany">{{'company' | i18n}}</label>
|
||||
<input id="idCompany" type="text" name="Identity.Company"
|
||||
[(ngModel)]="cipher.identity.company">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idSsn">{{'ssn' | i18n}}</label>
|
||||
<input id="idSsn" type="text" name="Identity.SSN" [(ngModel)]="cipher.identity.ssn"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPassportNumber">{{'passportNumber' | i18n}}</label>
|
||||
<input id="idPassportNumber" type="text" name="Identity.PassportNumber"
|
||||
[(ngModel)]="cipher.identity.passportNumber" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLicenseNumber">{{'licenseNumber' | i18n}}</label>
|
||||
<input id="idLicenseNumber" type="text" name="Identity.LicenseNumber"
|
||||
[(ngModel)]="cipher.identity.licenseNumber" appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idEmail">{{'email' | i18n}}</label>
|
||||
<input id="idEmail" type="text" name="Identity.Email" [(ngModel)]="cipher.identity.email"
|
||||
appInputVerbatim>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPhone">{{'phone' | i18n}}</label>
|
||||
<input id="idPhone" type="text" name="Identity.Phone" [(ngModel)]="cipher.identity.phone">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress1">{{'address1' | i18n}}</label>
|
||||
<input id="idAddress1" type="text" name="Identity.Address1"
|
||||
[(ngModel)]="cipher.identity.address1">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress2">{{'address2' | i18n}}</label>
|
||||
<input id="idAddress2" type="text" name="Identity.Address2"
|
||||
[(ngModel)]="cipher.identity.address2">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress3">{{'address3' | i18n}}</label>
|
||||
<input id="idAddress3" type="text" name="Identity.Address3"
|
||||
[(ngModel)]="cipher.identity.address3">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCity">{{'cityTown' | i18n}}</label>
|
||||
<input id="idCity" type="text" name="Identity.City" [(ngModel)]="cipher.identity.city">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idState">{{'stateProvince' | i18n}}</label>
|
||||
<input id="idState" type="text" name="Identity.State" [(ngModel)]="cipher.identity.state">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPostalCode">{{'zipPostalCode' | i18n}}</label>
|
||||
<input id="idPostalCode" type="text" name="Identity.PostalCode"
|
||||
[(ngModel)]="cipher.identity.postalCode">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCountry">{{'country' | i18n}}</label>
|
||||
<input id="idCountry" type="text" name="Identity.Country"
|
||||
[(ngModel)]="cipher.identity.country">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="cipher.login.hasUris">
|
||||
<div class="box-content-row box-content-row-multi" appBoxRow
|
||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy:trackByFunction">
|
||||
<a href="#" appStopClick (click)="removeUri(u)" appA11yTitle="{{'remove' | i18n}}">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true" role="button"></i>
|
||||
</a>
|
||||
<div class="row-main">
|
||||
<label for="loginUri{{i}}">{{'uriPosition' | i18n : (i + 1)}}</label>
|
||||
<input id="loginUri{{i}}" type="text" name="Login.Uris[{{i}}].Uri" [(ngModel)]="u.uri"
|
||||
placeholder="{{'ex' | i18n}} https://google.com" appInputVerbatim>
|
||||
<label for="loginUriMatch{{i}}" class="sr-only">
|
||||
{{'matchDetection' | i18n}} {{(i + 1)}}
|
||||
</label>
|
||||
<select id="loginUriMatch{{i}}" name="Login.Uris[{{i}}].Match" [(ngModel)]="u.match"
|
||||
[hidden]="u.showOptions === false || (u.showOptions == null && u.match == null)"
|
||||
(change)="loginUriMatchChanged(u)">
|
||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick role="button"
|
||||
appA11yTitle="{{'toggleOptions' | i18n}}" (click)="toggleUriOptions(u)">
|
||||
<i class="fa fa-lg fa-cog" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<a href="#" appStopClick appBlurClick (click)="addUri()" class="box-content-row" role="button">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{'newUri' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="folder">{{'folder' | i18n}}</label>
|
||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId">
|
||||
<option *ngFor="let f of folders" [ngValue]="f.id">{{f.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="favorite">{{'favorite' | i18n}}</label>
|
||||
<input id="favorite" type="checkbox" name="Favorite" [(ngModel)]="cipher.favorite">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="canUseReprompt">
|
||||
<label for="passwordPrompt">{{'passwordPrompt' | i18n}}
|
||||
<a href="#" appA11yTitle="{{'learnMore' | i18n}}" (click)="openHelpReprompt()">
|
||||
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
</label>
|
||||
<input id="passwordPrompt" type="checkbox" name="PasswordPrompt" [ngModel]="reprompt"
|
||||
(change)="repromptChanged()">
|
||||
</div>
|
||||
<a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick
|
||||
(click)="attachments()" *ngIf="editMode && !cloneMode" role="button">
|
||||
<div class="row-main">{{'attachments' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick
|
||||
(click)="editCollections()" *ngIf="editMode && !cloneMode && cipher.organizationId"
|
||||
role="button">
|
||||
<div class="row-main">{{'collections' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<label for="notes">{{'notes' | i18n}}</label>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="cipher.notes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-add-edit-custom-fields [cipher]="cipher" [thisCipherType]="cipher.type" [editMode]="editMode">
|
||||
</app-vault-add-edit-custom-fields>
|
||||
<div class="box" *ngIf="allowOwnershipOptions()">
|
||||
<div class="box-header">
|
||||
{{'ownership' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organizationId">{{'whoOwnsThisItem' | i18n}}</label>
|
||||
<select id="organizationId" class="form-control" name="OrganizationId"
|
||||
[(ngModel)]="cipher.organizationId" (change)="organizationChanged()">
|
||||
<option *ngFor="let o of ownershipOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="(!editMode || cloneMode) && cipher.organizationId">
|
||||
<div class="box-header">
|
||||
{{'collections' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{'noCollectionsInList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div class="box-content-row box-content-row-checkbox" *ngFor="let c of collections; let i = index"
|
||||
appBoxRow>
|
||||
<label for="collection_{{i}}">{{c.name}}</label>
|
||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
||||
name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="cipher">
|
||||
<div class="box">
|
||||
<app-callout type="info" *ngIf="allowOwnershipOptions() && !allowPersonal">
|
||||
{{ "personalOwnershipPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="box-header">
|
||||
{{ title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}" [disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button appBlurClick type="button" (click)="share()" appA11yTitle="{{'moveToOrganization' | i18n}}"
|
||||
*ngIf="editMode && cipher && !cipher.organizationId && !cloneMode">
|
||||
<i class="fa fa-arrow-circle-o-right fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode && !cloneMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||
<label for="type">{{ "type" | i18n }}</label>
|
||||
<select id="type" name="Type" [(ngModel)]="cipher.type">
|
||||
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{ "name" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="cipher.name"
|
||||
[appAutofocus]="!editMode"
|
||||
/>
|
||||
</div>
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUsername">{{ "username" | i18n }}</label>
|
||||
<input
|
||||
id="loginUsername"
|
||||
type="text"
|
||||
name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="loginPassword">{{ "password" | i18n }}</label>
|
||||
<input
|
||||
id="loginPassword"
|
||||
class="monospaced"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="Login.Password"
|
||||
[(ngModel)]="cipher.login.password"
|
||||
[disabled]="!cipher.viewPassword"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons" *ngIf="cipher.viewPassword">
|
||||
<button
|
||||
type="button"
|
||||
#checkPasswordBtn
|
||||
class="row-btn btn"
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'checkPassword' | i18n }}"
|
||||
(click)="checkPassword()"
|
||||
[appApiAction]="checkPasswordPromise"
|
||||
[disabled]="checkPasswordBtn.loading"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg fa-check-circle"
|
||||
[hidden]="checkPasswordBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-lg fa-spinner fa-spin"
|
||||
[hidden]="!checkPasswordBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'generatePassword' | i18n }}"
|
||||
(click)="generatePassword()"
|
||||
>
|
||||
<i class="fa fa-lg fa-refresh" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label>
|
||||
<input
|
||||
id="loginTotp"
|
||||
type="{{ cipher.viewPassword ? 'text' : 'password' }}"
|
||||
name="Login.Totp"
|
||||
class="monospaced"
|
||||
[(ngModel)]="cipher.login.totp"
|
||||
[disabled]="!cipher.viewPassword"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.type === cipherType.Card">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardCardholderName">{{ "cardholderName" | i18n }}</label>
|
||||
<input
|
||||
id="cardCardholderName"
|
||||
type="text"
|
||||
name="Card.CardCardholderName"
|
||||
[(ngModel)]="cipher.card.cardholderName"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="cardNumber">{{ "number" | i18n }}</label>
|
||||
<input
|
||||
id="cardNumber"
|
||||
class="monospaced"
|
||||
type="{{ showCardNumber ? 'text' : 'password' }}"
|
||||
name="Card.Number"
|
||||
[(ngModel)]="cipher.card.number"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleCardNumber()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardBrand">{{ "brand" | i18n }}</label>
|
||||
<select id="cardBrand" name="Card.Brand" [(ngModel)]="cipher.card.brand">
|
||||
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpMonth">{{ "expirationMonth" | i18n }}</label>
|
||||
<select id="cardExpMonth" name="Card.ExpMonth" [(ngModel)]="cipher.card.expMonth">
|
||||
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpYear">{{ "expirationYear" | i18n }}</label>
|
||||
<input
|
||||
id="cardExpYear"
|
||||
type="text"
|
||||
name="Card.ExpYear"
|
||||
[(ngModel)]="cipher.card.expYear"
|
||||
placeholder="{{ 'ex' | i18n }} {{ currentDate | date: 'yyyy' }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="cardCode">{{ "securityCode" | i18n }}</label>
|
||||
<input
|
||||
id="cardCode"
|
||||
class="monospaced"
|
||||
type="{{ showCardCode ? 'text' : 'password' }}"
|
||||
name="Card.Code"
|
||||
[(ngModel)]="cipher.card.code"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleCardCode()"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode }"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Identity -->
|
||||
<div *ngIf="cipher.type === cipherType.Identity">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idTitle">{{ "title" | i18n }}</label>
|
||||
<select id="idTitle" name="Identity.Title" [(ngModel)]="cipher.identity.title">
|
||||
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idFirstName">{{ "firstName" | i18n }}</label>
|
||||
<input
|
||||
id="idFirstName"
|
||||
type="text"
|
||||
name="Identity.FirstName"
|
||||
[(ngModel)]="cipher.identity.firstName"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idMiddleName">{{ "middleName" | i18n }}</label>
|
||||
<input
|
||||
id="idMiddleName"
|
||||
type="text"
|
||||
name="Identity.MiddleName"
|
||||
[(ngModel)]="cipher.identity.middleName"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLastName">{{ "lastName" | i18n }}</label>
|
||||
<input
|
||||
id="idLastName"
|
||||
type="text"
|
||||
name="Identity.LastName"
|
||||
[(ngModel)]="cipher.identity.lastName"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idUsername">{{ "username" | i18n }}</label>
|
||||
<input
|
||||
id="idUsername"
|
||||
type="text"
|
||||
name="Identity.Username"
|
||||
[(ngModel)]="cipher.identity.username"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCompany">{{ "company" | i18n }}</label>
|
||||
<input
|
||||
id="idCompany"
|
||||
type="text"
|
||||
name="Identity.Company"
|
||||
[(ngModel)]="cipher.identity.company"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idSsn">{{ "ssn" | i18n }}</label>
|
||||
<input
|
||||
id="idSsn"
|
||||
type="text"
|
||||
name="Identity.SSN"
|
||||
[(ngModel)]="cipher.identity.ssn"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPassportNumber">{{ "passportNumber" | i18n }}</label>
|
||||
<input
|
||||
id="idPassportNumber"
|
||||
type="text"
|
||||
name="Identity.PassportNumber"
|
||||
[(ngModel)]="cipher.identity.passportNumber"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLicenseNumber">{{ "licenseNumber" | i18n }}</label>
|
||||
<input
|
||||
id="idLicenseNumber"
|
||||
type="text"
|
||||
name="Identity.LicenseNumber"
|
||||
[(ngModel)]="cipher.identity.licenseNumber"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idEmail">{{ "email" | i18n }}</label>
|
||||
<input
|
||||
id="idEmail"
|
||||
type="text"
|
||||
name="Identity.Email"
|
||||
[(ngModel)]="cipher.identity.email"
|
||||
appInputVerbatim
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPhone">{{ "phone" | i18n }}</label>
|
||||
<input
|
||||
id="idPhone"
|
||||
type="text"
|
||||
name="Identity.Phone"
|
||||
[(ngModel)]="cipher.identity.phone"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress1">{{ "address1" | i18n }}</label>
|
||||
<input
|
||||
id="idAddress1"
|
||||
type="text"
|
||||
name="Identity.Address1"
|
||||
[(ngModel)]="cipher.identity.address1"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress2">{{ "address2" | i18n }}</label>
|
||||
<input
|
||||
id="idAddress2"
|
||||
type="text"
|
||||
name="Identity.Address2"
|
||||
[(ngModel)]="cipher.identity.address2"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress3">{{ "address3" | i18n }}</label>
|
||||
<input
|
||||
id="idAddress3"
|
||||
type="text"
|
||||
name="Identity.Address3"
|
||||
[(ngModel)]="cipher.identity.address3"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCity">{{ "cityTown" | i18n }}</label>
|
||||
<input
|
||||
id="idCity"
|
||||
type="text"
|
||||
name="Identity.City"
|
||||
[(ngModel)]="cipher.identity.city"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idState">{{ "stateProvince" | i18n }}</label>
|
||||
<input
|
||||
id="idState"
|
||||
type="text"
|
||||
name="Identity.State"
|
||||
[(ngModel)]="cipher.identity.state"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPostalCode">{{ "zipPostalCode" | i18n }}</label>
|
||||
<input
|
||||
id="idPostalCode"
|
||||
type="text"
|
||||
name="Identity.PostalCode"
|
||||
[(ngModel)]="cipher.identity.postalCode"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCountry">{{ "country" | i18n }}</label>
|
||||
<input
|
||||
id="idCountry"
|
||||
type="text"
|
||||
name="Identity.Country"
|
||||
[(ngModel)]="cipher.identity.country"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content">
|
||||
<ng-container *ngIf="cipher.login.hasUris">
|
||||
<div
|
||||
class="box-content-row box-content-row-multi"
|
||||
appBoxRow
|
||||
*ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction"
|
||||
>
|
||||
<a href="#" appStopClick (click)="removeUri(u)" appA11yTitle="{{ 'remove' | i18n }}">
|
||||
<i class="fa fa-minus-circle fa-lg" aria-hidden="true" role="button"></i>
|
||||
</a>
|
||||
<div class="row-main">
|
||||
<label for="loginUri{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
||||
<input
|
||||
id="loginUri{{ i }}"
|
||||
type="text"
|
||||
name="Login.Uris[{{ i }}].Uri"
|
||||
[(ngModel)]="u.uri"
|
||||
placeholder="{{ 'ex' | i18n }} https://google.com"
|
||||
appInputVerbatim
|
||||
/>
|
||||
<label for="loginUriMatch{{ i }}" class="sr-only">
|
||||
{{ "matchDetection" | i18n }} {{ i + 1 }}
|
||||
</label>
|
||||
<select
|
||||
id="loginUriMatch{{ i }}"
|
||||
name="Login.Uris[{{ i }}].Match"
|
||||
[(ngModel)]="u.match"
|
||||
[hidden]="u.showOptions === false || (u.showOptions == null && u.match == null)"
|
||||
(change)="loginUriMatchChanged(u)"
|
||||
>
|
||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'toggleOptions' | i18n }}"
|
||||
(click)="toggleUriOptions(u)"
|
||||
>
|
||||
<i class="fa fa-lg fa-cog" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="addUri()"
|
||||
class="box-content-row"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg" aria-hidden="true"></i> {{ "newUri" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="folder">{{ "folder" | i18n }}</label>
|
||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId">
|
||||
<option *ngFor="let f of folders" [ngValue]="f.id">{{ f.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="favorite">{{ "favorite" | i18n }}</label>
|
||||
<input id="favorite" type="checkbox" name="Favorite" [(ngModel)]="cipher.favorite" />
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="canUseReprompt">
|
||||
<label for="passwordPrompt"
|
||||
>{{ "passwordPrompt" | i18n }}
|
||||
<a href="#" appA11yTitle="{{ 'learnMore' | i18n }}" (click)="openHelpReprompt()">
|
||||
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
</label>
|
||||
<input
|
||||
id="passwordPrompt"
|
||||
type="checkbox"
|
||||
name="PasswordPrompt"
|
||||
[ngModel]="reprompt"
|
||||
(change)="repromptChanged()"
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="attachments()"
|
||||
*ngIf="editMode && !cloneMode"
|
||||
role="button"
|
||||
>
|
||||
<div class="row-main">{{ "attachments" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
(click)="editCollections()"
|
||||
*ngIf="editMode && !cloneMode && cipher.organizationId"
|
||||
role="button"
|
||||
>
|
||||
<div class="row-main">{{ "collections" | i18n }}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<label for="notes">{{ "notes" | i18n }}</label>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="cipher.notes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-add-edit-custom-fields
|
||||
[cipher]="cipher"
|
||||
[thisCipherType]="cipher.type"
|
||||
[editMode]="editMode"
|
||||
>
|
||||
</app-vault-add-edit-custom-fields>
|
||||
<div class="box" *ngIf="allowOwnershipOptions()">
|
||||
<div class="box-header">
|
||||
{{ "ownership" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organizationId">{{ "whoOwnsThisItem" | i18n }}</label>
|
||||
<select
|
||||
id="organizationId"
|
||||
class="form-control"
|
||||
name="OrganizationId"
|
||||
[(ngModel)]="cipher.organizationId"
|
||||
(change)="organizationChanged()"
|
||||
>
|
||||
<option *ngFor="let o of ownershipOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="(!editMode || cloneMode) && cipher.organizationId">
|
||||
<div class="box-header">
|
||||
{{ "collections" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{ "noCollectionsInList" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index"
|
||||
appBoxRow
|
||||
>
|
||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||
<input
|
||||
id="collection_{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading"
|
||||
>
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="share()"
|
||||
appA11yTitle="{{ 'moveToOrganization' | i18n }}"
|
||||
*ngIf="editMode && cipher && !cipher.organizationId && !cloneMode"
|
||||
>
|
||||
<i class="fa fa-arrow-circle-o-right fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
#deleteBtn
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
*ngIf="editMode && !cloneMode"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise"
|
||||
>
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,103 +1,125 @@
|
||||
import {
|
||||
Component,
|
||||
NgZone,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { Component, NgZone, OnChanges, OnDestroy, ViewChild } from "@angular/core";
|
||||
import { NgForm } from "@angular/forms";
|
||||
|
||||
import { AuditService } from 'jslib-common/abstractions/audit.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { AuditService } from "jslib-common/abstractions/audit.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { AddEditComponent as BaseAddEditComponent } from 'jslib-angular/components/add-edit.component';
|
||||
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/add-edit.component";
|
||||
|
||||
const BroadcasterSubscriptionId = 'AddEditComponent';
|
||||
const BroadcasterSubscriptionId = "AddEditComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-add-edit',
|
||||
templateUrl: 'add-edit.component.html',
|
||||
selector: "app-vault-add-edit",
|
||||
templateUrl: "add-edit.component.html",
|
||||
})
|
||||
export class AddEditComponent extends BaseAddEditComponent implements OnChanges, OnDestroy {
|
||||
@ViewChild('form')
|
||||
private form: NgForm;
|
||||
constructor(cipherService: CipherService, folderService: FolderService,
|
||||
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService, stateService: StateService,
|
||||
collectionService: CollectionService, messagingService: MessagingService,
|
||||
eventService: EventService, policyService: PolicyService,
|
||||
passwordRepromptService: PasswordRepromptService, private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone, logService: LogService,
|
||||
organizationService: OrganizationService) {
|
||||
super(cipherService, folderService, i18nService, platformUtilsService, auditService, stateService,
|
||||
collectionService, messagingService, eventService, policyService, logService,
|
||||
passwordRepromptService, organizationService);
|
||||
}
|
||||
@ViewChild("form")
|
||||
private form: NgForm;
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
folderService: FolderService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
collectionService: CollectionService,
|
||||
messagingService: MessagingService,
|
||||
eventService: EventService,
|
||||
policyService: PolicyService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private ngZone: NgZone,
|
||||
logService: LogService,
|
||||
organizationService: OrganizationService
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
folderService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
eventService,
|
||||
policyService,
|
||||
logService,
|
||||
passwordRepromptService,
|
||||
organizationService
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
// We use ngOnChanges for everything else instead.
|
||||
}
|
||||
|
||||
async ngOnChanges() {
|
||||
await super.init();
|
||||
await this.load();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async load() {
|
||||
if (document.querySelectorAll('app-vault-add-edit .ng-dirty').length === 0 ||
|
||||
(this.cipher != null && this.cipherId !== this.cipher.id)) {
|
||||
this.cipher = null;
|
||||
async ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
super.load();
|
||||
}
|
||||
});
|
||||
});
|
||||
// We use ngOnChanges for everything else instead.
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
this.showCardNumber = false;
|
||||
this.showCardCode = false;
|
||||
if (this.cipher !== null && this.cipher.hasFields) {
|
||||
this.cipher.fields.forEach(field => {
|
||||
field.showValue = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
async ngOnChanges() {
|
||||
await super.init();
|
||||
await this.load();
|
||||
}
|
||||
|
||||
allowOwnershipOptions(): boolean {
|
||||
return (!this.editMode || this.cloneMode) && this.ownershipOptions
|
||||
&& (this.ownershipOptions.length > 1 || !this.allowPersonal);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
markPasswordAsDirty() {
|
||||
this.form.controls['Login.Password'].markAsDirty();
|
||||
async load() {
|
||||
if (
|
||||
document.querySelectorAll("app-vault-add-edit .ng-dirty").length === 0 ||
|
||||
(this.cipher != null && this.cipherId !== this.cipher.id)
|
||||
) {
|
||||
this.cipher = null;
|
||||
}
|
||||
super.load();
|
||||
}
|
||||
|
||||
openHelpReprompt() {
|
||||
this.platformUtilsService.launchUri('https://bitwarden.com/help/article/managing-items/#protect-individual-items');
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
this.showCardNumber = false;
|
||||
this.showCardCode = false;
|
||||
if (this.cipher !== null && this.cipher.hasFields) {
|
||||
this.cipher.fields.forEach((field) => {
|
||||
field.showValue = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
allowOwnershipOptions(): boolean {
|
||||
return (
|
||||
(!this.editMode || this.cloneMode) &&
|
||||
this.ownershipOptions &&
|
||||
(this.ownershipOptions.length > 1 || !this.allowPersonal)
|
||||
);
|
||||
}
|
||||
|
||||
markPasswordAsDirty() {
|
||||
this.form.controls["Login.Password"].markAsDirty();
|
||||
}
|
||||
|
||||
openHelpReprompt() {
|
||||
this.platformUtilsService.launchUri(
|
||||
"https://bitwarden.com/help/article/managing-items/#protect-individual-items"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +1,76 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="attachmentsTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box" *ngIf="cipher && cipher.hasAttachments">
|
||||
<div class="box-header" id="attachmentsTitle">
|
||||
{{'attachments' | i18n}}
|
||||
</div>
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let a of cipher.attachments">
|
||||
<div class="row-main">
|
||||
{{a.fileName}}
|
||||
</div>
|
||||
<small class="row-sub-label">{{a.sizeName}}</small>
|
||||
<div class="action-buttons no-pad">
|
||||
<button class="row-btn btn" type="button" appStopClick appBlurClick
|
||||
appA11yTitle="{{'delete' | i18n}}" (click)="delete(a)" #deleteBtn
|
||||
[appApiAction]="deletePromises[a.id]" [disabled]="deleteBtn.loading">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'newAttachment' | i18n}}
|
||||
</div>
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row">
|
||||
<label for="file">{{'file' | i18n}}</label>
|
||||
<input type="file" id="file" name="file" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'maxFileSize' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
||||
[disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box" *ngIf="cipher && cipher.hasAttachments">
|
||||
<div class="box-header" id="attachmentsTitle">
|
||||
{{ "attachments" | i18n }}
|
||||
</div>
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let a of cipher.attachments">
|
||||
<div class="row-main">
|
||||
{{ a.fileName }}
|
||||
</div>
|
||||
<small class="row-sub-label">{{ a.sizeName }}</small>
|
||||
<div class="action-buttons no-pad">
|
||||
<button
|
||||
class="row-btn btn"
|
||||
type="button"
|
||||
appStopClick
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
(click)="delete(a)"
|
||||
#deleteBtn
|
||||
[appApiAction]="deletePromises[a.id]"
|
||||
[disabled]="deleteBtn.loading"
|
||||
>
|
||||
<i
|
||||
class="fa fa-trash-o fa-lg fa-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "newAttachment" | i18n }}
|
||||
</div>
|
||||
<div class="box-content no-hover">
|
||||
<div class="box-content-row">
|
||||
<label for="file">{{ "file" | i18n }}</label>
|
||||
<input type="file" id="file" name="file" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "maxFileSize" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading"
|
||||
>
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!form.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,25 +1,38 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from 'jslib-angular/components/attachments.component';
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "jslib-angular/components/attachments.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-attachments',
|
||||
templateUrl: 'attachments.component.html',
|
||||
selector: "app-vault-attachments",
|
||||
templateUrl: "attachments.component.html",
|
||||
})
|
||||
export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
||||
cryptoService: CryptoService, platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService, logService: LogService,
|
||||
stateService: StateService) {
|
||||
super(cipherService, i18nService, cryptoService, platformUtilsService,
|
||||
apiService, window, logService, stateService);
|
||||
}
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
apiService: ApiService,
|
||||
logService: LogService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
i18nService,
|
||||
cryptoService,
|
||||
platformUtilsService,
|
||||
apiService,
|
||||
window,
|
||||
logService,
|
||||
stateService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,61 @@
|
||||
<div class="content">
|
||||
<cdk-virtual-scroll-viewport itemSize="42" minBufferPx="400" maxBufferPx="600" *ngIf="ciphers.length">
|
||||
<div class="list">
|
||||
<a *cdkVirtualFor="let c of ciphers; trackBy: trackByFn" appStopClick (click)="selectCipher(c)"
|
||||
(contextmenu)="rightClickCipher(c)" href="#" title="{{'viewItem' | i18n}}"
|
||||
[ngClass]="{'active': c.id === activeCipherId}" class="flex-list-item virtual-scroll-item">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
<div class="flex-cipher-list-item">
|
||||
<span class="text">
|
||||
{{c.name}}
|
||||
<ng-container *ngIf="c.organizationId">
|
||||
<i class="fa fa-cube text-muted" title="{{'shared' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'shared' | i18n}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i class="fa fa-paperclip text-muted" title="{{'attachments' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'attachments' | i18n}}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span *ngIf="c.subTitle" class="detail">{{c.subTitle}}</span>
|
||||
</div>
|
||||
</a>
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="42"
|
||||
minBufferPx="400"
|
||||
maxBufferPx="600"
|
||||
*ngIf="ciphers.length"
|
||||
>
|
||||
<div class="list">
|
||||
<a
|
||||
*cdkVirtualFor="let c of ciphers; trackBy: trackByFn"
|
||||
appStopClick
|
||||
(click)="selectCipher(c)"
|
||||
(contextmenu)="rightClickCipher(c)"
|
||||
href="#"
|
||||
title="{{ 'viewItem' | i18n }}"
|
||||
[ngClass]="{ active: c.id === activeCipherId }"
|
||||
class="flex-list-item virtual-scroll-item"
|
||||
>
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
<div class="flex-cipher-list-item">
|
||||
<span class="text">
|
||||
{{ c.name }}
|
||||
<ng-container *ngIf="c.organizationId">
|
||||
<i class="fa fa-cube text-muted" title="{{ 'shared' | i18n }}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="c.hasAttachments">
|
||||
<i
|
||||
class="fa fa-paperclip text-muted"
|
||||
title="{{ 'attachments' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="sr-only">{{ "attachments" | i18n }}</span>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span *ngIf="c.subTitle" class="detail">{{ c.subTitle }}</span>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="no-items" *ngIf="!ciphers.length">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||
<ng-container *ngIf="loaded">
|
||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||
<p>{{'noItemsInList' | i18n}}</p>
|
||||
<button (click)="addCipher()" class="btn block primary link">{{'addItem' | i18n}}</button>
|
||||
</ng-container>
|
||||
</a>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="no-items" *ngIf="!ciphers.length">
|
||||
<i class="fa fa-spinner fa-spin fa-3x" *ngIf="!loaded" aria-hidden="true"></i>
|
||||
<ng-container *ngIf="loaded">
|
||||
<i class="fa fa-frown-o fa-4x" aria-hidden="true"></i>
|
||||
<p>{{ "noItemsInList" | i18n }}</p>
|
||||
<button (click)="addCipher()" class="btn block primary link">{{ "addItem" | i18n }}</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button appBlurClick (click)="addCipher()" (contextmenu)="addCipherOptions()"
|
||||
class="block primary" appA11yTitle="{{'addItem' | i18n}}" [disabled]="deleted">
|
||||
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
appBlurClick
|
||||
(click)="addCipher()"
|
||||
(contextmenu)="addCipherOptions()"
|
||||
class="block primary"
|
||||
appA11yTitle="{{ 'addItem' | i18n }}"
|
||||
[disabled]="deleted"
|
||||
>
|
||||
<i class="fa fa-plus fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,28 +1,27 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { CiphersComponent as BaseCiphersComponent } from 'jslib-angular/components/ciphers.component';
|
||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
||||
import { CiphersComponent as BaseCiphersComponent } from "jslib-angular/components/ciphers.component";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
import { SearchBarService } from '../layout/search/search-bar.service';
|
||||
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-ciphers',
|
||||
templateUrl: 'ciphers.component.html',
|
||||
selector: "app-vault-ciphers",
|
||||
templateUrl: "ciphers.component.html",
|
||||
})
|
||||
export class CiphersComponent extends BaseCiphersComponent {
|
||||
constructor(searchService: SearchService, searchBarService: SearchBarService) {
|
||||
super(searchService);
|
||||
|
||||
constructor(searchService: SearchService, searchBarService: SearchBarService) {
|
||||
super(searchService);
|
||||
searchBarService.searchText.subscribe((searchText) => {
|
||||
this.searchText = searchText;
|
||||
this.search(200);
|
||||
});
|
||||
}
|
||||
|
||||
searchBarService.searchText.subscribe(searchText => {
|
||||
this.searchText = searchText;
|
||||
this.search(200);
|
||||
});
|
||||
}
|
||||
|
||||
trackByFn(index: number, c: CipherView) {
|
||||
return c.id;
|
||||
}
|
||||
trackByFn(index: number, c: CipherView) {
|
||||
return c.id;
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,48 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="collectionsTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="collectionsTitle">
|
||||
{{'collections' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{'noCollectionsInList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
||||
<label for="collection_{{i}}">{{c.name}}</label>
|
||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
||||
name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="collectionsTitle">
|
||||
{{ "collections" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{ "noCollectionsInList" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index"
|
||||
appBoxRow
|
||||
>
|
||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||
<input
|
||||
id="collection_{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
||||
[disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading"
|
||||
>
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!form.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,21 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { CollectionsComponent as BaseCollectionsComponent } from 'jslib-angular/components/collections.component';
|
||||
import { CollectionsComponent as BaseCollectionsComponent } from "jslib-angular/components/collections.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-collections',
|
||||
templateUrl: 'collections.component.html',
|
||||
selector: "app-vault-collections",
|
||||
templateUrl: "collections.component.html",
|
||||
})
|
||||
export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
||||
collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService) {
|
||||
super(collectionService, platformUtilsService, i18nService, cipherService, logService);
|
||||
}
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
i18nService: I18nService,
|
||||
collectionService: CollectionService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(collectionService, platformUtilsService, i18nService, cipherService, logService);
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,45 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="exportTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [formGroup]="exportForm">
|
||||
<div class="modal-body">
|
||||
<app-callout type="warning" title="{{'vaultExportDisabled' | i18n}}" *ngIf="disabledByPolicy">
|
||||
{{'personalVaultExportPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-header" id="exportTitle">
|
||||
{{'exportVault' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="format">{{'fileFormat' | i18n}}</label>
|
||||
<select class="form-control" id="format" name="Format" formControlName="format">
|
||||
<option *ngFor="let f of formatOptions" [value]="f.value">{{f.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<app-verify-master-password ngDefaultControl formControlName="secret" name="secret">
|
||||
</app-verify-master-password>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{'confirmIdentity' | i18n}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [formGroup]="exportForm">
|
||||
<div class="modal-body">
|
||||
<app-callout
|
||||
type="warning"
|
||||
title="{{ 'vaultExportDisabled' | i18n }}"
|
||||
*ngIf="disabledByPolicy"
|
||||
>
|
||||
{{ "personalVaultExportPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="box">
|
||||
<div class="box-header" id="exportTitle">
|
||||
{{ "exportVault" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="format">{{ "fileFormat" | i18n }}</label>
|
||||
<select class="form-control" id="format" name="Format" formControlName="format">
|
||||
<option *ngFor="let f of formatOptions" [value]="f.value">{{ f.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'submit' | i18n}}"
|
||||
[disabled]="disabledByPolicy">
|
||||
<i class="fa fa-download fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<app-verify-master-password ngDefaultControl formControlName="secret" name="secret">
|
||||
</app-verify-master-password>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<p>{{ "confirmIdentity" | i18n }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'submit' | i18n }}"
|
||||
[disabled]="disabledByPolicy"
|
||||
>
|
||||
<i class="fa fa-download fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,57 +1,78 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
|
||||
import * as os from 'os';
|
||||
import * as os from "os";
|
||||
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { ExportService } from 'jslib-common/abstractions/export.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { ExportService } from "jslib-common/abstractions/export.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
|
||||
|
||||
import { ExportComponent as BaseExportComponent } from 'jslib-angular/components/export.component';
|
||||
import { ExportComponent as BaseExportComponent } from "jslib-angular/components/export.component";
|
||||
|
||||
const BroadcasterSubscriptionId = 'ExportComponent';
|
||||
const BroadcasterSubscriptionId = "ExportComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-export',
|
||||
templateUrl: 'export.component.html',
|
||||
selector: "app-export",
|
||||
templateUrl: "export.component.html",
|
||||
})
|
||||
export class ExportComponent extends BaseExportComponent implements OnInit {
|
||||
constructor(cryptoService: CryptoService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, exportService: ExportService,
|
||||
eventService: EventService, policyService: PolicyService,
|
||||
userVerificationService: UserVerificationService, fb: FormBuilder,
|
||||
private broadcasterService: BroadcasterService, logService: LogService) {
|
||||
super(cryptoService, i18nService, platformUtilsService, exportService, eventService,
|
||||
policyService, window, logService, userVerificationService, fb);
|
||||
}
|
||||
constructor(
|
||||
cryptoService: CryptoService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
exportService: ExportService,
|
||||
eventService: EventService,
|
||||
policyService: PolicyService,
|
||||
userVerificationService: UserVerificationService,
|
||||
fb: FormBuilder,
|
||||
private broadcasterService: BroadcasterService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(
|
||||
cryptoService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
exportService,
|
||||
eventService,
|
||||
policyService,
|
||||
window,
|
||||
logService,
|
||||
userVerificationService,
|
||||
fb
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
ngOnDestroy() {
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async warningDialog() {
|
||||
if (this.encryptedFormat) {
|
||||
return await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('encExportKeyWarningDesc') +
|
||||
os.EOL + os.EOL +
|
||||
this.i18nService.t('encExportAccountWarningDesc'),
|
||||
this.i18nService.t('confirmVaultExport'), this.i18nService.t('exportVault'),
|
||||
this.i18nService.t('cancel'), 'warning',
|
||||
true);
|
||||
} else {
|
||||
return await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t('exportWarningDesc'),
|
||||
this.i18nService.t('confirmVaultExport'), this.i18nService.t('exportVault'),
|
||||
this.i18nService.t('cancel'), 'warning');
|
||||
}
|
||||
async warningDialog() {
|
||||
if (this.encryptedFormat) {
|
||||
return await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("encExportKeyWarningDesc") +
|
||||
os.EOL +
|
||||
os.EOL +
|
||||
this.i18nService.t("encExportAccountWarningDesc"),
|
||||
this.i18nService.t("confirmVaultExport"),
|
||||
this.i18nService.t("exportVault"),
|
||||
this.i18nService.t("cancel"),
|
||||
"warning",
|
||||
true
|
||||
);
|
||||
} else {
|
||||
return await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t("exportWarningDesc"),
|
||||
this.i18nService.t("confirmVaultExport"),
|
||||
this.i18nService.t("exportVault"),
|
||||
this.i18nService.t("cancel"),
|
||||
"warning"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,66 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="folderAddEditTitle">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="folderAddEditTitle">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="folder.name"
|
||||
[appAutofocus]="!editMode">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="folderAddEditTitle">
|
||||
{{ title }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{ "name" | i18n }}</label>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
name="Name"
|
||||
[(ngModel)]="folder.name"
|
||||
[appAutofocus]="!editMode"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
||||
[disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
<div class="right">
|
||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
||||
appA11yTitle="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading"
|
||||
>
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!form.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||
<div class="right">
|
||||
<button
|
||||
#deleteBtn
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
*ngIf="editMode"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise"
|
||||
>
|
||||
<i
|
||||
class="fa fa-trash-o fa-lg fa-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,21 +1,23 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
FolderAddEditComponent as BaseFolderAddEditComponent,
|
||||
} from 'jslib-angular/components/folder-add-edit.component';
|
||||
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "jslib-angular/components/folder-add-edit.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-folder-add-edit',
|
||||
templateUrl: 'folder-add-edit.component.html',
|
||||
selector: "app-folder-add-edit",
|
||||
templateUrl: "folder-add-edit.component.html",
|
||||
})
|
||||
export class FolderAddEditComponent extends BaseFolderAddEditComponent {
|
||||
constructor(folderService: FolderService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, logService: LogService) {
|
||||
super(folderService, i18nService, platformUtilsService, logService);
|
||||
}
|
||||
constructor(
|
||||
folderService: FolderService,
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService
|
||||
) {
|
||||
super(folderService, i18nService, platformUtilsService, logService);
|
||||
}
|
||||
}
|
||||
|
@ -1,104 +1,146 @@
|
||||
<div class="mac-bar"></div>
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<h2 class="sr-only">{{'filters' | i18n}}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{active: selectedAll}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{'allItems' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{active: selectedFavorites}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFavorites()">
|
||||
<i class="fa fa-fw fa-star" aria-hidden="true"></i> {{'favorites' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{active: selectedTrash}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectTrash()">
|
||||
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> {{'trash' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>{{'types' | i18n}}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{active: selectedType === cipherType.Login}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
||||
<i class="fa fa-fw fa-globe" aria-hidden="true"></i> {{'typeLogin' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{active: selectedType === cipherType.Card}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
||||
<i class="fa fa-fw fa-credit-card" aria-hidden="true"></i> {{'typeCard' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{active: selectedType === cipherType.Identity}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
||||
<i class="fa fa-fw fa-id-card-o" aria-hidden="true"></i> {{'typeIdentity' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{active: selectedType === cipherType.SecureNote}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
||||
<i class="fa fa-fw fa-sticky-note-o" aria-hidden="true"></i> {{'typeSecureNote' | i18n}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p *ngIf="!loaded" class="text-muted">{{'loading' | i18n}}</p>
|
||||
<ng-container *ngIf="loaded">
|
||||
<div class="heading">
|
||||
<h2>{{'folders' | i18n}}</h2>
|
||||
<button appBlurClick (click)="addFolder()" appA11yTitle="{{'addFolder' | i18n}}">
|
||||
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<ul>
|
||||
<ng-template #recursiveFolders let-folders>
|
||||
<li *ngFor="let f of folders"
|
||||
[ngClass]="{active: selectedFolder && f.node.id === selectedFolderId}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFolder(f.node)">
|
||||
<i *ngIf="f.children.length" class="fa-fw fa" title="{{'toggleCollapse' | i18n}}" aria-hidden="true"
|
||||
[ngClass]="{'fa-caret-right': isCollapsed(f.node), 'fa-caret-down': !isCollapsed(f.node)}"
|
||||
(click)="collapse(f.node)" appStopProp></i>
|
||||
<i *ngIf="f.children.length === 0" class="fa-fw fa fa-folder-o" aria-hidden="true"></i>
|
||||
{{f.node.name}}
|
||||
<span appStopProp appStopClick (click)="editFolder(f.node)" role="button"
|
||||
appA11yTitle="{{'editFolder' | i18n}}" *ngIf="f.node.id">
|
||||
<i class="fa fa-pencil fa-fw" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="fa-ul" *ngIf="f.children.length && !isCollapsed(f.node)">
|
||||
<ng-container *ngTemplateOutlet="recursiveFolders; context:{ $implicit: f.children }">
|
||||
</ng-container>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-container *ngTemplateOutlet="recursiveFolders; context:{ $implicit: nestedFolders }"></ng-container>
|
||||
<div class="inner-content">
|
||||
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedAll }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectAll()">
|
||||
<i class="fa fa-fw fa-th" aria-hidden="true"></i> {{ "allItems" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedFavorites }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFavorites()">
|
||||
<i class="fa fa-fw fa-star" aria-hidden="true"></i> {{ "favorites" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedTrash }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectTrash()">
|
||||
<i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> {{ "trash" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>{{ "types" | i18n }}</h2>
|
||||
<ul>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.Login }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Login)">
|
||||
<i class="fa fa-fw fa-globe" aria-hidden="true"></i> {{ "typeLogin" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.Card }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Card)">
|
||||
<i class="fa fa-fw fa-credit-card" aria-hidden="true"></i> {{ "typeCard" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.Identity }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.Identity)">
|
||||
<i class="fa fa-fw fa-id-card-o" aria-hidden="true"></i> {{ "typeIdentity" | i18n }}
|
||||
</a>
|
||||
</li>
|
||||
<li [ngClass]="{ active: selectedType === cipherType.SecureNote }">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectType(cipherType.SecureNote)">
|
||||
<i class="fa fa-fw fa-sticky-note-o" aria-hidden="true"></i> {{
|
||||
"typeSecureNote" | i18n
|
||||
}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p *ngIf="!loaded" class="text-muted">{{ "loading" | i18n }}</p>
|
||||
<ng-container *ngIf="loaded">
|
||||
<div class="heading">
|
||||
<h2>{{ "folders" | i18n }}</h2>
|
||||
<button appBlurClick (click)="addFolder()" appA11yTitle="{{ 'addFolder' | i18n }}">
|
||||
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<ul>
|
||||
<ng-template #recursiveFolders let-folders>
|
||||
<li
|
||||
*ngFor="let f of folders"
|
||||
[ngClass]="{ active: selectedFolder && f.node.id === selectedFolderId }"
|
||||
>
|
||||
<a href="#" appStopClick appBlurClick (click)="selectFolder(f.node)">
|
||||
<i
|
||||
*ngIf="f.children.length"
|
||||
class="fa-fw fa"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'fa-caret-right': isCollapsed(f.node),
|
||||
'fa-caret-down': !isCollapsed(f.node)
|
||||
}"
|
||||
(click)="collapse(f.node)"
|
||||
appStopProp
|
||||
></i>
|
||||
<i
|
||||
*ngIf="f.children.length === 0"
|
||||
class="fa-fw fa fa-folder-o"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{ f.node.name }}
|
||||
<span
|
||||
appStopProp
|
||||
appStopClick
|
||||
(click)="editFolder(f.node)"
|
||||
role="button"
|
||||
appA11yTitle="{{ 'editFolder' | i18n }}"
|
||||
*ngIf="f.node.id"
|
||||
>
|
||||
<i class="fa fa-pencil fa-fw" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="fa-ul" *ngIf="f.children.length && !isCollapsed(f.node)">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveFolders; context: { $implicit: f.children }"
|
||||
>
|
||||
</ng-container>
|
||||
</ul>
|
||||
<div *ngIf="collections && collections.length">
|
||||
<h2>{{'collections' | i18n}}</h2>
|
||||
<ul>
|
||||
<ng-template #recursiveCollections let-collections>
|
||||
<li *ngFor="let c of collections" [ngClass]="{active: c.node.id === selectedCollectionId}">
|
||||
<a href="#" appStopClick appBlurClick (click)="selectCollection(c.node)">
|
||||
<i *ngIf="c.children.length" class="fa-fw fa" title="{{'toggleCollapse' | i18n}}" aria-hidden="true"
|
||||
[ngClass]="{'fa-caret-right': isCollapsed(c.node), 'fa-caret-down': !isCollapsed(c.node)}"
|
||||
(click)="collapse(c.node)" appStopProp></i>
|
||||
<i *ngIf="c.children.length === 0" class="fa-fw fa fa-cube" aria-hidden="true"></i>
|
||||
{{c.node.name}}
|
||||
</a>
|
||||
<ul class="fa-ul" *ngIf="c.children.length && !isCollapsed(c.node)">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context:{ $implicit: c.children }">
|
||||
</ng-container>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-container *ngTemplateOutlet="recursiveCollections; context:{ $implicit: nestedCollections }">
|
||||
</ng-container>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<app-nav class="nav"></app-nav>
|
||||
</div>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveFolders; context: { $implicit: nestedFolders }"
|
||||
></ng-container>
|
||||
</ul>
|
||||
<div *ngIf="collections && collections.length">
|
||||
<h2>{{ "collections" | i18n }}</h2>
|
||||
<ul>
|
||||
<ng-template #recursiveCollections let-collections>
|
||||
<li
|
||||
*ngFor="let c of collections"
|
||||
[ngClass]="{ active: c.node.id === selectedCollectionId }"
|
||||
>
|
||||
<a href="#" appStopClick appBlurClick (click)="selectCollection(c.node)">
|
||||
<i
|
||||
*ngIf="c.children.length"
|
||||
class="fa-fw fa"
|
||||
title="{{ 'toggleCollapse' | i18n }}"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{
|
||||
'fa-caret-right': isCollapsed(c.node),
|
||||
'fa-caret-down': !isCollapsed(c.node)
|
||||
}"
|
||||
(click)="collapse(c.node)"
|
||||
appStopProp
|
||||
></i>
|
||||
<i *ngIf="c.children.length === 0" class="fa-fw fa fa-cube" aria-hidden="true"></i>
|
||||
{{ c.node.name }}
|
||||
</a>
|
||||
<ul class="fa-ul" *ngIf="c.children.length && !isCollapsed(c.node)">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context: { $implicit: c.children }"
|
||||
>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</li>
|
||||
</ng-template>
|
||||
<ng-container
|
||||
*ngTemplateOutlet="recursiveCollections; context: { $implicit: nestedCollections }"
|
||||
>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<app-nav class="nav"></app-nav>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
|
||||
import { GroupingsComponent as BaseGroupingsComponent } from 'jslib-angular/components/groupings.component';
|
||||
import { GroupingsComponent as BaseGroupingsComponent } from "jslib-angular/components/groupings.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-groupings',
|
||||
templateUrl: 'groupings.component.html',
|
||||
selector: "app-vault-groupings",
|
||||
templateUrl: "groupings.component.html",
|
||||
})
|
||||
export class GroupingsComponent extends BaseGroupingsComponent {
|
||||
constructor(collectionService: CollectionService, folderService: FolderService,
|
||||
stateService: StateService) {
|
||||
super(collectionService, folderService, stateService);
|
||||
}
|
||||
constructor(
|
||||
collectionService: CollectionService,
|
||||
folderService: FolderService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(collectionService, folderService, stateService);
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,54 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="passwordGenHistoryTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="passwordGenHistoryTitle">
|
||||
{{'passwordHistory' | i18n}}
|
||||
</div>
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<div class="password-wrapper monospaced" appSelectCopy
|
||||
[innerHTML]="h.password | colorPassword"></div>
|
||||
<span class="detail">{{h.date | date:'medium'}}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
||||
(click)="copy(h.password)" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="!history.length">
|
||||
{{'noPasswordsInList' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="passwordGenHistoryTitle">
|
||||
{{ "passwordHistory" | i18n }}
|
||||
</div>
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<div
|
||||
class="password-wrapper monospaced"
|
||||
appSelectCopy
|
||||
[innerHTML]="h.password | colorPassword"
|
||||
></div>
|
||||
<span class="detail">{{ h.date | date: "medium" }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(h.password)"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="right">
|
||||
<button appBlurClick type="button" (click)="clear()" class="danger"
|
||||
appA11yTitle="{{'clear' | i18n}}">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="!history.length">
|
||||
{{ "noPasswordsInList" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="clear()"
|
||||
class="danger"
|
||||
appA11yTitle="{{ 'clear' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,20 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent,
|
||||
} from 'jslib-angular/components/password-generator-history.component';
|
||||
import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "jslib-angular/components/password-generator-history.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-generator-history',
|
||||
templateUrl: 'password-generator-history.component.html',
|
||||
selector: "app-password-generator-history",
|
||||
templateUrl: "password-generator-history.component.html",
|
||||
})
|
||||
export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent {
|
||||
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
constructor(
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
}
|
||||
|
@ -1,130 +1,230 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" attr.aria-label="{{'generatePassword' | i18n}}">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
||||
{{'passwordGeneratorPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
<div class="password-block">
|
||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content condensed">
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="regenerate()">
|
||||
<i class="fa fa-fw fa-refresh" aria-hidden="true"></i> {{'regeneratePassword' | i18n}}
|
||||
</a>
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="copy()">
|
||||
<i class="fa fa-fw fa-clone" aria-hidden="true"></i> {{'copyPassword' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button type="button" (click)="toggleOptions()" appA11yTitle="{{'toggleVisibility' | i18n}}">
|
||||
<i class="fa fa-plus-square-o" aria-hidden="true" [hidden]="showOptions"></i>
|
||||
<i class="fa fa-minus-square-o" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||
{{'options' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="sr-only radio-header">{{'type' | i18n}}</label>
|
||||
<div class="radio-group text-default" appBoxRow name="PassTypeOptions"
|
||||
*ngFor="let o of passTypeOptions">
|
||||
<input type="radio" class="radio" [(ngModel)]="options.type" name="Type_{{o.value}}"
|
||||
id="type_{{o.value}}" [value]="o.value" (change)="saveOptions()"
|
||||
[checked]="options.type === o.value">
|
||||
<label class="unstyled" for="type_{{o.value}}">
|
||||
{{o.name}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions" *ngIf="options.type === 'passphrase'">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="num-words">{{'numWords' | i18n}}</label>
|
||||
<input id="num-words" type="number" min="3" max="20" (blur)="saveOptions()"
|
||||
[(ngModel)]="options.numWords">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="word-separator">{{'wordSeparator' | i18n}}</label>
|
||||
<input id="word-separator" type="text" maxlength="1" (input)="saveOptions()"
|
||||
[(ngModel)]="options.wordSeparator">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="capitalize">{{'capitalize' | i18n}}</label>
|
||||
<input id="capitalize" type="checkbox" (change)="saveOptions()"
|
||||
[(ngModel)]="options.capitalize" [disabled]="enforcedPolicyOptions?.capitalize">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="include-number">{{'includeNumber' | i18n}}</label>
|
||||
<input id="include-number" type="checkbox" (change)="saveOptions()"
|
||||
[(ngModel)]="options.includeNumber" [disabled]="enforcedPolicyOptions?.includeNumber">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="options.type === 'password'">
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||
<label for="length">{{'length' | i18n}}</label>
|
||||
<input id="length" type="number" min="5" max="128" [(ngModel)]="options.length"
|
||||
(blur)="saveOptions()">
|
||||
<input id="lengthRange" type="range" min="5" max="128" step="1"
|
||||
[(ngModel)]="options.length" (change)="sliderChanged()" (input)="sliderInput()">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="uppercase">A-Z</label>
|
||||
<input id="uppercase" type="checkbox" (change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useUppercase" [(ngModel)]="options.uppercase">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<input id="lowercase" type="checkbox" (change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useLowercase" [(ngModel)]="options.lowercase">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input id="numbers" type="checkbox" (change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useNumbers" [(ngModel)]="options.number">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input id="special" type="checkbox" (change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useSpecial" [(ngModel)]="options.special">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-number">{{'minNumbers' | i18n}}</label>
|
||||
<input id="min-number" type="number" min="0" max="9" (blur)="saveOptions()"
|
||||
[(ngModel)]="options.minNumber">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-special">{{'minSpecial' | i18n}}</label>
|
||||
<input id="min-special" type="number" min="0" max="9" (blur)="saveOptions()"
|
||||
[(ngModel)]="options.minSpecial">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="ambiguous">{{'ambiguous' | i18n}}</label>
|
||||
<input id="ambiguous" type="checkbox" (change)="saveOptions()"
|
||||
[(ngModel)]="avoidAmbiguous">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick *ngIf="showSelect" (click)="select()"
|
||||
appA11yTitle="{{'select' | i18n}}">
|
||||
<i class="fa fa-lg fa-fw fa-check" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{(showSelect ? 'cancel' : 'close') | i18n}}</button>
|
||||
</div>
|
||||
<div
|
||||
class="modal fade"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
attr.aria-label="{{ 'generatePassword' | i18n }}"
|
||||
>
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||
</app-callout>
|
||||
<div class="password-block">
|
||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content condensed">
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="regenerate()">
|
||||
<i class="fa fa-fw fa-refresh" aria-hidden="true"></i>
|
||||
{{ "regeneratePassword" | i18n }}
|
||||
</a>
|
||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="copy()">
|
||||
<i class="fa fa-fw fa-clone" aria-hidden="true"></i> {{ "copyPassword" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggleOptions()"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-plus-square-o" aria-hidden="true" [hidden]="showOptions"></i>
|
||||
<i class="fa fa-minus-square-o" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||
{{ "options" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div class="box-content-row box-content-row-radio">
|
||||
<label class="sr-only radio-header">{{ "type" | i18n }}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
name="PassTypeOptions"
|
||||
*ngFor="let o of passTypeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="options.type"
|
||||
name="Type_{{ o.value }}"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveOptions()"
|
||||
[checked]="options.type === o.value"
|
||||
/>
|
||||
<label class="unstyled" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions" *ngIf="options.type === 'passphrase'">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||
<input
|
||||
id="num-words"
|
||||
type="number"
|
||||
min="3"
|
||||
max="20"
|
||||
(blur)="saveOptions()"
|
||||
[(ngModel)]="options.numWords"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||
<input
|
||||
id="word-separator"
|
||||
type="text"
|
||||
maxlength="1"
|
||||
(input)="saveOptions()"
|
||||
[(ngModel)]="options.wordSeparator"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||
<input
|
||||
id="capitalize"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.capitalize"
|
||||
[disabled]="enforcedPolicyOptions?.capitalize"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||
<input
|
||||
id="include-number"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="options.includeNumber"
|
||||
[disabled]="enforcedPolicyOptions?.includeNumber"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="options.type === 'password'">
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||
<label for="length">{{ "length" | i18n }}</label>
|
||||
<input
|
||||
id="length"
|
||||
type="number"
|
||||
min="5"
|
||||
max="128"
|
||||
[(ngModel)]="options.length"
|
||||
(blur)="saveOptions()"
|
||||
/>
|
||||
<input
|
||||
id="lengthRange"
|
||||
type="range"
|
||||
min="5"
|
||||
max="128"
|
||||
step="1"
|
||||
[(ngModel)]="options.length"
|
||||
(change)="sliderChanged()"
|
||||
(input)="sliderInput()"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="uppercase">A-Z</label>
|
||||
<input
|
||||
id="uppercase"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useUppercase"
|
||||
[(ngModel)]="options.uppercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<input
|
||||
id="lowercase"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useLowercase"
|
||||
[(ngModel)]="options.lowercase"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input
|
||||
id="numbers"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useNumbers"
|
||||
[(ngModel)]="options.number"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input
|
||||
id="special"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[disabled]="enforcedPolicyOptions?.useSpecial"
|
||||
[(ngModel)]="options.special"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||
<input
|
||||
id="min-number"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(blur)="saveOptions()"
|
||||
[(ngModel)]="options.minNumber"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||
<input
|
||||
id="min-special"
|
||||
type="number"
|
||||
min="0"
|
||||
max="9"
|
||||
(blur)="saveOptions()"
|
||||
[(ngModel)]="options.minSpecial"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="ambiguous">{{ "ambiguous" | i18n }}</label>
|
||||
<input
|
||||
id="ambiguous"
|
||||
type="checkbox"
|
||||
(change)="saveOptions()"
|
||||
[(ngModel)]="avoidAmbiguous"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="primary"
|
||||
appBlurClick
|
||||
*ngIf="showSelect"
|
||||
(click)="select()"
|
||||
appA11yTitle="{{ 'select' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-lg fa-fw fa-check" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">
|
||||
{{ (showSelect ? "cancel" : "close") | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,20 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
PasswordGeneratorComponent as BasePasswordGeneratorComponent,
|
||||
} from 'jslib-angular/components/password-generator.component';
|
||||
import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-generator',
|
||||
templateUrl: 'password-generator.component.html',
|
||||
selector: "app-password-generator",
|
||||
templateUrl: "password-generator.component.html",
|
||||
})
|
||||
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
|
||||
constructor(passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
constructor(
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,41 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="passwordHistoryTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="passwordHistoryTitle">
|
||||
{{'passwordHistory' | i18n}}
|
||||
</div>
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<span class="text monospaced">
|
||||
{{h.password}}
|
||||
</span>
|
||||
<span class="detail">{{h.lastUsedDate | date:'medium'}}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
||||
(click)="copy(h.password)" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="!history.length">
|
||||
{{'noPasswordsInList' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="passwordHistoryTitle">
|
||||
{{ "passwordHistory" | i18n }}
|
||||
</div>
|
||||
<div class="box-content condensed">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||
<div class="row-main">
|
||||
<span class="text monospaced">
|
||||
{{ h.password }}
|
||||
</span>
|
||||
<span class="detail">{{ h.lastUsedDate | date: "medium" }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(h.password)"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="box-content-row" *ngIf="!history.length">
|
||||
{{ "noPasswordsInList" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,20 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import {
|
||||
PasswordHistoryComponent as BasePasswordHistoryComponent,
|
||||
} from 'jslib-angular/components/password-history.component';
|
||||
import { PasswordHistoryComponent as BasePasswordHistoryComponent } from "jslib-angular/components/password-history.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-history',
|
||||
templateUrl: 'password-history.component.html',
|
||||
selector: "app-password-history",
|
||||
templateUrl: "password-history.component.html",
|
||||
})
|
||||
export class PasswordHistoryComponent extends BasePasswordHistoryComponent {
|
||||
constructor(cipherService: CipherService, platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService) {
|
||||
super(cipherService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService
|
||||
) {
|
||||
super(cipherService, platformUtilsService, i18nService, window);
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,75 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="moveToOrgTitle">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="moveToOrgTitle">
|
||||
{{'moveToOrganization' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||
<div class="box-content-row">
|
||||
{{'noOrganizationsList' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organization">{{'organization' | i18n}}</label>
|
||||
<select id="organization" name="OrganizationId" [(ngModel)]="organizationId"
|
||||
(change)="filterCollections()">
|
||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'moveToOrgDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="organizations && organizations.length">
|
||||
<div class="box-header">
|
||||
{{'collections' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{'noCollectionsInList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
||||
<label for="collection_{{i}}">{{c.name}}</label>
|
||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
||||
name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header" id="moveToOrgTitle">
|
||||
{{ "moveToOrganization" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||
<div class="box-content-row">
|
||||
{{ "noOrganizationsList" | i18n }}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" appA11yTitle="{{'save' | i18n}}"
|
||||
[disabled]="form.loading || !canSave" *ngIf="organizations && organizations.length">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organization">{{ "organization" | i18n }}</label>
|
||||
<select
|
||||
id="organization"
|
||||
name="OrganizationId"
|
||||
[(ngModel)]="organizationId"
|
||||
(change)="filterCollections()"
|
||||
>
|
||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "moveToOrgDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="organizations && organizations.length">
|
||||
<div class="box-header">
|
||||
{{ "collections" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{ "noCollectionsInList" | i18n }}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index"
|
||||
appBoxRow
|
||||
>
|
||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||
<input
|
||||
id="collection_{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
appBlurClick
|
||||
type="submit"
|
||||
class="primary"
|
||||
appA11yTitle="{{ 'save' | i18n }}"
|
||||
[disabled]="form.loading || !canSave"
|
||||
*ngIf="organizations && organizations.length"
|
||||
>
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-spin fa-lg fa-fw"
|
||||
[hidden]="!form.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,23 +1,34 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
|
||||
import { ShareComponent as BaseShareComponent } from 'jslib-angular/components/share.component';
|
||||
import { ShareComponent as BaseShareComponent } from "jslib-angular/components/share.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-share',
|
||||
templateUrl: 'share.component.html',
|
||||
selector: "app-vault-share",
|
||||
templateUrl: "share.component.html",
|
||||
})
|
||||
export class ShareComponent extends BaseShareComponent {
|
||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
||||
collectionService: CollectionService, platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService, organizationService: OrganizationService) {
|
||||
super(collectionService, platformUtilsService, i18nService, cipherService,
|
||||
logService, organizationService);
|
||||
}
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
i18nService: I18nService,
|
||||
collectionService: CollectionService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService,
|
||||
organizationService: OrganizationService
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
platformUtilsService,
|
||||
i18nService,
|
||||
cipherService,
|
||||
logService,
|
||||
organizationService
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,69 @@
|
||||
<div id="vault" class="vault" attr.aria-hidden="{{showingModal}}">
|
||||
<app-vault-groupings id="groupings" class="groupings" (onAllClicked)="clearGroupingFilters()" (onFavoritesClicked)="filterFavorites()"
|
||||
(onCipherTypeClicked)="filterCipherType($event)" (onFolderClicked)="filterFolder($event.id)"
|
||||
(onAddFolder)="addFolder()" (onEditFolder)="editFolder($event.id)"
|
||||
(onCollectionClicked)="filterCollection($event.id)" (onTrashClicked)="filterDeleted()">
|
||||
</app-vault-groupings>
|
||||
<app-vault-ciphers id="items" class="items" [activeCipherId]="cipherId" (onCipherClicked)="viewCipher($event)"
|
||||
(onCipherRightClicked)="viewCipherMenu($event)" (onAddCipher)="addCipher($event)"
|
||||
(onAddCipherOptions)="addCipherOptions()">
|
||||
</app-vault-ciphers>
|
||||
<app-vault-view id="details" class="details" *ngIf="cipherId && action === 'view'" [cipherId]="cipherId"
|
||||
(onCloneCipher)="cloneCipherWithoutPasswordPrompt($event)" (onEditCipher)="editCipherWithoutPasswordPrompt($event)"
|
||||
(onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)" (onRestoredCipher)="restoredCipher($event)"
|
||||
(onDeletedCipher)="deletedCipher($event)">
|
||||
</app-vault-view>
|
||||
<app-vault-add-edit id="addEdit" class="details" *ngIf="action === 'add' || action === 'edit' || action === 'clone'"
|
||||
[cloneMode]="action === 'clone'"
|
||||
[folderId]="action === 'add' && folderId !== 'none' ? folderId : null"
|
||||
[organizationId]="action === 'add' ? addOrganizationId : null"
|
||||
[collectionIds]="action === 'add' ? addCollectionIds : null"
|
||||
[type]="action === 'add' ? (addType ? addType : type) : null" [cipherId]="(action === 'edit' || action === 'clone') ? cipherId : null"
|
||||
(onSavedCipher)="savedCipher($event)" (onDeletedCipher)="deletedCipher($event)"
|
||||
(onEditAttachments)="editCipherAttachments($event)" (onCancelled)="cancelledAddEdit($event)"
|
||||
(onShareCipher)="shareCipher($event)" (onEditCollections)="cipherCollections($event)"
|
||||
(onGeneratePassword)="openPasswordGenerator(true)">
|
||||
</app-vault-add-edit>
|
||||
<div id="logo" class="logo" *ngIf="action !== 'add' && action !== 'edit' && action !== 'view' && action !== 'clone'">
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<img class="logo-image" alt="Bitwarden" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="vault" class="vault" attr.aria-hidden="{{ showingModal }}">
|
||||
<app-vault-groupings
|
||||
id="groupings"
|
||||
class="groupings"
|
||||
(onAllClicked)="clearGroupingFilters()"
|
||||
(onFavoritesClicked)="filterFavorites()"
|
||||
(onCipherTypeClicked)="filterCipherType($event)"
|
||||
(onFolderClicked)="filterFolder($event.id)"
|
||||
(onAddFolder)="addFolder()"
|
||||
(onEditFolder)="editFolder($event.id)"
|
||||
(onCollectionClicked)="filterCollection($event.id)"
|
||||
(onTrashClicked)="filterDeleted()"
|
||||
>
|
||||
</app-vault-groupings>
|
||||
<app-vault-ciphers
|
||||
id="items"
|
||||
class="items"
|
||||
[activeCipherId]="cipherId"
|
||||
(onCipherClicked)="viewCipher($event)"
|
||||
(onCipherRightClicked)="viewCipherMenu($event)"
|
||||
(onAddCipher)="addCipher($event)"
|
||||
(onAddCipherOptions)="addCipherOptions()"
|
||||
>
|
||||
</app-vault-ciphers>
|
||||
<app-vault-view
|
||||
id="details"
|
||||
class="details"
|
||||
*ngIf="cipherId && action === 'view'"
|
||||
[cipherId]="cipherId"
|
||||
(onCloneCipher)="cloneCipherWithoutPasswordPrompt($event)"
|
||||
(onEditCipher)="editCipherWithoutPasswordPrompt($event)"
|
||||
(onViewCipherPasswordHistory)="viewCipherPasswordHistory($event)"
|
||||
(onRestoredCipher)="restoredCipher($event)"
|
||||
(onDeletedCipher)="deletedCipher($event)"
|
||||
>
|
||||
</app-vault-view>
|
||||
<app-vault-add-edit
|
||||
id="addEdit"
|
||||
class="details"
|
||||
*ngIf="action === 'add' || action === 'edit' || action === 'clone'"
|
||||
[cloneMode]="action === 'clone'"
|
||||
[folderId]="action === 'add' && folderId !== 'none' ? folderId : null"
|
||||
[organizationId]="action === 'add' ? addOrganizationId : null"
|
||||
[collectionIds]="action === 'add' ? addCollectionIds : null"
|
||||
[type]="action === 'add' ? (addType ? addType : type) : null"
|
||||
[cipherId]="action === 'edit' || action === 'clone' ? cipherId : null"
|
||||
(onSavedCipher)="savedCipher($event)"
|
||||
(onDeletedCipher)="deletedCipher($event)"
|
||||
(onEditAttachments)="editCipherAttachments($event)"
|
||||
(onCancelled)="cancelledAddEdit($event)"
|
||||
(onShareCipher)="shareCipher($event)"
|
||||
(onEditCollections)="cipherCollections($event)"
|
||||
(onGeneratePassword)="openPasswordGenerator(true)"
|
||||
>
|
||||
</app-vault-add-edit>
|
||||
<div
|
||||
id="logo"
|
||||
class="logo"
|
||||
*ngIf="action !== 'add' && action !== 'edit' && action !== 'view' && action !== 'clone'"
|
||||
>
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
<img class="logo-image" alt="Bitwarden" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #passwordGenerator></ng-template>
|
||||
<ng-template #attachments></ng-template>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,46 +1,66 @@
|
||||
<div class="box" >
|
||||
<div class="box-header">
|
||||
{{'customFields' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let field of cipher.fields">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{field.name}}</span>
|
||||
<div *ngIf="field.type === fieldType.Text">
|
||||
{{field.value || ' '}}
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Hidden">
|
||||
<span *ngIf="field.showValue" class="monospaced show-whitespace">{{field.value}}</span>
|
||||
<span *ngIf="!field.showValue" class="monospaced">{{field.maskedValue}}</span>
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Boolean">
|
||||
<i class="fa fa-check-square-o" *ngIf="field.value === 'true'" aria-hidden="true"></i>
|
||||
<i class="fa fa-square-o" *ngIf="field.value !== 'true'" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{field.value}}</span>
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Linked" class="box-content-row-flex">
|
||||
<div class="icon icon-small">
|
||||
<i class="fa fa-link" aria-hidden="true" appA11yTitle="{{'linkedValue' | i18n}}"></i>
|
||||
<span class="sr-only">{{'linkedValue' | i18n}}</span>
|
||||
</div>
|
||||
<span>{{cipher.linkedFieldI18nKey(field.linkedId) | i18n}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
*ngIf="field.type === fieldType.Hidden && cipher.viewPassword"
|
||||
(click)="toggleFieldValue(field)" role="button">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyValue' | i18n}}"
|
||||
*ngIf="field.value && field.type !== fieldType.Boolean && field.type !== fieldType.Linked &&
|
||||
!(field.type === fieldType.Hidden && !cipher.viewPassword)"
|
||||
(click)="copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')"
|
||||
role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "customFields" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let field of cipher.fields">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ field.name }}</span>
|
||||
<div *ngIf="field.type === fieldType.Text">
|
||||
{{ field.value || " " }}
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Hidden">
|
||||
<span *ngIf="field.showValue" class="monospaced show-whitespace">{{ field.value }}</span>
|
||||
<span *ngIf="!field.showValue" class="monospaced">{{ field.maskedValue }}</span>
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Boolean">
|
||||
<i class="fa fa-check-square-o" *ngIf="field.value === 'true'" aria-hidden="true"></i>
|
||||
<i class="fa fa-square-o" *ngIf="field.value !== 'true'" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ field.value }}</span>
|
||||
</div>
|
||||
<div *ngIf="field.type === fieldType.Linked" class="box-content-row-flex">
|
||||
<div class="icon icon-small">
|
||||
<i class="fa fa-link" aria-hidden="true" appA11yTitle="{{ 'linkedValue' | i18n }}"></i>
|
||||
<span class="sr-only">{{ "linkedValue" | i18n }}</span>
|
||||
</div>
|
||||
<span>{{ cipher.linkedFieldI18nKey(field.linkedId) | i18n }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
*ngIf="field.type === fieldType.Hidden && cipher.viewPassword"
|
||||
(click)="toggleFieldValue(field)"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !field.showValue, 'fa-eye-slash': field.showValue }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
*ngIf="
|
||||
field.value &&
|
||||
field.type !== fieldType.Boolean &&
|
||||
field.type !== fieldType.Linked &&
|
||||
!(field.type === fieldType.Hidden && !cipher.viewPassword)
|
||||
"
|
||||
(click)="
|
||||
copy(field.value, 'value', field.type === fieldType.Hidden ? 'H_Field' : 'Field')
|
||||
"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,19 +1,15 @@
|
||||
import {
|
||||
Component,
|
||||
} from '@angular/core';
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
|
||||
import {
|
||||
ViewCustomFieldsComponent as BaseViewCustomFieldsComponent
|
||||
} from 'jslib-angular/components/view-custom-fields.component';
|
||||
import { ViewCustomFieldsComponent as BaseViewCustomFieldsComponent } from "jslib-angular/components/view-custom-fields.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-view-custom-fields',
|
||||
templateUrl: 'view-custom-fields.component.html',
|
||||
selector: "app-vault-view-custom-fields",
|
||||
templateUrl: "view-custom-fields.component.html",
|
||||
})
|
||||
export class ViewCustomFieldsComponent extends BaseViewCustomFieldsComponent {
|
||||
constructor(eventService: EventService) {
|
||||
super(eventService);
|
||||
}
|
||||
constructor(eventService: EventService) {
|
||||
super(eventService);
|
||||
}
|
||||
}
|
||||
|
@ -1,265 +1,422 @@
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="cipher">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'itemInformation' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<span class="row-label">{{'name' | i18n}}</span>
|
||||
{{cipher.name}}
|
||||
</div>
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.login">
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.username">
|
||||
<div class="row-main">
|
||||
<span class="row-label draggable" draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.login.username)">{{'username' | i18n}}</span>
|
||||
{{cipher.login.username}}
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyUsername' | i18n}}"
|
||||
(click)="copy(cipher.login.username, 'username', 'Username')" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.password">
|
||||
<div class="row-main">
|
||||
<span class="row-label draggable" draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.login.password)">{{'password' | i18n}}</span>
|
||||
<div *ngIf="!showPassword" class="monospaced">
|
||||
{{cipher.login.maskedPassword}}</div>
|
||||
<div *ngIf="showPassword" class="monospaced password-wrapper" appSelectCopy
|
||||
[innerHTML]="cipher.login.password | colorPassword"></div>
|
||||
</div>
|
||||
<div class="action-buttons" *ngIf=cipher.viewPassword>
|
||||
<button type="button" #checkPasswordBtn class="row-btn btn" appBlurClick
|
||||
appA11yTitle="{{'checkPassword' | i18n}}" (click)="checkPassword()"
|
||||
[appApiAction]="checkPasswordPromise" [disabled]="checkPasswordBtn.loading">
|
||||
<i class="fa fa-lg fa-check-circle" [hidden]="checkPasswordBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
<i class="fa fa-lg fa-spinner fa-spin" [hidden]="!checkPasswordBtn.loading"
|
||||
aria-hidden="true"></i>
|
||||
</button>
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
(click)="togglePassword()" role="button">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyPassword' | i18n}}"
|
||||
(click)="copy(cipher.login.password, 'password', 'Password')" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex totp" [ngClass]="{'low': totpLow}"
|
||||
*ngIf="cipher.login.totp && totpCode">
|
||||
<div class="row-main">
|
||||
<span class="row-label draggable" draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, totpCode)">{{'verificationCodeTotp' | i18n}}</span>
|
||||
<span class="totp-code">{{totpCodeFormatted}}</span>
|
||||
</div>
|
||||
<span class="totp-countdown">
|
||||
<span class="totp-sec">{{totpSec}}</span>
|
||||
<svg>
|
||||
<g>
|
||||
<circle class="totp-circle inner" r="12.6" cy="16" cx="16"
|
||||
[ngStyle]="{'stroke-dashoffset.px': totpDash}"></circle>
|
||||
<circle class="totp-circle outer" r="14" cy="16" cx="16"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.card">
|
||||
<div class="box-content-row" *ngIf="cipher.card.cardholderName">
|
||||
<span class="row-label">{{'cardholderName' | i18n}}</span>
|
||||
{{cipher.card.cardholderName}}
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.number">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{'number' | i18n}}</span>
|
||||
<span *ngIf="!showCardNumber" class="monospaced">{{cipher.card.maskedNumber}}</span>
|
||||
<span *ngIf="showCardNumber" class="monospaced">{{cipher.card.number}}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
(click)="toggleCardNumber()" role="button">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyNumber' | i18n}}"
|
||||
(click)="copy(cipher.card.number, 'number', 'Card Number')" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.card.brand">
|
||||
<span class="row-label">{{'brand' | i18n}}</span>
|
||||
{{cipher.card.brand}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.card.expiration">
|
||||
<span class="row-label">{{'expiration' | i18n}}</span>
|
||||
{{cipher.card.expiration}}
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.code">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{'securityCode' | i18n}}</span>
|
||||
<span *ngIf="!showCardCode" class="monospaced">{{cipher.card.maskedCode}}</span>
|
||||
<span *ngIf="showCardCode" class="monospaced">{{cipher.card.code}}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
(click)="toggleCardCode()" role="button">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copySecurityCode' | i18n}}"
|
||||
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Identity -->
|
||||
<div *ngIf="cipher.identity">
|
||||
<div class="box-content-row" *ngIf="cipher.identity.fullName">
|
||||
<span class="row-label">{{'identityName' | i18n}}</span>
|
||||
{{cipher.identity.fullName}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.username">
|
||||
<span class="row-label">{{'username' | i18n}}</span>
|
||||
{{cipher.identity.username}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.company">
|
||||
<span class="row-label">{{'company' | i18n}}</span>
|
||||
{{cipher.identity.company}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.ssn">
|
||||
<span class="row-label">{{'ssn' | i18n}}</span>
|
||||
{{cipher.identity.ssn}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.passportNumber">
|
||||
<span class="row-label">{{'passportNumber' | i18n}}</span>
|
||||
{{cipher.identity.passportNumber}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.licenseNumber">
|
||||
<span class="row-label">{{'licenseNumber' | i18n}}</span>
|
||||
{{cipher.identity.licenseNumber}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.email">
|
||||
<span class="row-label">{{'email' | i18n}}</span>
|
||||
{{cipher.identity.email}}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.phone">
|
||||
<span class="row-label">{{'phone' | i18n}}</span>
|
||||
{{cipher.identity.phone}}
|
||||
</div>
|
||||
<div class="box-content-row"
|
||||
*ngIf="cipher.identity.address1 || cipher.identity.city || cipher.identity.country">
|
||||
<span class="row-label">{{'address' | i18n}}</span>
|
||||
<div *ngIf="cipher.identity.address1">{{cipher.identity.address1}}</div>
|
||||
<div *ngIf="cipher.identity.address2">{{cipher.identity.address2}}</div>
|
||||
<div *ngIf="cipher.identity.address3">{{cipher.identity.address3}}</div>
|
||||
<div *ngIf="cipher.identity.fullAddressPart2">{{cipher.identity.fullAddressPart2}}</div>
|
||||
<div *ngIf="cipher.identity.country">{{cipher.identity.country}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inner-content" *ngIf="cipher">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{ "itemInformation" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row">
|
||||
<span class="row-label">{{ "name" | i18n }}</span>
|
||||
{{ cipher.name }}
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.login && cipher.login.hasUris">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" *ngFor="let u of cipher.login.uris; let i = index">
|
||||
<div class="row-main">
|
||||
<span class="row-label" *ngIf="!u.isWebsite">{{'uri' | i18n}}</span>
|
||||
<span class="row-label" *ngIf="u.isWebsite">{{'website' | i18n}}</span>
|
||||
<span title="{{u.uri}}">{{u.hostOrUri}}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'launch' | i18n}}" *ngIf="u.canLaunch"
|
||||
(click)="launch(u)" role="button">
|
||||
<i class="fa fa-lg fa-share-square-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appA11yTitle="{{'copyUri' | i18n}}"
|
||||
(click)="copy(u.uri, u.isWebsite ? 'website' : 'uri', 'URI')" role="button">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.login">
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.username">
|
||||
<div class="row-main">
|
||||
<span
|
||||
class="row-label draggable"
|
||||
draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.login.username)"
|
||||
>{{ "username" | i18n }}</span
|
||||
>
|
||||
{{ cipher.login.username }}
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||
(click)="copy(cipher.login.username, 'username', 'Username')"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.login.password">
|
||||
<div class="row-main">
|
||||
<span
|
||||
class="row-label draggable"
|
||||
draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, cipher.login.password)"
|
||||
>{{ "password" | i18n }}</span
|
||||
>
|
||||
<div *ngIf="!showPassword" class="monospaced">
|
||||
{{ cipher.login.maskedPassword }}
|
||||
</div>
|
||||
<div
|
||||
*ngIf="showPassword"
|
||||
class="monospaced password-wrapper"
|
||||
appSelectCopy
|
||||
[innerHTML]="cipher.login.password | colorPassword"
|
||||
></div>
|
||||
</div>
|
||||
<div class="action-buttons" *ngIf="cipher.viewPassword">
|
||||
<button
|
||||
type="button"
|
||||
#checkPasswordBtn
|
||||
class="row-btn btn"
|
||||
appBlurClick
|
||||
appA11yTitle="{{ 'checkPassword' | i18n }}"
|
||||
(click)="checkPassword()"
|
||||
[appApiAction]="checkPasswordPromise"
|
||||
[disabled]="checkPasswordBtn.loading"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg fa-check-circle"
|
||||
[hidden]="checkPasswordBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-lg fa-spinner fa-spin"
|
||||
[hidden]="!checkPasswordBtn.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showPassword, 'fa-eye-slash': showPassword }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy(cipher.login.password, 'password', 'Password')"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row box-content-row-flex totp"
|
||||
[ngClass]="{ low: totpLow }"
|
||||
*ngIf="cipher.login.totp && totpCode"
|
||||
>
|
||||
<div class="row-main">
|
||||
<span
|
||||
class="row-label draggable"
|
||||
draggable="true"
|
||||
(dragstart)="setTextDataOnDrag($event, totpCode)"
|
||||
>{{ "verificationCodeTotp" | i18n }}</span
|
||||
>
|
||||
<span class="totp-code">{{ totpCodeFormatted }}</span>
|
||||
</div>
|
||||
<span class="totp-countdown">
|
||||
<span class="totp-sec">{{ totpSec }}</span>
|
||||
<svg>
|
||||
<g>
|
||||
<circle
|
||||
class="totp-circle inner"
|
||||
r="12.6"
|
||||
cy="16"
|
||||
cx="16"
|
||||
[ngStyle]="{ 'stroke-dashoffset.px': totpDash }"
|
||||
></circle>
|
||||
<circle class="totp-circle outer" r="14" cy="16" cx="16"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.notes">
|
||||
<div class="box-header">
|
||||
{{'notes' | i18n}}
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.card">
|
||||
<div class="box-content-row" *ngIf="cipher.card.cardholderName">
|
||||
<span class="row-label">{{ "cardholderName" | i18n }}</span>
|
||||
{{ cipher.card.cardholderName }}
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.number">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "number" | i18n }}</span>
|
||||
<span *ngIf="!showCardNumber" class="monospaced">{{ cipher.card.maskedNumber }}</span>
|
||||
<span *ngIf="showCardNumber" class="monospaced">{{ cipher.card.number }}</span>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row pre-wrap">{{cipher.notes}}</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleCardNumber()"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showCardNumber, 'fa-eye-slash': showCardNumber }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyNumber' | i18n }}"
|
||||
(click)="copy(cipher.card.number, 'number', 'Card Number')"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.card.brand">
|
||||
<span class="row-label">{{ "brand" | i18n }}</span>
|
||||
{{ cipher.card.brand }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.card.expiration">
|
||||
<span class="row-label">{{ "expiration" | i18n }}</span>
|
||||
{{ cipher.card.expiration }}
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" *ngIf="cipher.card.code">
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "securityCode" | i18n }}</span>
|
||||
<span *ngIf="!showCardCode" class="monospaced">{{ cipher.card.maskedCode }}</span>
|
||||
<span *ngIf="showCardCode" class="monospaced">{{ cipher.card.code }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="toggleCardCode()"
|
||||
role="button"
|
||||
>
|
||||
<i
|
||||
class="fa fa-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'fa-eye': !showCardCode, 'fa-eye-slash': showCardCode }"
|
||||
></i>
|
||||
</a>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copySecurityCode' | i18n }}"
|
||||
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-view-custom-fields *ngIf="cipher.hasFields" [cipher]="cipher"
|
||||
[promptPassword]="promptPassword.bind(this)" [copy]="copy.bind(this)">
|
||||
</app-vault-view-custom-fields>
|
||||
<div class="box" *ngIf="cipher.hasAttachments && (canAccessPremium || cipher.organizationId)">
|
||||
<div class="box-header">
|
||||
{{'attachments' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a class="box-content-row box-content-row-flex text-default"
|
||||
*ngFor="let attachment of cipher.attachments" href="#" appStopClick appBlurCLick
|
||||
(click)="downloadAttachment(attachment)">
|
||||
<span class="row-main">{{attachment.fileName}}</span>
|
||||
<small class="row-sub-label">{{attachment.sizeName}}</small>
|
||||
<i class="fa fa-download fa-fw row-sub-icon" *ngIf="!attachment.downloading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-fw fa-spin row-sub-icon" *ngIf="attachment.downloading"
|
||||
aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-footer">
|
||||
<div>
|
||||
<b class="font-weight-semibold">{{'dateUpdated' | i18n}}:</b>
|
||||
{{cipher.revisionDate | date:'medium'}}
|
||||
</div>
|
||||
<div *ngIf="cipher.passwordRevisionDisplayDate">
|
||||
<b class="font-weight-semibold">{{'datePasswordUpdated' | i18n}}:</b>
|
||||
{{cipher.passwordRevisionDisplayDate | date:'medium'}}
|
||||
</div>
|
||||
<div *ngIf="cipher.hasPasswordHistory">
|
||||
<b class="font-weight-semibold">{{'passwordHistory' | i18n}}:</b>
|
||||
<a href="#" (click)="viewHistory()" appStopClick role="button"
|
||||
appA11yTitle="{{'passwordHistory' | i18n}}, {{cipher.passwordHistory.length}}">
|
||||
<span aria-hidden="true">{{cipher.passwordHistory.length}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Identity -->
|
||||
<div *ngIf="cipher.identity">
|
||||
<div class="box-content-row" *ngIf="cipher.identity.fullName">
|
||||
<span class="row-label">{{ "identityName" | i18n }}</span>
|
||||
{{ cipher.identity.fullName }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.username">
|
||||
<span class="row-label">{{ "username" | i18n }}</span>
|
||||
{{ cipher.identity.username }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.company">
|
||||
<span class="row-label">{{ "company" | i18n }}</span>
|
||||
{{ cipher.identity.company }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.ssn">
|
||||
<span class="row-label">{{ "ssn" | i18n }}</span>
|
||||
{{ cipher.identity.ssn }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.passportNumber">
|
||||
<span class="row-label">{{ "passportNumber" | i18n }}</span>
|
||||
{{ cipher.identity.passportNumber }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.licenseNumber">
|
||||
<span class="row-label">{{ "licenseNumber" | i18n }}</span>
|
||||
{{ cipher.identity.licenseNumber }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.email">
|
||||
<span class="row-label">{{ "email" | i18n }}</span>
|
||||
{{ cipher.identity.email }}
|
||||
</div>
|
||||
<div class="box-content-row" *ngIf="cipher.identity.phone">
|
||||
<span class="row-label">{{ "phone" | i18n }}</span>
|
||||
{{ cipher.identity.phone }}
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row"
|
||||
*ngIf="cipher.identity.address1 || cipher.identity.city || cipher.identity.country"
|
||||
>
|
||||
<span class="row-label">{{ "address" | i18n }}</span>
|
||||
<div *ngIf="cipher.identity.address1">{{ cipher.identity.address1 }}</div>
|
||||
<div *ngIf="cipher.identity.address2">{{ cipher.identity.address2 }}</div>
|
||||
<div *ngIf="cipher.identity.address3">{{ cipher.identity.address3 }}</div>
|
||||
<div *ngIf="cipher.identity.fullAddressPart2">
|
||||
{{ cipher.identity.fullAddressPart2 }}
|
||||
</div>
|
||||
<div *ngIf="cipher.identity.country">{{ cipher.identity.country }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.login && cipher.login.hasUris">
|
||||
<div class="box-content">
|
||||
<div
|
||||
class="box-content-row box-content-row-flex"
|
||||
*ngFor="let u of cipher.login.uris; let i = index"
|
||||
>
|
||||
<div class="row-main">
|
||||
<span class="row-label" *ngIf="!u.isWebsite">{{ "uri" | i18n }}</span>
|
||||
<span class="row-label" *ngIf="u.isWebsite">{{ "website" | i18n }}</span>
|
||||
<span title="{{ u.uri }}">{{ u.hostOrUri }}</span>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'launch' | i18n }}"
|
||||
*ngIf="u.canLaunch"
|
||||
(click)="launch(u)"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-share-square-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
class="row-btn"
|
||||
href="#"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyUri' | i18n }}"
|
||||
(click)="copy(u.uri, u.isWebsite ? 'website' : 'uri', 'URI')"
|
||||
role="button"
|
||||
>
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="cipher.notes">
|
||||
<div class="box-header">
|
||||
{{ "notes" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row pre-wrap">{{ cipher.notes }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-vault-view-custom-fields
|
||||
*ngIf="cipher.hasFields"
|
||||
[cipher]="cipher"
|
||||
[promptPassword]="promptPassword.bind(this)"
|
||||
[copy]="copy.bind(this)"
|
||||
>
|
||||
</app-vault-view-custom-fields>
|
||||
<div class="box" *ngIf="cipher.hasAttachments && (canAccessPremium || cipher.organizationId)">
|
||||
<div class="box-header">
|
||||
{{ "attachments" | i18n }}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<a
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
*ngFor="let attachment of cipher.attachments"
|
||||
href="#"
|
||||
appStopClick
|
||||
appBlurCLick
|
||||
(click)="downloadAttachment(attachment)"
|
||||
>
|
||||
<span class="row-main">{{ attachment.fileName }}</span>
|
||||
<small class="row-sub-label">{{ attachment.sizeName }}</small>
|
||||
<i
|
||||
class="fa fa-download fa-fw row-sub-icon"
|
||||
*ngIf="!attachment.downloading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="fa fa-spinner fa-fw fa-spin row-sub-icon"
|
||||
*ngIf="attachment.downloading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-footer">
|
||||
<div>
|
||||
<b class="font-weight-semibold">{{ "dateUpdated" | i18n }}:</b>
|
||||
{{ cipher.revisionDate | date: "medium" }}
|
||||
</div>
|
||||
<div *ngIf="cipher.passwordRevisionDisplayDate">
|
||||
<b class="font-weight-semibold">{{ "datePasswordUpdated" | i18n }}:</b>
|
||||
{{ cipher.passwordRevisionDisplayDate | date: "medium" }}
|
||||
</div>
|
||||
<div *ngIf="cipher.hasPasswordHistory">
|
||||
<b class="font-weight-semibold">{{ "passwordHistory" | i18n }}:</b>
|
||||
<a
|
||||
href="#"
|
||||
(click)="viewHistory()"
|
||||
appStopClick
|
||||
role="button"
|
||||
appA11yTitle="{{ 'passwordHistory' | i18n }}, {{ cipher.passwordHistory.length }}"
|
||||
>
|
||||
<span aria-hidden="true">{{ cipher.passwordHistory.length }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer" *ngIf="cipher">
|
||||
<button appBlurClick class="primary" (click)="edit()" appA11yTitle="{{'edit' | i18n}}" *ngIf="!cipher.isDeleted">
|
||||
<i class="fa fa-pencil fa-fw fa-lg" aria-hidden="true"></i>
|
||||
<button
|
||||
appBlurClick
|
||||
class="primary"
|
||||
(click)="edit()"
|
||||
appA11yTitle="{{ 'edit' | i18n }}"
|
||||
*ngIf="!cipher.isDeleted"
|
||||
>
|
||||
<i class="fa fa-pencil fa-fw fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
appBlurClick
|
||||
class="primary"
|
||||
(click)="restore()"
|
||||
appA11yTitle="{{ 'restore' | i18n }}"
|
||||
*ngIf="cipher.isDeleted"
|
||||
>
|
||||
<i class="fa fa-undo fa-fw fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
appBlurClick
|
||||
class="primary"
|
||||
*ngIf="!cipher?.organizationId && !cipher.isDeleted"
|
||||
(click)="clone()"
|
||||
appA11yTitle="{{ 'clone' | i18n }}"
|
||||
>
|
||||
<i class="fa fa-files-o fa-fw fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="right">
|
||||
<button
|
||||
appBlurClick
|
||||
type="button"
|
||||
(click)="delete()"
|
||||
class="danger"
|
||||
appA11yTitle="{{ (cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n }}"
|
||||
>
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button appBlurClick class="primary" (click)="restore()" appA11yTitle="{{'restore' | i18n}}"
|
||||
*ngIf="cipher.isDeleted">
|
||||
<i class="fa fa-undo fa-fw fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button appBlurClick class="primary" *ngIf="!cipher?.organizationId && !cipher.isDeleted" (click)="clone()"
|
||||
appA11yTitle="{{'clone' | i18n}}">
|
||||
<i class="fa fa-files-o fa-fw fa-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="right">
|
||||
<button appBlurClick type="button" (click)="delete()" class="danger"
|
||||
appA11yTitle="{{(cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n}}">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,92 +1,117 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
OnChanges,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
OnChanges,
|
||||
Output,
|
||||
} from "@angular/core";
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { AuditService } from 'jslib-common/abstractions/audit.service';
|
||||
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
|
||||
import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { TokenService } from 'jslib-common/abstractions/token.service';
|
||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { AuditService } from "jslib-common/abstractions/audit.service";
|
||||
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||
import { EventService } from "jslib-common/abstractions/event.service";
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { LogService } from "jslib-common/abstractions/log.service";
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||
import { StateService } from "jslib-common/abstractions/state.service";
|
||||
import { TokenService } from "jslib-common/abstractions/token.service";
|
||||
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||
|
||||
import { ViewComponent as BaseViewComponent } from 'jslib-angular/components/view.component';
|
||||
import { ViewComponent as BaseViewComponent } from "jslib-angular/components/view.component";
|
||||
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
|
||||
const BroadcasterSubscriptionId = 'ViewComponent';
|
||||
const BroadcasterSubscriptionId = "ViewComponent";
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-view',
|
||||
templateUrl: 'view.component.html',
|
||||
selector: "app-vault-view",
|
||||
templateUrl: "view.component.html",
|
||||
})
|
||||
export class ViewComponent extends BaseViewComponent implements OnChanges {
|
||||
@Output() onViewCipherPasswordHistory = new EventEmitter<CipherView>();
|
||||
@Output() onViewCipherPasswordHistory = new EventEmitter<CipherView>();
|
||||
|
||||
constructor(cipherService: CipherService, totpService: TotpService,
|
||||
tokenService: TokenService, i18nService: I18nService,
|
||||
cryptoService: CryptoService, platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService, broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone, changeDetectorRef: ChangeDetectorRef,
|
||||
eventService: EventService, apiService: ApiService,
|
||||
private messagingService: MessagingService, passwordRepromptService: PasswordRepromptService,
|
||||
logService: LogService, stateService: StateService) {
|
||||
super(cipherService, totpService, tokenService, i18nService, cryptoService, platformUtilsService,
|
||||
auditService, window, broadcasterService, ngZone, changeDetectorRef, eventService,
|
||||
apiService, passwordRepromptService, logService, stateService);
|
||||
}
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case 'windowHidden':
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async ngOnChanges() {
|
||||
await super.load();
|
||||
}
|
||||
|
||||
viewHistory() {
|
||||
this.onViewCipherPasswordHistory.emit(this.cipher);
|
||||
}
|
||||
|
||||
async copy(value: string, typeI18nKey: string, aType: string) {
|
||||
super.copy(value, typeI18nKey, aType);
|
||||
this.messagingService.send('minimizeOnCopy');
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
this.showCardNumber = false;
|
||||
this.showCardCode = false;
|
||||
if (this.cipher !== null && this.cipher.hasFields) {
|
||||
this.cipher.fields.forEach(field => {
|
||||
field.showValue = false;
|
||||
});
|
||||
constructor(
|
||||
cipherService: CipherService,
|
||||
totpService: TotpService,
|
||||
tokenService: TokenService,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
broadcasterService: BroadcasterService,
|
||||
ngZone: NgZone,
|
||||
changeDetectorRef: ChangeDetectorRef,
|
||||
eventService: EventService,
|
||||
apiService: ApiService,
|
||||
private messagingService: MessagingService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
logService: LogService,
|
||||
stateService: StateService
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
totpService,
|
||||
tokenService,
|
||||
i18nService,
|
||||
cryptoService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
window,
|
||||
broadcasterService,
|
||||
ngZone,
|
||||
changeDetectorRef,
|
||||
eventService,
|
||||
apiService,
|
||||
passwordRepromptService,
|
||||
logService,
|
||||
stateService
|
||||
);
|
||||
}
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
this.ngZone.run(() => {
|
||||
switch (message.command) {
|
||||
case "windowHidden":
|
||||
this.onWindowHidden();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||
}
|
||||
|
||||
async ngOnChanges() {
|
||||
await super.load();
|
||||
}
|
||||
|
||||
viewHistory() {
|
||||
this.onViewCipherPasswordHistory.emit(this.cipher);
|
||||
}
|
||||
|
||||
async copy(value: string, typeI18nKey: string, aType: string) {
|
||||
super.copy(value, typeI18nKey, aType);
|
||||
this.messagingService.send("minimizeOnCopy");
|
||||
}
|
||||
|
||||
onWindowHidden() {
|
||||
this.showPassword = false;
|
||||
this.showCardNumber = false;
|
||||
this.showCardCode = false;
|
||||
if (this.cipher !== null && this.cipher.hasFields) {
|
||||
this.cipher.fields.forEach((field) => {
|
||||
field.showValue = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
48
src/entry.ts
48
src/entry.ts
@ -1,4 +1,4 @@
|
||||
import { NativeMessagingProxy } from './proxy/native-messaging-proxy';
|
||||
import { NativeMessagingProxy } from "./proxy/native-messaging-proxy";
|
||||
|
||||
// We need to import the other dependencies using `require` since `import` will
|
||||
// generate `Error: Cannot find module 'electron'`. The cause of this error is
|
||||
@ -6,28 +6,30 @@ import { NativeMessagingProxy } from './proxy/native-messaging-proxy';
|
||||
// which removes the electron module. This flag is needed for stdin/out to work
|
||||
// properly on Windows.
|
||||
|
||||
if (process.argv.some(arg => arg.indexOf('chrome-extension://') !== -1 || arg.indexOf('{') !== -1)) {
|
||||
if (process.platform === 'darwin') {
|
||||
// tslint:disable-next-line
|
||||
const app = require('electron').app;
|
||||
|
||||
app.on('ready', () => {
|
||||
app.dock.hide();
|
||||
});
|
||||
}
|
||||
|
||||
process.stdout.on('error', e => {
|
||||
if (e.code === 'EPIPE') {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
const proxy = new NativeMessagingProxy();
|
||||
proxy.run();
|
||||
} else {
|
||||
if (
|
||||
process.argv.some((arg) => arg.indexOf("chrome-extension://") !== -1 || arg.indexOf("{") !== -1)
|
||||
) {
|
||||
if (process.platform === "darwin") {
|
||||
// tslint:disable-next-line
|
||||
const Main = require('./main').Main;
|
||||
const app = require("electron").app;
|
||||
|
||||
const main = new Main();
|
||||
main.bootstrap();
|
||||
app.on("ready", () => {
|
||||
app.dock.hide();
|
||||
});
|
||||
}
|
||||
|
||||
process.stdout.on("error", (e) => {
|
||||
if (e.code === "EPIPE") {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
const proxy = new NativeMessagingProxy();
|
||||
proxy.run();
|
||||
} else {
|
||||
// tslint:disable-next-line
|
||||
const Main = require("./main").Main;
|
||||
|
||||
const main = new Main();
|
||||
main.bootstrap();
|
||||
}
|
||||
|
2
src/global.d.ts
vendored
2
src/global.d.ts
vendored
@ -1 +1 @@
|
||||
declare module 'forcefocus';
|
||||
declare module "forcefocus";
|
||||
|
@ -1,16 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: *; child-src *; frame-src *; connect-src *;">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: *; child-src *; frame-src *; connect-src *;"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Bitwarden</title>
|
||||
<base href="">
|
||||
</head>
|
||||
<body class="layout_frontend">
|
||||
<base href="" />
|
||||
</head>
|
||||
<body class="layout_frontend">
|
||||
<app-root>
|
||||
<div id="loading"><i class="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i></div>
|
||||
<div id="loading"><i class="fa fa-spinner fa-spin fa-3x" aria-hidden="true"></i></div>
|
||||
</app-root>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user