bitwarden-estensione-browser/apps/browser/src/popup/vault/current-tab.component.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

260 lines
8.2 KiB
TypeScript
Raw Normal View History

2018-04-05 04:59:42 +02:00
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
2019-08-19 15:31:05 +02:00
import { Router } from "@angular/router";
2019-08-19 15:17:40 +02:00
2022-06-14 17:10:53 +02:00
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType";
import { CipherType } from "@bitwarden/common/enums/cipherType";
import { Utils } from "@bitwarden/common/misc/utils";
import { CipherView } from "@bitwarden/common/models/view/cipherView";
2018-04-06 20:03:35 +02:00
2022-02-24 18:14:04 +01:00
import { BrowserApi } from "../../browser/browserApi";
2018-04-06 20:03:35 +02:00
import { AutofillService } from "../../services/abstractions/autofill.service";
[Feature] End User Vault Refresh (#2545) * Initial org filter work * update jslib * Move filter to below cipher length check * don't show vault filter in personal or org folder * Use family icon for families org * jslib and auth guard updates * lint fixes * rename GroupingsComponent to VaultFilterComponent * fix no folder showing all items * Add checks for PersonalOwnership policy * update css class names * lint fixes * cleanup * Some final cleanup * import order lint fix * remove unused import * Use smaller icon for chevron * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * fix lint error * remove extra localizations * rename orgFilter -> vaultSelect * Rename orgFilterService to VaultSelectService * lint fixes * combine vault select service with vault filter service * Use base vault filter service methods * Use VaultFilter model and other small fixes * lint fixes * Final restructuring pass * Update jslib and remove extra function * Remove extra imports * remove space * Remove vaultFilterService from background services * Update jslib to latest on feature branch * merge fix * update jslib * [feat] Implement EUVR for desktop Should contain only https://github.com/bitwarden/desktop/pull/1487, with merge resolutions and style fixes * [fix] Delete unused GroupingsComponentTemplate * [dep] Update jslib Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-05-09 14:19:18 +02:00
import { VaultFilterService } from "../../services/vaultFilter.service";
2018-04-06 20:03:35 +02:00
import { PopupUtilsService } from "../services/popup-utils.service";
2018-04-06 21:33:20 +02:00
const BroadcasterSubscriptionId = "CurrentTabComponent";
2018-04-06 20:03:35 +02:00
2018-04-05 04:59:42 +02:00
@Component({
2018-04-06 20:03:35 +02:00
selector: "app-current-tab",
templateUrl: "current-tab.component.html",
2018-04-05 04:59:42 +02:00
})
2018-04-06 21:33:20 +02:00
export class CurrentTabComponent implements OnInit, OnDestroy {
2018-04-06 20:03:35 +02:00
pageDetails: any[] = [];
2018-04-06 20:18:28 +02:00
cardCiphers: CipherView[];
identityCiphers: CipherView[];
loginCiphers: CipherView[];
2018-04-06 20:03:35 +02:00
url: string;
hostname: string;
2018-04-09 16:50:28 +02:00
searchText: string;
2018-04-06 20:03:35 +02:00
inSidebar = false;
2019-03-14 03:54:18 +01:00
searchTypeSearch = false;
2018-04-06 20:03:35 +02:00
loaded = false;
[Feature] End User Vault Refresh (#2545) * Initial org filter work * update jslib * Move filter to below cipher length check * don't show vault filter in personal or org folder * Use family icon for families org * jslib and auth guard updates * lint fixes * rename GroupingsComponent to VaultFilterComponent * fix no folder showing all items * Add checks for PersonalOwnership policy * update css class names * lint fixes * cleanup * Some final cleanup * import order lint fix * remove unused import * Use smaller icon for chevron * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * fix lint error * remove extra localizations * rename orgFilter -> vaultSelect * Rename orgFilterService to VaultSelectService * lint fixes * combine vault select service with vault filter service * Use base vault filter service methods * Use VaultFilter model and other small fixes * lint fixes * Final restructuring pass * Update jslib and remove extra function * Remove extra imports * remove space * Remove vaultFilterService from background services * Update jslib to latest on feature branch * merge fix * update jslib * [feat] Implement EUVR for desktop Should contain only https://github.com/bitwarden/desktop/pull/1487, with merge resolutions and style fixes * [fix] Delete unused GroupingsComponentTemplate * [dep] Update jslib Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-05-09 14:19:18 +02:00
showOrganizations = false;
2021-12-21 15:43:35 +01:00
2018-04-23 17:30:12 +02:00
private totpCode: string;
private totpTimeout: number;
private loadedTimeout: number;
2018-08-13 17:53:16 +02:00
private searchTimeout: number;
2021-12-21 15:43:35 +01:00
2018-04-06 20:03:35 +02:00
constructor(
private platformUtilsService: PlatformUtilsService,
private cipherService: CipherService,
private popupUtilsService: PopupUtilsService,
private autofillService: AutofillService,
2021-12-07 20:42:18 +01:00
private i18nService: I18nService,
private router: Router,
2018-04-06 21:33:20 +02:00
private ngZone: NgZone,
private broadcasterService: BroadcasterService,
2018-08-13 17:53:16 +02:00
private changeDetectorRef: ChangeDetectorRef,
private syncService: SyncService,
private searchService: SearchService,
[Account Switching] Base changes for account switching (#2250) * Pull in jslib * Create new state models * Create browser specific stateService * Remove registration deprecated services, register stateService * Replace usage of deprecated services (user, constants) * Add missing properties to BrowserGroupingsComponentState * Remove StorageService from initFactory * Clear the correct state * Add null check when restoring send-grouping state * add remember email * Initialize stateservice in services.module * Fix 'lock now' not working * Comment to remove setting defaults on install * Pull jslib * Remove setting defaults on install * Bump jslib * Pass the current userId to services when logging out * Bump jslib * Override vaultTimeout default on account addition * Pull latest jslib * Retrieve vaultTimeout from stateService * Record activity per Account * Add userId to logout and add fallback if not present * Register AccountFactory * Pass userId in messages * Base changes for account switching di fixes (#2280) * [bug] Null checks on Account init * [bug] Use same stateService instance for all operations We override the stateService in browser, but currently don't pull the background service into popup and allow jslib to create its own instance of the base StateService for jslib services. This causes a split in in memory state between the three isntances that results in many errors, namely locking not working. * [chore] Update jslib * Pull in jslib * Pull in jslib * Pull in latest jslib to multiple stateservice inits * Check vault states before executing processReload * Adjust iterator * Update native messaging to include the userId (#2290) * Re-Add UserVerificationService * Fix email not being remembered by base component * Improve readability of reloadProcess * Removed unneeded null check * Fix constructor dependency (stateService) * Added missing await * Simplify dependency registration * Fixed typos * Reverted back to simple loop * Use vaultTimeoutService to retrieve Timeout Co-authored-by: Addison Beck <abeck@bitwarden.com> Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
2022-01-27 22:22:51 +01:00
private stateService: StateService,
[Feature] End User Vault Refresh (#2545) * Initial org filter work * update jslib * Move filter to below cipher length check * don't show vault filter in personal or org folder * Use family icon for families org * jslib and auth guard updates * lint fixes * rename GroupingsComponent to VaultFilterComponent * fix no folder showing all items * Add checks for PersonalOwnership policy * update css class names * lint fixes * cleanup * Some final cleanup * import order lint fix * remove unused import * Use smaller icon for chevron * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * fix lint error * remove extra localizations * rename orgFilter -> vaultSelect * Rename orgFilterService to VaultSelectService * lint fixes * combine vault select service with vault filter service * Use base vault filter service methods * Use VaultFilter model and other small fixes * lint fixes * Final restructuring pass * Update jslib and remove extra function * Remove extra imports * remove space * Remove vaultFilterService from background services * Update jslib to latest on feature branch * merge fix * update jslib * [feat] Implement EUVR for desktop Should contain only https://github.com/bitwarden/desktop/pull/1487, with merge resolutions and style fixes * [fix] Delete unused GroupingsComponentTemplate * [dep] Update jslib Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-05-09 14:19:18 +02:00
private passwordRepromptService: PasswordRepromptService,
private organizationService: OrganizationService,
private vaultFilterService: VaultFilterService
2021-12-21 15:43:35 +01:00
) {}
2019-08-19 15:31:05 +02:00
async ngOnInit() {
this.searchTypeSearch = !this.platformUtilsService.isSafari();
this.inSidebar = this.popupUtilsService.inSidebar(window);
2021-12-21 15:43:35 +01:00
2019-08-19 15:31:05 +02:00
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case "syncCompleted":
if (this.loaded) {
window.setTimeout(() => {
this.load();
2021-12-21 15:43:35 +01:00
}, 500);
2019-08-19 15:31:05 +02:00
}
2021-12-21 15:43:35 +01:00
break;
2019-08-19 15:31:05 +02:00
case "collectPageDetailsResponse":
if (message.sender === BroadcasterSubscriptionId) {
this.pageDetails.push({
frameId: message.webExtSender.frameId,
tab: message.tab,
details: message.details,
});
2021-12-21 15:43:35 +01:00
}
break;
default:
break;
2019-08-19 15:31:05 +02:00
}
this.changeDetectorRef.detectChanges();
});
2018-04-06 20:03:35 +02:00
});
2018-04-06 21:33:20 +02:00
if (!this.syncService.syncInProgress) {
2018-04-11 04:49:19 +02:00
await this.load();
2021-12-21 15:43:35 +01:00
} else {
2018-04-11 04:49:19 +02:00
this.loadedTimeout = window.setTimeout(async () => {
2018-04-06 21:33:20 +02:00
if (!this.loaded) {
await this.load();
2021-12-21 15:43:35 +01:00
}
}, 5000);
2018-04-06 21:33:20 +02:00
}
2019-08-19 15:31:05 +02:00
window.setTimeout(() => {
document.getElementById("search").focus();
2021-12-21 15:43:35 +01:00
}, 100);
}
2018-04-06 21:33:20 +02:00
ngOnDestroy() {
2018-04-23 17:30:12 +02:00
window.clearTimeout(this.loadedTimeout);
2018-04-06 21:33:20 +02:00
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
2021-12-21 15:43:35 +01:00
}
2018-04-06 20:03:35 +02:00
async refresh() {
await this.load();
2021-12-21 15:43:35 +01:00
}
2018-04-06 20:03:35 +02:00
addCipher() {
this.router.navigate(["/add-cipher"], {
queryParams: {
name: this.hostname,
uri: this.url,
selectedVault: this.vaultFilterService.getVaultFilter().selectedOrganizationId,
},
});
2021-12-21 15:43:35 +01:00
}
2018-04-06 20:03:35 +02:00
viewCipher(cipher: CipherView) {
this.router.navigate(["/view-cipher"], { queryParams: { cipherId: cipher.id } });
2021-12-21 15:43:35 +01:00
}
2019-08-19 15:38:59 +02:00
async fillCipher(cipher: CipherView) {
2021-12-21 15:43:35 +01:00
if (
cipher.reprompt !== CipherRepromptType.None &&
!(await this.passwordRepromptService.showPasswordPrompt())
2021-12-21 15:43:35 +01:00
) {
return;
2018-04-06 20:03:35 +02:00
}
this.totpCode = null;
if (this.totpTimeout != null) {
window.clearTimeout(this.totpTimeout);
2018-04-06 20:03:35 +02:00
}
2018-04-05 04:59:42 +02:00
2018-04-06 20:03:35 +02:00
if (this.pageDetails == null || this.pageDetails.length === 0) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("autofillError"));
2021-12-21 15:43:35 +01:00
return;
2018-04-05 04:59:42 +02:00
}
2018-04-06 20:03:35 +02:00
try {
2021-12-07 20:42:18 +01:00
this.totpCode = await this.autofillService.doAutoFill({
cipher: cipher,
2019-08-19 15:38:59 +02:00
pageDetails: this.pageDetails,
doc: window.document,
fillNewPassword: true,
2019-08-19 15:38:59 +02:00
});
2019-08-19 18:05:13 +02:00
if (this.totpCode != null) {
2019-08-19 15:38:59 +02:00
this.platformUtilsService.copyToClipboard(this.totpCode, { window: window });
2018-04-06 20:03:35 +02:00
}
if (this.popupUtilsService.inPopup(window)) {
if (this.platformUtilsService.isFirefox() || this.platformUtilsService.isSafari()) {
BrowserApi.closePopup(window);
} else {
// Slight delay to fix bug in Chromium browsers where popup closes without copying totp to clipboard
setTimeout(() => BrowserApi.closePopup(window), 50);
2019-08-19 15:38:59 +02:00
}
2021-12-21 15:43:35 +01:00
}
2019-08-19 15:38:59 +02:00
} catch {
2018-04-23 17:50:14 +02:00
this.ngZone.run(() => {
2021-12-07 20:42:18 +01:00
this.platformUtilsService.showToast("error", null, this.i18nService.t("autofillError"));
2018-04-23 17:50:14 +02:00
this.changeDetectorRef.detectChanges();
2021-12-21 15:43:35 +01:00
});
2018-04-06 20:03:35 +02:00
}
2021-12-21 15:43:35 +01:00
}
2018-04-06 20:03:35 +02:00
searchVault() {
2018-08-13 17:53:16 +02:00
if (this.searchTimeout != null) {
clearTimeout(this.searchTimeout);
2018-04-06 20:03:35 +02:00
}
2018-08-13 17:53:16 +02:00
if (!this.searchService.isSearchable(this.searchText)) {
2021-12-21 15:43:35 +01:00
return;
}
2018-08-13 17:53:16 +02:00
this.searchTimeout = window.setTimeout(async () => {
this.router.navigate(["/tabs/vault"], { queryParams: { searchText: this.searchText } });
2021-12-21 15:43:35 +01:00
}, 200);
}
closeOnEsc(e: KeyboardEvent) {
// If input not empty, use browser default behavior of clearing input instead
if (e.key === "Escape" && (this.searchText == null || this.searchText === "")) {
BrowserApi.closePopup(window);
2021-12-21 15:43:35 +01:00
}
}
2018-04-06 20:03:35 +02:00
private async load() {
[Feature] End User Vault Refresh (#2545) * Initial org filter work * update jslib * Move filter to below cipher length check * don't show vault filter in personal or org folder * Use family icon for families org * jslib and auth guard updates * lint fixes * rename GroupingsComponent to VaultFilterComponent * fix no folder showing all items * Add checks for PersonalOwnership policy * update css class names * lint fixes * cleanup * Some final cleanup * import order lint fix * remove unused import * Use smaller icon for chevron * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * fix lint error * remove extra localizations * rename orgFilter -> vaultSelect * Rename orgFilterService to VaultSelectService * lint fixes * combine vault select service with vault filter service * Use base vault filter service methods * Use VaultFilter model and other small fixes * lint fixes * Final restructuring pass * Update jslib and remove extra function * Remove extra imports * remove space * Remove vaultFilterService from background services * Update jslib to latest on feature branch * merge fix * update jslib * [feat] Implement EUVR for desktop Should contain only https://github.com/bitwarden/desktop/pull/1487, with merge resolutions and style fixes * [fix] Delete unused GroupingsComponentTemplate * [dep] Update jslib Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-05-09 14:19:18 +02:00
this.loaded = false;
2018-04-06 20:03:35 +02:00
const tab = await BrowserApi.getTabFromCurrentWindow();
2018-04-14 06:02:55 +02:00
if (tab != null) {
2018-04-06 20:03:35 +02:00
this.url = tab.url;
} else {
2018-04-14 06:02:55 +02:00
this.loginCiphers = [];
2018-04-06 20:03:35 +02:00
this.loaded = true;
2021-12-21 15:43:35 +01:00
return;
}
2018-04-23 19:04:11 +02:00
this.hostname = Utils.getHostname(this.url);
2018-09-27 16:52:01 +02:00
this.pageDetails = [];
2018-04-06 20:03:35 +02:00
BrowserApi.tabSendMessage(tab, {
command: "collectPageDetails",
tab: tab,
2018-04-06 21:33:20 +02:00
sender: BroadcasterSubscriptionId,
2018-04-06 20:03:35 +02:00
});
2021-12-21 15:43:35 +01:00
const otherTypes: CipherType[] = [];
[Account Switching] Base changes for account switching (#2250) * Pull in jslib * Create new state models * Create browser specific stateService * Remove registration deprecated services, register stateService * Replace usage of deprecated services (user, constants) * Add missing properties to BrowserGroupingsComponentState * Remove StorageService from initFactory * Clear the correct state * Add null check when restoring send-grouping state * add remember email * Initialize stateservice in services.module * Fix 'lock now' not working * Comment to remove setting defaults on install * Pull jslib * Remove setting defaults on install * Bump jslib * Pass the current userId to services when logging out * Bump jslib * Override vaultTimeout default on account addition * Pull latest jslib * Retrieve vaultTimeout from stateService * Record activity per Account * Add userId to logout and add fallback if not present * Register AccountFactory * Pass userId in messages * Base changes for account switching di fixes (#2280) * [bug] Null checks on Account init * [bug] Use same stateService instance for all operations We override the stateService in browser, but currently don't pull the background service into popup and allow jslib to create its own instance of the base StateService for jslib services. This causes a split in in memory state between the three isntances that results in many errors, namely locking not working. * [chore] Update jslib * Pull in jslib * Pull in jslib * Pull in latest jslib to multiple stateservice inits * Check vault states before executing processReload * Adjust iterator * Update native messaging to include the userId (#2290) * Re-Add UserVerificationService * Fix email not being remembered by base component * Improve readability of reloadProcess * Removed unneeded null check * Fix constructor dependency (stateService) * Added missing await * Simplify dependency registration * Fixed typos * Reverted back to simple loop * Use vaultTimeoutService to retrieve Timeout Co-authored-by: Addison Beck <abeck@bitwarden.com> Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
2022-01-27 22:22:51 +01:00
const dontShowCards = await this.stateService.getDontShowCardsCurrentTab();
const dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab();
[Feature] End User Vault Refresh (#2545) * Initial org filter work * update jslib * Move filter to below cipher length check * don't show vault filter in personal or org folder * Use family icon for families org * jslib and auth guard updates * lint fixes * rename GroupingsComponent to VaultFilterComponent * fix no folder showing all items * Add checks for PersonalOwnership policy * update css class names * lint fixes * cleanup * Some final cleanup * import order lint fix * remove unused import * Use smaller icon for chevron * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * fix lint error * remove extra localizations * rename orgFilter -> vaultSelect * Rename orgFilterService to VaultSelectService * lint fixes * combine vault select service with vault filter service * Use base vault filter service methods * Use VaultFilter model and other small fixes * lint fixes * Final restructuring pass * Update jslib and remove extra function * Remove extra imports * remove space * Remove vaultFilterService from background services * Update jslib to latest on feature branch * merge fix * update jslib * [feat] Implement EUVR for desktop Should contain only https://github.com/bitwarden/desktop/pull/1487, with merge resolutions and style fixes * [fix] Delete unused GroupingsComponentTemplate * [dep] Update jslib Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-05-09 14:19:18 +02:00
this.showOrganizations = await this.organizationService.hasOrganizations();
2018-04-06 20:03:35 +02:00
if (!dontShowCards) {
otherTypes.push(CipherType.Card);
}
if (!dontShowIdentities) {
otherTypes.push(CipherType.Identity);
2021-12-21 15:43:35 +01:00
}
const ciphers = await this.cipherService.getAllDecryptedForUrl(
2021-12-21 15:43:35 +01:00
this.url,
otherTypes.length > 0 ? otherTypes : null
2021-12-21 15:43:35 +01:00
);
2018-04-06 20:18:28 +02:00
this.loginCiphers = [];
this.cardCiphers = [];
2018-04-06 20:03:35 +02:00
this.identityCiphers = [];
2021-12-21 15:43:35 +01:00
2021-03-02 19:31:52 +01:00
ciphers.forEach((c) => {
[Feature] End User Vault Refresh (#2545) * Initial org filter work * update jslib * Move filter to below cipher length check * don't show vault filter in personal or org folder * Use family icon for families org * jslib and auth guard updates * lint fixes * rename GroupingsComponent to VaultFilterComponent * fix no folder showing all items * Add checks for PersonalOwnership policy * update css class names * lint fixes * cleanup * Some final cleanup * import order lint fix * remove unused import * Use smaller icon for chevron * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Update src/popup/vault/organization-filter.component.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * fix lint error * remove extra localizations * rename orgFilter -> vaultSelect * Rename orgFilterService to VaultSelectService * lint fixes * combine vault select service with vault filter service * Use base vault filter service methods * Use VaultFilter model and other small fixes * lint fixes * Final restructuring pass * Update jslib and remove extra function * Remove extra imports * remove space * Remove vaultFilterService from background services * Update jslib to latest on feature branch * merge fix * update jslib * [feat] Implement EUVR for desktop Should contain only https://github.com/bitwarden/desktop/pull/1487, with merge resolutions and style fixes * [fix] Delete unused GroupingsComponentTemplate * [dep] Update jslib Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-05-09 14:19:18 +02:00
if (!this.vaultFilterService.filterCipherForSelectedVault(c)) {
switch (c.type) {
case CipherType.Login:
this.loginCiphers.push(c);
break;
case CipherType.Card:
this.cardCiphers.push(c);
break;
case CipherType.Identity:
this.identityCiphers.push(c);
break;
default:
break;
}
2021-12-21 15:43:35 +01:00
}
});
2018-04-14 03:23:11 +02:00
this.loginCiphers = this.loginCiphers.sort((a, b) =>
this.cipherService.sortCiphersByLastUsedThenName(a, b)
2021-12-21 15:43:35 +01:00
);
2018-04-06 20:03:35 +02:00
this.loaded = true;
2021-12-21 15:43:35 +01:00
}
2018-04-05 04:59:42 +02:00
}