Merge branch 'dev'

This commit is contained in:
jonaswinkler 2021-02-25 17:53:17 +01:00
commit 170ddbc76f
49 changed files with 3642 additions and 226 deletions

View File

@ -20,7 +20,7 @@ jobs:
name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.7
-
name: Get pip cache dir
id: pip-cache
@ -49,6 +49,39 @@ jobs:
name: documentation
path: docs/_build/html/
codestyle:
runs-on: ubuntu-20.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.7
-
name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
-
name: Persistent Github pip cache
uses: actions/cache@v2
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip${{ matrix.python-version }}
-
name: Install dependencies
run: |
pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile
-
name: Codestyle
run: |
cd src/
pycodestyle
tests:
runs-on: ubuntu-20.04
strategy:
@ -76,7 +109,7 @@ jobs:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip${{ matrix.python-version }}
-
name: Prepare tests
name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng
@ -87,11 +120,6 @@ jobs:
run: |
cd src/
pytest
-
name: Codestyle
run: |
cd src/
pycodestyle
-
name: Publish coverage results
if: matrix.python-version == '3.8'
@ -130,7 +158,7 @@ jobs:
path: src/documents/static/frontend/
build-release:
needs: [frontend, documentation, tests]
needs: [frontend, documentation, tests, codestyle]
runs-on: ubuntu-20.04
steps:
-
@ -240,7 +268,7 @@ jobs:
build-docker-image:
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
runs-on: ubuntu-latest
needs: [frontend, tests]
needs: [frontend, tests, codestyle]
steps:
-
name: Prepare

View File

@ -77,9 +77,9 @@ The documentation for Paperless-ng is available on [ReadTheDocs](https://paperle
# Translation
Paperless is currently available in English, German, Dutch and French. Translation is coordinated at transifex: https://www.transifex.com/paperless/paperless-ng
Paperless is currently available in English, German, Dutch, French, and Portuguese.
If you want to see paperless in your own language, request that language at transifex and you can start translating after I approve the language.
There's an active translation project at transifex! If you want to help out by translating paperless into your language, please head over to https://github.com/jonaswinkler/paperless-ng/issues/212 for details.
# Feature Requests

View File

@ -5,6 +5,19 @@
Changelog
*********
paperless-ng 1.2.1
##################
* `Rodrigo Avelino <https://github.com/rodavelino>`_ translated Paperless into Portuguese (Brazil)!
* The date input fields now respect the currently selected date format.
* Added a fancy icon when adding paperless to the home screen on iOS devices. Thanks to `Joel Nordell <https://github.com/joelnordell>`_.
* When using regular expression matching, the regular expression is now validated before saving the tag/correspondent/type.
* Regression fix: Dates on the front end did not respect date locale settings in some cases.
paperless-ng 1.2.0
##################

View File

@ -19,7 +19,8 @@
"de": "src/locale/messages.de.xlf",
"nl-NL": "src/locale/messages.nl_NL.xlf",
"fr": "src/locale/messages.fr.xlf",
"en-GB": "src/locale/messages.en_GB.xlf"
"en-GB": "src/locale/messages.en_GB.xlf",
"pt-BR": "src/locale/messages.pt_BR.xlf"
}
},
"architect": {
@ -36,6 +37,7 @@
"aot": true,
"assets": [
"src/favicon.ico",
"src/apple-touch-icon.png",
"src/assets",
"src/manifest.webmanifest", {
"glob": "pdf.worker.min.js",
@ -112,6 +114,7 @@
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/apple-touch-icon.png",
"src/assets",
"src/manifest.webmanifest"
],

View File

@ -531,28 +531,21 @@
<source>Use system language</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit id="7729897675462249787" datatype="html">
<source>Use date format of display language</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">96</context>
</context-group>
</trans-unit>
<trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">97</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit id="8488620293789898901" datatype="html">
<source>Error while storing settings on server: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">114</context>
<context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
@ -1605,6 +1598,13 @@
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="d6529debfc1613db22d6fa096ebfeb8a85fa739d" datatype="html">
<source>Invalid date.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="2807800733729323332" datatype="html">
<source>Yes</source>
<context-group purpose="location">
@ -1630,35 +1630,49 @@
<source>English (US)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context>
<context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit id="6987083569809053351" datatype="html">
<source>English (GB)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context>
<context context-type="linenumber">89</context>
</context-group>
</trans-unit>
<trans-unit id="1858110241312746425" datatype="html">
<source>German</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context>
<context context-type="linenumber">90</context>
</context-group>
</trans-unit>
<trans-unit id="3071065188816255493" datatype="html">
<source>Dutch</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">91</context>
</context-group>
</trans-unit>
<trans-unit id="7633754075223722162" datatype="html">
<source>French</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit id="9184513005098760425" datatype="html">
<source>Portuguese (Brazil)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit id="2119857572761283468" datatype="html">

View File

@ -2055,9 +2055,9 @@
}
},
"@ng-bootstrap/ng-bootstrap": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.0.tgz",
"integrity": "sha512-v77Gfd8xHH+exq0WqIqVRlxbUEHdA/2+RUJenUP2IDTQN9E1rWl7O461/kosr+0XPuxPArHQJxhh/WsCYckcNg==",
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.4.tgz",
"integrity": "sha512-EdxTwOPOtlvfnwrglPniulmzdnXdXH3lTGaGAY1HrYRvdtGg6wicRvl+BvwVE/3Qik5NPkOWMVghUHpv3evIYg==",
"requires": {
"tslib": "^2.0.0"
}

View File

@ -20,7 +20,7 @@
"@angular/platform-browser": "~10.1.5",
"@angular/platform-browser-dynamic": "~10.1.5",
"@angular/router": "~10.1.5",
"@ng-bootstrap/ng-bootstrap": "^8.0.0",
"@ng-bootstrap/ng-bootstrap": "^8.0.4",
"@ng-select/ng-select": "^5.0.9",
"bootstrap": "^4.5.0",
"file-saver": "^2.0.5",

View File

@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { DocumentListComponent } from './components/document-list/document-list.component';
import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
@ -39,7 +39,6 @@ import { SelectComponent } from './components/common/input/select/select.compone
import { CheckComponent } from './components/common/input/check/check.component';
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { DateTimeComponent } from './components/common/input/date-time/date-time.component';
import { TagsComponent } from './components/common/input/tags/tags.component';
import { SortableDirective } from './directives/sortable.directive';
import { CookieService } from 'ngx-cookie-service';
@ -60,15 +59,21 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { NumberComponent } from './components/common/input/number/number.component';
import { SafePipe } from './pipes/safe.pipe';
import { CustomDatePipe } from './pipes/custom-date.pipe';
import { DateComponent } from './components/common/input/date/date.component';
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter';
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter';
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor';
import localeFr from '@angular/common/locales/fr';
import localeNl from '@angular/common/locales/nl';
import localeDe from '@angular/common/locales/de';
import localePt from '@angular/common/locales/pt';
import localeEnGb from '@angular/common/locales/en-GB';
registerLocaleData(localeFr)
registerLocaleData(localeNl)
registerLocaleData(localeDe)
registerLocaleData(localePt, "pt-BR")
registerLocaleData(localeEnGb)
@NgModule({
@ -104,7 +109,6 @@ registerLocaleData(localeEnGb)
SelectComponent,
CheckComponent,
SaveViewConfigDialogComponent,
DateTimeComponent,
TagsComponent,
SortableDirective,
SavedViewWidgetComponent,
@ -120,7 +124,8 @@ registerLocaleData(localeEnGb)
SelectDialogComponent,
NumberComponent,
SafePipe,
CustomDatePipe
CustomDatePipe,
DateComponent
],
imports: [
BrowserModule,
@ -140,9 +145,15 @@ registerLocaleData(localeEnGb)
provide: HTTP_INTERCEPTORS,
useClass: CsrfInterceptor,
multi: true
},{
provide: HTTP_INTERCEPTORS,
useClass: ApiVersionInterceptor,
multi: true
},
FilterPipe,
DocumentTitlePipe
DocumentTitlePipe,
{provide: NgbDateAdapter, useClass: ISODateTimeAdapter},
{provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter}
],
bootstrap: [AppComponent]
})

View File

@ -94,7 +94,7 @@
</svg>&nbsp;{{d.title | documentTitle}}
</a>
</li>
<li class="nav-item w-100" *ngIf="openDocuments.length > 1">
<li class="nav-item w-100" *ngIf="openDocuments.length >= 1">
<a class="nav-link text-truncate" [routerLink]="" (click)="closeAll()">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#x"/>

View File

@ -20,8 +20,17 @@
</div>
<div class="input-group input-group-sm">
<input type="date" class="form-control" id="date_after" [(ngModel)]="dateAfter" (change)="onChangeDebounce()">
<input class="form-control" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()"
[(ngModel)]="dateAfter" ngbDatepicker #dateAfterPicker="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="dateAfterPicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</button>
</div>
</div>
</div>
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
@ -36,8 +45,17 @@
</div>
<div class="input-group input-group-sm">
<input type="date" class="form-control" id="date_before" [(ngModel)]="dateBefore" (change)="onChangeDebounce()">
<input class="form-control" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()"
[(ngModel)]="dateBefore" ngbDatepicker #dateBeforePicker="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="dateBeforePicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,7 +1,10 @@
import { formatDate } from '@angular/common';
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core';
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { debounceTime } from 'rxjs/operators';
import { SettingsService } from 'src/app/services/settings.service';
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter';
export interface DateSelection {
before?: string
@ -16,10 +19,17 @@ const LAST_YEAR = 3
@Component({
selector: 'app-date-dropdown',
templateUrl: './date-dropdown.component.html',
styleUrls: ['./date-dropdown.component.scss']
styleUrls: ['./date-dropdown.component.scss'],
providers: [
{provide: NgbDateAdapter, useClass: ISODateAdapter},
]
})
export class DateDropdownComponent implements OnInit, OnDestroy {
constructor(settings: SettingsService) {
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
}
quickFilters = [
{id: LAST_7_DAYS, name: $localize`Last 7 days`},
{id: LAST_MONTH, name: $localize`Last month`},
@ -27,6 +37,8 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
{id: LAST_YEAR, name: $localize`Last year`}
]
datePlaceHolder: string
@Input()
dateBefore: string

View File

@ -1,13 +0,0 @@
<div class="form-row">
<div class="form-group col">
<label for="created_date">{{titleDate}}</label>
<input type="date" class="form-control" id="created_date" [(ngModel)]="dateValue" (change)="dateOrTimeChanged()">
</div>
<div class="form-group col" *ngIf="titleTime">
<label for="created_time">{{titleTime}}</label>
<input type="time" class="form-control" id="created_time" [(ngModel)]="timeValue" (change)="dateOrTimeChanged()">
</div>
</div>
<!-- <small *ngIf="hint" class="form-text text-muted">{{hint}}</small> -->

View File

@ -1,61 +0,0 @@
import { formatDate } from '@angular/common';
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateTimeComponent),
multi: true
}],
selector: 'app-input-date-time',
templateUrl: './date-time.component.html',
styleUrls: ['./date-time.component.scss']
})
export class DateTimeComponent implements OnInit,ControlValueAccessor {
constructor() {
}
onChange = (newValue: any) => {};
onTouched = () => {};
writeValue(newValue: any): void {
this.dateValue = formatDate(newValue, 'yyyy-MM-dd', "en-US")
this.timeValue = formatDate(newValue, 'HH:mm:ss', 'en-US')
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
@Input()
titleDate: string = "Date"
@Input()
titleTime: string
@Input()
disabled: boolean = false
@Input()
hint: string
timeValue
dateValue
ngOnInit(): void {
}
dateOrTimeChanged() {
this.onChange(formatDate(this.dateValue + "T" + this.timeValue,"yyyy-MM-ddTHH:mm:ssZZZZZ", "en-us", "UTC"))
}
}

View File

@ -0,0 +1,16 @@
<div class="form-group">
<label [for]="inputId">{{title}}</label>
<div class="input-group" [class.is-invalid]="error">
<input class="form-control" [class.is-invalid]="error" [placeholder]="placeholder" [id]="inputId" (dateSelect)="onChange(value)" (change)="onChange(value)"
name="dp" [(ngModel)]="value" ngbDatepicker #datePicker="ngbDatepicker" #datePickerContent="ngModel">
<div class="input-group-append">
<button class="btn btn-outline-secondary calendar" (click)="datePicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</button>
</div>
</div>
<div class="invalid-feedback" i18n>Invalid date.</div>
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
</div>

View File

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DateTimeComponent } from './date-time.component';
import { DateComponent } from './date.component';
describe('DateTimeComponent', () => {
let component: DateTimeComponent;
let fixture: ComponentFixture<DateTimeComponent>;
describe('DateComponent', () => {
let component: DateComponent;
let fixture: ComponentFixture<DateComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DateTimeComponent ]
declarations: [ DateComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DateTimeComponent);
fixture = TestBed.createComponent(DateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,32 @@
import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NgbDateAdapter, NgbDateParserFormatter, NgbDatepickerContent } from '@ng-bootstrap/ng-bootstrap';
import { SettingsService } from 'src/app/services/settings.service';
import { v4 as uuidv4 } from 'uuid';
import { AbstractInputComponent } from '../abstract-input';
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateComponent),
multi: true
}],
selector: 'app-input-date',
templateUrl: './date.component.html',
styleUrls: ['./date.component.scss']
})
export class DateComponent extends AbstractInputComponent<string> implements OnInit {
constructor(private settings: SettingsService) {
super()
}
ngOnInit(): void {
super.ngOnInit()
this.placeholder = this.settings.getLocalizedDateInputFormat()
}
placeholder: string
}

View File

@ -58,7 +58,7 @@
<app-input-text #inputTitle i18n-title title="Title" formControlName="title" [error]="error?.title"></app-input-text>
<app-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" formControlName='archive_serial_number'></app-input-number>
<app-input-date-time i18n-titleDate titleDate="Date created" formControlName="created"></app-input-date-time>
<app-input-date i18n-title title="Date created" formControlName="created" [error]="error?.created"></app-input-date>
<app-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true"
(createNew)="createCorrespondent()" [suggestions]="suggestions?.correspondents"></app-input-select>
<app-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true"

View File

@ -8,7 +8,7 @@
<div class="modal-body">
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></app-input-check>
</div>
<div class="modal-footer">

View File

@ -9,7 +9,7 @@
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
</div>

View File

@ -88,14 +88,15 @@ export class SettingsComponent implements OnInit {
}
get displayLanguageOptions(): LanguageOption[] {
return [{code: "", name: $localize`Use system language`}].concat(this.settings.getLanguageOptions())
return [
{code: "", name: $localize`Use system language`}
].concat(this.settings.getLanguageOptions())
}
get dateLocaleOptions(): LanguageOption[] {
return [
{code: "", name: $localize`Use date format of display language`},
{code: "iso-8601", name: $localize`ISO 8601`}
].concat(this.settings.getLanguageOptions())
{code: "", name: $localize`Use date format of display language`}
].concat(this.settings.getDateLocaleOptions())
}
get today() {

View File

@ -20,7 +20,7 @@
<app-input-check i18n-title title="Inbox tag" formControlName="is_inbox_tag" i18n-hint hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
</div>
<div class="modal-footer">

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ApiVersionInterceptor } from './api-version.interceptor';
describe('ApiVersionInterceptor', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [
ApiVersionInterceptor
]
}));
it('should be created', () => {
const interceptor: ApiVersionInterceptor = TestBed.inject(ApiVersionInterceptor);
expect(interceptor).toBeTruthy();
});
});

View File

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
@Injectable()
export class ApiVersionInterceptor implements HttpInterceptor {
constructor() {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
request = request.clone({
setHeaders: {
'Accept': `application/json; version=${environment.apiVersion}`
}
})
return next.handle(request);
}
}

View File

@ -13,17 +13,20 @@ const FORMAT_TO_ISO_FORMAT = {
})
export class CustomDatePipe extends DatePipe implements PipeTransform {
private defaultLocale: string
constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) {
super(locale)
this.defaultLocale = locale
}
transform(value: any, format?: string, timezone?: string, locale?: string): string | null {
let l = locale || this.settings.get(SETTINGS_KEYS.DATE_LOCALE)
let l = locale || this.settings.get(SETTINGS_KEYS.DATE_LOCALE) || this.defaultLocale
let f = format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT)
if (l == "iso-8601") {
return super.transform(value, FORMAT_TO_ISO_FORMAT[f], timezone)
} else {
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale)
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, l)
}
}

View File

@ -1,5 +1,5 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
import { Inject, Injectable, LOCALE_ID, Renderer2, RendererFactory2 } from '@angular/core';
import { Meta } from '@angular/platform-browser';
import { CookieService } from 'ngx-cookie-service';
@ -10,9 +10,14 @@ export interface PaperlessSettings {
}
export interface LanguageOption {
code: string,
name: string,
code: string
name: string
englishName?: string
/**
* A date format string for use by the date selectors. MUST contain 'yyyy', 'mm' and 'dd'.
*/
dateInputFormat?: string
}
export const SETTINGS_KEYS = {
@ -56,7 +61,8 @@ export class SettingsService {
private rendererFactory: RendererFactory2,
@Inject(DOCUMENT) private document,
private cookieService: CookieService,
private meta: Meta
private meta: Meta,
@Inject(LOCALE_ID) private localeId: string
) {
this.renderer = rendererFactory.createRenderer(null, null);
@ -79,14 +85,20 @@ export class SettingsService {
getLanguageOptions(): LanguageOption[] {
return [
{code: "en-us", name: $localize`English (US)`, englishName: "English (US)"},
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)"},
{code: "de", name: $localize`German`, englishName: "German"},
{code: "nl", name: $localize`Dutch`, englishName: "Dutch"},
{code: "fr", name: $localize`French`, englishName: "French"}
{code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
{code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
{code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
{code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
{code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
]
}
getDateLocaleOptions(): LanguageOption[] {
let isoOption: LanguageOption = {code: "iso-8601", name: $localize`ISO 8601`, dateInputFormat: "yyyy-mm-dd"}
return [isoOption].concat(this.getLanguageOptions())
}
private getLanguageCookieName() {
let prefix = ""
if (this.meta.getTag('name=cookie_prefix')) {
@ -107,6 +119,11 @@ export class SettingsService {
}
}
getLocalizedDateInputFormat(): string {
let dateLocale = this.get(SETTINGS_KEYS.DATE_LOCALE) || this.getLanguage() || this.localeId.toLowerCase()
return this.getDateLocaleOptions().find(o => o.code == dateLocale)?.dateInputFormat || "yyyy-mm-dd"
}
get(key: string): any {
let setting = SETTINGS.find(s => s.key == key)

View File

@ -0,0 +1,59 @@
import { Injectable } from "@angular/core"
import { NgbDateParserFormatter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap"
import { SettingsService } from "../services/settings.service"
@Injectable()
export class LocalizedDateParserFormatter extends NgbDateParserFormatter {
constructor(private settings: SettingsService) {
super()
}
private getDateInputFormat() {
return this.settings.getLocalizedDateInputFormat()
}
/**
* This constructs a regular expression from a date input format which is then
* used to parse dates.
*/
private getDateParseRegex() {
return new RegExp(
"^" + this.getDateInputFormat()
.replace('dd', '(?<day>[0-9]+)')
.replace('mm', '(?<month>[0-9]+)')
.replace('yyyy', '(?<year>[0-9]+)')
.split('.').join('\\.\\s*') + "$" // allow whitespace(s) after dot (specific for German)
)
}
parse(value: string): NgbDateStruct | null {
let match = this.getDateParseRegex().exec(value)
if (match) {
let dateStruct = {
day: +match.groups.day,
month: +match.groups.month,
year: +match.groups.year
}
if (dateStruct.year <= (new Date().getFullYear() - 2000)) {
dateStruct.year += 2000
} else if (dateStruct.year < 100) {
dateStruct.year += 1900
}
return dateStruct
} else {
return null
}
}
format(date: NgbDateStruct | null): string {
if (date) {
return this.getDateInputFormat()
.replace('dd', date.day.toString().padStart(2, '0'))
.replace('mm', date.month.toString().padStart(2, '0'))
.replace('yyyy', date.year.toString().padStart(4, '0'))
} else {
return null
}
}
}

View File

@ -0,0 +1,27 @@
import { Injectable } from "@angular/core";
import { NgbDateAdapter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap";
@Injectable()
export class ISODateAdapter extends NgbDateAdapter<string> {
fromModel(value: string | null): NgbDateStruct | null {
if (value) {
let date = new Date(value)
return {
day : date.getDate(),
month : date.getMonth() + 1,
year : date.getFullYear()
}
} else {
return null
}
}
toModel(date: NgbDateStruct | null): string | null {
if (date) {
return date.year.toString().padStart(4, '0') + "-" + date.month.toString().padStart(2, '0') + "-" + date.day.toString().padStart(2, '0')
} else {
return null
}
}
}

View File

@ -0,0 +1,23 @@
import { Injectable } from "@angular/core";
import { NgbDateAdapter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap";
@Injectable()
export class ISODateTimeAdapter extends NgbDateAdapter<string> {
fromModel(value: string | null): NgbDateStruct | null {
if (value) {
let date = new Date(value)
return {
day : date.getDate(),
month : date.getMonth() + 1,
year : date.getFullYear()
}
} else {
return null
}
}
toModel(date: NgbDateStruct | null): string | null {
return date ? new Date(date.year, date.month - 1, date.day).toISOString() : null
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -1,8 +1,9 @@
export const environment = {
production: true,
apiBaseUrl: "/api/",
apiVersion: "1",
appTitle: "Paperless-ng",
version: "1.2.0",
version: "1.2.1",
webSocketHost: window.location.host,
webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:")
};

View File

@ -5,6 +5,7 @@
export const environment = {
production: false,
apiBaseUrl: "http://localhost:8000/api/",
apiVersion: "1",
appTitle: "Paperless-ng",
version: "DEVELOPMENT",
webSocketHost: "localhost:8000",

View File

@ -9,6 +9,7 @@
<meta name="theme-color" content="#17541f" />
<link rel="manifest" href="manifest.webmanifest">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
</head>
<body class="color-scheme-system">
<app-root></app-root>

View File

@ -606,7 +606,7 @@
<target>Benutze Systemsprache</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7729897675462249787">
@ -614,15 +614,7 @@
<target>Benutze Datumsformat der Anzeigesprache</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">96</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">97</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8488620293789898901">
@ -630,7 +622,7 @@
<target>Fehler beim Speichern der Einstellungen auf dem Server: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">114</context>
<context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@ -1832,6 +1824,14 @@
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="d6529debfc1613db22d6fa096ebfeb8a85fa739d">
<source>Invalid date.</source>
<target>Ungültiges Datum.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2807800733729323332">
<source>Yes</source>
<target>Ja</target>
@ -1861,7 +1861,7 @@
<target>Englisch (US)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context>
<context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6987083569809053351">
@ -1869,7 +1869,7 @@
<target>Englisch (UK)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context>
<context context-type="linenumber">89</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="1858110241312746425">
@ -1877,7 +1877,7 @@
<target>Deutsch</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context>
<context context-type="linenumber">90</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3071065188816255493">
@ -1885,7 +1885,7 @@
<target>Niederländisch</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">91</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7633754075223722162">
@ -1893,7 +1893,23 @@
<target>Französisch</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9184513005098760425">
<source>Portuguese (Brazil)</source>
<target>Portugiesisch (Brasilien)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2119857572761283468">

View File

@ -606,7 +606,7 @@
<target>Use system language</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7729897675462249787">
@ -614,15 +614,7 @@
<target>Use date format of display language</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">96</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">97</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8488620293789898901">
@ -630,7 +622,7 @@
<target>Error while storing settings on server: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">114</context>
<context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@ -1832,6 +1824,14 @@
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="d6529debfc1613db22d6fa096ebfeb8a85fa739d">
<source>Invalid date.</source>
<target>Invalid date.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2807800733729323332">
<source>Yes</source>
<target>Yes</target>
@ -1861,7 +1861,7 @@
<target>English (US)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context>
<context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6987083569809053351">
@ -1869,7 +1869,7 @@
<target>English (GB)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context>
<context context-type="linenumber">89</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="1858110241312746425">
@ -1877,7 +1877,7 @@
<target>German</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context>
<context context-type="linenumber">90</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3071065188816255493">
@ -1885,7 +1885,7 @@
<target>Dutch</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">91</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7633754075223722162">
@ -1893,7 +1893,23 @@
<target>French</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9184513005098760425">
<source>Portuguese (Brazil)</source>
<target>Portuguese (Brazil)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2119857572761283468">

View File

@ -606,7 +606,7 @@
<target>Utiliser la langue du système</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7729897675462249787">
@ -614,15 +614,7 @@
<target>Utiliser le format de date de la langue d'affichage</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">96</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">97</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8488620293789898901">
@ -630,7 +622,7 @@
<target>Une erreur s'est produite lors de l'enregistrement des paramètres sur le serveur : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">114</context>
<context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@ -819,7 +811,7 @@
</trans-unit>
<trans-unit datatype="html" id="8680abbea249ebe9c2fe35556559c8e1a9eb5841">
<source>Document processing</source>
<target>Traitement de document</target>
<target>Traitement de documents</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">118</context>
@ -1832,6 +1824,14 @@
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="d6529debfc1613db22d6fa096ebfeb8a85fa739d">
<source>Invalid date.</source>
<target>Date incorrecte.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2807800733729323332">
<source>Yes</source>
<target>Oui</target>
@ -1861,7 +1861,7 @@
<target>Anglais (US)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context>
<context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6987083569809053351">
@ -1869,7 +1869,7 @@
<target>Anglais (GB)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context>
<context context-type="linenumber">89</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="1858110241312746425">
@ -1877,7 +1877,7 @@
<target>Allemand</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context>
<context context-type="linenumber">90</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3071065188816255493">
@ -1885,7 +1885,7 @@
<target>Néerlandais</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">91</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7633754075223722162">
@ -1893,7 +1893,23 @@
<target>Français</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9184513005098760425">
<source>Portuguese (Brazil)</source>
<target>Portugais (Brésil)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2119857572761283468">

File diff suppressed because it is too large Load Diff

View File

@ -246,6 +246,16 @@ $border-color-dark-mode: #47494f;
}
}
.btn-light:not(:disabled):not(.disabled) {
background-color: $bg-dark-mode;
color: $text-color-dark-mode-accent;
&:hover {
background-color: $text-color-dark-mode;
color: $bg-dark-mode;
}
}
.btn-link:not(:disabled):not(.disabled) {
color: $primary-dark-mode;
}
@ -366,6 +376,12 @@ $border-color-dark-mode: #47494f;
.progress-bar.bg-primary {
background-color: darken($primary-dark-mode, 5%) !important;
}
.ngb-dp-header,
.ngb-dp-weekdays,
.ngb-dp-month {
background-color: $bg-light-dark-mode;
}
}
body.color-scheme-dark {

View File

@ -1,11 +1,13 @@
import re
import magic
from django.utils.text import slugify
from rest_framework import serializers
from rest_framework.fields import SerializerMethodField
from . import bulk_edit
from .models import Correspondent, Tag, Document, Log, DocumentType, \
SavedView, SavedViewFilterRule
from .models import Correspondent, Tag, Document, DocumentType, \
SavedView, SavedViewFilterRule, MatchingModel
from .parsers import is_mime_type_supported
from django.utils.translation import gettext as _
@ -33,16 +35,30 @@ class DynamicFieldsModelSerializer(serializers.ModelSerializer):
self.fields.pop(field_name)
class CorrespondentSerializer(serializers.ModelSerializer):
class MatchingModelSerializer(serializers.ModelSerializer):
document_count = serializers.IntegerField(read_only=True)
last_correspondence = serializers.DateTimeField(read_only=True)
def get_slug(self, obj):
return slugify(obj.name)
slug = SerializerMethodField()
def validate_match(self, match):
if 'matching_algorithm' in self.initial_data and self.initial_data['matching_algorithm'] == MatchingModel.MATCH_REGEX: # NOQA: E501
try:
re.compile(match)
except Exception as e:
raise serializers.ValidationError(
_("Invalid regular expresssion: %(error)s") %
{'error': str(e)}
)
return match
class CorrespondentSerializer(MatchingModelSerializer):
last_correspondence = serializers.DateTimeField(read_only=True)
class Meta:
model = Correspondent
fields = (
@ -57,13 +73,7 @@ class CorrespondentSerializer(serializers.ModelSerializer):
)
class DocumentTypeSerializer(serializers.ModelSerializer):
document_count = serializers.IntegerField(read_only=True)
def get_slug(self, obj):
return slugify(obj.name)
slug = SerializerMethodField()
class DocumentTypeSerializer(MatchingModelSerializer):
class Meta:
model = DocumentType
@ -78,13 +88,7 @@ class DocumentTypeSerializer(serializers.ModelSerializer):
)
class TagSerializer(serializers.ModelSerializer):
document_count = serializers.IntegerField(read_only=True)
def get_slug(self, obj):
return slugify(obj.name)
slug = SerializerMethodField()
class TagSerializer(MatchingModelSerializer):
class Meta:
model = Tag

View File

@ -15,6 +15,7 @@
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="manifest" href="{% static webmanifest %}">
<link rel="stylesheet" href="{% static styles_css %}">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
</head>
<body>
<app-root>{% translate "Paperless-ng is loading..." %}</app-root>

View File

@ -14,7 +14,7 @@ from rest_framework.test import APITestCase
from whoosh.writing import AsyncWriter
from documents import index, bulk_edit
from documents.models import Document, Correspondent, DocumentType, Tag, SavedView
from documents.models import Document, Correspondent, DocumentType, Tag, SavedView, MatchingModel
from documents.tests.utils import DirectoriesMixin
@ -772,6 +772,41 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, 200)
self.assertListEqual(response.data, ["test", "test2"])
def test_invalid_regex_other_algorithm(self):
for endpoint in ['correspondents', 'tags', 'document_types']:
response = self.client.post(f"/api/{endpoint}/", {
"name": "test",
"matching_algorithm": MatchingModel.MATCH_ANY,
"match": "["
}, format='json')
self.assertEqual(response.status_code, 201, endpoint)
def test_invalid_regex(self):
for endpoint in ['correspondents', 'tags', 'document_types']:
response = self.client.post(f"/api/{endpoint}/", {
"name": "test",
"matching_algorithm": MatchingModel.MATCH_REGEX,
"match": "["
}, format='json')
self.assertEqual(response.status_code, 400, endpoint)
def test_valid_regex(self):
for endpoint in ['correspondents', 'tags', 'document_types']:
response = self.client.post(f"/api/{endpoint}/", {
"name": "test",
"matching_algorithm": MatchingModel.MATCH_REGEX,
"match": "[0-9]"
}, format='json')
self.assertEqual(response.status_code, 201, endpoint)
def test_regex_no_algorithm(self):
for endpoint in ['correspondents', 'tags', 'document_types']:
response = self.client.post(f"/api/{endpoint}/", {
"name": "test",
"match": "[0-9]"
}, format='json')
self.assertEqual(response.status_code, 201, endpoint)
class TestBulkEdit(DirectoriesMixin, APITestCase):

View File

@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-16 14:52+0100\n"
"POT-Creation-Date: 2021-02-24 16:49+0100\n"
"PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Jonas Winkler, 2021\n"
"Language-Team: German (https://www.transifex.com/paperless/teams/115905/de/)\n"
@ -354,7 +354,12 @@ msgstr "Filterregel"
msgid "filter rules"
msgstr "Filterregeln"
#: documents/serialisers.py:370
#: documents/serialisers.py:52
#, python-format
msgid "Invalid regular expresssion: %(error)s"
msgstr "Ungültiger regulärer Ausdruck: %(error)s"
#: documents/serialisers.py:378
#, python-format
msgid "File type %(type)s not supported"
msgstr "Dateityp %(type)s nicht unterstützt"
@ -421,7 +426,11 @@ msgstr "Niederländisch"
msgid "French"
msgstr "Französisch"
#: paperless/urls.py:114
#: paperless/settings.py:296
msgid "Portuguese (Brazil)"
msgstr "Portugiesisch (Brasilien)"
#: paperless/urls.py:118
msgid "Paperless-ng administration"
msgstr "Paperless-ng Administration"

View File

@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-16 14:52+0100\n"
"POT-Creation-Date: 2021-02-24 16:49+0100\n"
"PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Jonas Winkler, 2021\n"
"Language-Team: English (United Kingdom) (https://www.transifex.com/paperless/teams/115905/en_GB/)\n"
@ -355,7 +355,12 @@ msgstr "filter rule"
msgid "filter rules"
msgstr "filter rules"
#: documents/serialisers.py:370
#: documents/serialisers.py:52
#, python-format
msgid "Invalid regular expresssion: %(error)s"
msgstr "Invalid regular expresssion: %(error)s"
#: documents/serialisers.py:378
#, python-format
msgid "File type %(type)s not supported"
msgstr "File type %(type)s not supported"
@ -420,7 +425,11 @@ msgstr "Dutch"
msgid "French"
msgstr "French"
#: paperless/urls.py:114
#: paperless/settings.py:296
msgid "Portuguese (Brazil)"
msgstr "Portuguese (Brazil)"
#: paperless/urls.py:118
msgid "Paperless-ng administration"
msgstr "Paperless-ng administration"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-16 14:52+0100\n"
"POT-Creation-Date: 2021-02-24 16:49+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -346,7 +346,12 @@ msgstr ""
msgid "filter rules"
msgstr ""
#: documents/serialisers.py:370
#: documents/serialisers.py:52
#, python-format
msgid "Invalid regular expresssion: %(error)s"
msgstr ""
#: documents/serialisers.py:378
#, python-format
msgid "File type %(type)s not supported"
msgstr ""
@ -411,7 +416,11 @@ msgstr ""
msgid "French"
msgstr ""
#: paperless/urls.py:114
#: paperless/settings.py:296
msgid "Portuguese (Brazil)"
msgstr ""
#: paperless/urls.py:118
msgid "Paperless-ng administration"
msgstr ""

View File

@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-16 14:52+0100\n"
"POT-Creation-Date: 2021-02-24 16:49+0100\n"
"PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Philmo67, 2021\n"
"Language-Team: French (https://www.transifex.com/paperless/teams/115905/fr/)\n"
@ -356,7 +356,12 @@ msgstr "règle de filtrage"
msgid "filter rules"
msgstr "règles de filtrage"
#: documents/serialisers.py:370
#: documents/serialisers.py:52
#, python-format
msgid "Invalid regular expresssion: %(error)s"
msgstr "Expression régulière incorrecte : %(error)s"
#: documents/serialisers.py:378
#, python-format
msgid "File type %(type)s not supported"
msgstr "Type de fichier %(type)s non pris en charge"
@ -423,7 +428,11 @@ msgstr "Néerlandais"
msgid "French"
msgstr "Français"
#: paperless/urls.py:114
#: paperless/settings.py:296
msgid "Portuguese (Brazil)"
msgstr "Portugais (Brésil)"
#: paperless/urls.py:118
msgid "Paperless-ng administration"
msgstr "Administration de Paperless-ng"

View File

@ -0,0 +1,672 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
# Translators:
# Jonas Winkler, 2021
# Rodrigo A <rodrigo.avelino@meliuz.com.br>, 2021
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-24 16:49+0100\n"
"PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Rodrigo A <rodrigo.avelino@meliuz.com.br>, 2021\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/paperless/teams/115905/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: documents/apps.py:10
msgid "Documents"
msgstr "Documentos"
#: documents/models.py:32
msgid "Any word"
msgstr "Qualquer palavra"
#: documents/models.py:33
msgid "All words"
msgstr "Todas as palavras"
#: documents/models.py:34
msgid "Exact match"
msgstr "Detecção exata"
#: documents/models.py:35
msgid "Regular expression"
msgstr "Expressão regular"
#: documents/models.py:36
msgid "Fuzzy word"
msgstr "Palavra difusa (fuzzy)"
#: documents/models.py:37
msgid "Automatic"
msgstr "Automático"
#: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25
#: paperless_mail/models.py:109
msgid "name"
msgstr "nome"
#: documents/models.py:45
msgid "match"
msgstr "detecção"
#: documents/models.py:49
msgid "matching algorithm"
msgstr "algoritmo de detecção"
#: documents/models.py:55
msgid "is insensitive"
msgstr "diferencia maiúsculas de minúsculas"
#: documents/models.py:74 documents/models.py:134
msgid "correspondent"
msgstr "correspondente"
#: documents/models.py:75
msgid "correspondents"
msgstr "correspondentes"
#: documents/models.py:97
msgid "color"
msgstr "cor"
#: documents/models.py:101
msgid "is inbox tag"
msgstr "é etiqueta caixa de entrada"
#: documents/models.py:103
msgid ""
"Marks this tag as an inbox tag: All newly consumed documents will be tagged "
"with inbox tags."
msgstr ""
"Marca essa etiqueta como caixa de entrada: Todos os novos documentos "
"consumidos terão as etiquetas de caixa de entrada."
#: documents/models.py:108
msgid "tag"
msgstr "etiqueta"
#: documents/models.py:109 documents/models.py:165
msgid "tags"
msgstr "etiquetas"
#: documents/models.py:115 documents/models.py:147
msgid "document type"
msgstr "tipo de documento"
#: documents/models.py:116
msgid "document types"
msgstr "tipos de documento"
#: documents/models.py:124
msgid "Unencrypted"
msgstr "Não encriptado"
#: documents/models.py:125
msgid "Encrypted with GNU Privacy Guard"
msgstr "Encriptado com GNU Privacy Guard"
#: documents/models.py:138
msgid "title"
msgstr "título"
#: documents/models.py:151
msgid "content"
msgstr "conteúdo"
#: documents/models.py:153
msgid ""
"The raw, text-only data of the document. This field is primarily used for "
"searching."
msgstr ""
"O conteúdo de texto bruto do documento. Esse campo é usado principalmente "
"para busca."
#: documents/models.py:158
msgid "mime type"
msgstr "tipo mime"
#: documents/models.py:169
msgid "checksum"
msgstr "some de verificação"
#: documents/models.py:173
msgid "The checksum of the original document."
msgstr "A soma de verificação original do documento."
#: documents/models.py:177
msgid "archive checksum"
msgstr "Soma de verificação de arquivamento."
#: documents/models.py:182
msgid "The checksum of the archived document."
msgstr "A soma de verificação do documento arquivado."
#: documents/models.py:186 documents/models.py:342
msgid "created"
msgstr "criado"
#: documents/models.py:190
msgid "modified"
msgstr "modificado"
#: documents/models.py:194
msgid "storage type"
msgstr "tipo de armazenamento"
#: documents/models.py:202
msgid "added"
msgstr "adicionado"
#: documents/models.py:206
msgid "filename"
msgstr "nome do arquivo"
#: documents/models.py:212
msgid "Current filename in storage"
msgstr "Nome do arquivo atual armazenado"
#: documents/models.py:216
msgid "archive filename"
msgstr "nome do arquivo para arquivamento"
#: documents/models.py:222
msgid "Current archive filename in storage"
msgstr "Nome do arquivo para arquivamento armazenado"
#: documents/models.py:226
msgid "archive serial number"
msgstr "número de sério de arquivamento"
#: documents/models.py:231
msgid "The position of this document in your physical document archive."
msgstr "A posição deste documento no seu arquivamento físico."
#: documents/models.py:237
msgid "document"
msgstr "documento"
#: documents/models.py:238
msgid "documents"
msgstr "documentos"
#: documents/models.py:325
msgid "debug"
msgstr "debug"
#: documents/models.py:326
msgid "information"
msgstr "informação"
#: documents/models.py:327
msgid "warning"
msgstr "aviso"
#: documents/models.py:328
msgid "error"
msgstr "erro"
#: documents/models.py:329
msgid "critical"
msgstr "crítico"
#: documents/models.py:333
msgid "group"
msgstr "grupo"
#: documents/models.py:336
msgid "message"
msgstr "mensagem"
#: documents/models.py:339
msgid "level"
msgstr "nível"
#: documents/models.py:346
msgid "log"
msgstr "log"
#: documents/models.py:347
msgid "logs"
msgstr "logs"
#: documents/models.py:358 documents/models.py:408
msgid "saved view"
msgstr "visualização"
#: documents/models.py:359
msgid "saved views"
msgstr "visualizações"
#: documents/models.py:362
msgid "user"
msgstr "usuário"
#: documents/models.py:368
msgid "show on dashboard"
msgstr "exibir no painel de controle"
#: documents/models.py:371
msgid "show in sidebar"
msgstr "exibir no painel lateral"
#: documents/models.py:375
msgid "sort field"
msgstr "ordenar campo"
#: documents/models.py:378
msgid "sort reverse"
msgstr "odernar reverso"
#: documents/models.py:384
msgid "title contains"
msgstr "título contém"
#: documents/models.py:385
msgid "content contains"
msgstr "conteúdo contém"
#: documents/models.py:386
msgid "ASN is"
msgstr "NSA é"
#: documents/models.py:387
msgid "correspondent is"
msgstr "correspondente é"
#: documents/models.py:388
msgid "document type is"
msgstr "tipo de documento é"
#: documents/models.py:389
msgid "is in inbox"
msgstr "é caixa de entrada"
#: documents/models.py:390
msgid "has tag"
msgstr "contém etiqueta"
#: documents/models.py:391
msgid "has any tag"
msgstr "contém qualquer etiqueta"
#: documents/models.py:392
msgid "created before"
msgstr "criado antes de"
#: documents/models.py:393
msgid "created after"
msgstr "criado depois de"
#: documents/models.py:394
msgid "created year is"
msgstr "ano de criação é"
#: documents/models.py:395
msgid "created month is"
msgstr "mês de criação é"
#: documents/models.py:396
msgid "created day is"
msgstr "dia de criação é"
#: documents/models.py:397
msgid "added before"
msgstr "adicionado antes de"
#: documents/models.py:398
msgid "added after"
msgstr "adicionado depois de"
#: documents/models.py:399
msgid "modified before"
msgstr "modificado antes de"
#: documents/models.py:400
msgid "modified after"
msgstr "modificado depois de"
#: documents/models.py:401
msgid "does not have tag"
msgstr "não tem etiqueta"
#: documents/models.py:412
msgid "rule type"
msgstr "tipo de regra"
#: documents/models.py:416
msgid "value"
msgstr "valor"
#: documents/models.py:422
msgid "filter rule"
msgstr "regra de filtragem"
#: documents/models.py:423
msgid "filter rules"
msgstr "regras de filtragem"
#: documents/serialisers.py:52
#, python-format
msgid "Invalid regular expresssion: %(error)s"
msgstr "Expressão regular inválida: %(error)s"
#: documents/serialisers.py:378
#, python-format
msgid "File type %(type)s not supported"
msgstr "Tipo de arquivo %(type)s não suportado"
#: documents/templates/index.html:20
msgid "Paperless-ng is loading..."
msgstr "Paperless-ng está carregando..."
#: documents/templates/registration/logged_out.html:13
msgid "Paperless-ng signed out"
msgstr "Paperless-ng saiu"
#: documents/templates/registration/logged_out.html:41
msgid "You have been successfully logged out. Bye!"
msgstr "Sua sessão foi encerrada com sucesso. Até mais!"
#: documents/templates/registration/logged_out.html:42
msgid "Sign in again"
msgstr "Entre novamente"
#: documents/templates/registration/login.html:13
msgid "Paperless-ng sign in"
msgstr "Entrar no Paperless-ng"
#: documents/templates/registration/login.html:42
msgid "Please sign in."
msgstr "Por favor, entre na sua conta"
#: documents/templates/registration/login.html:45
msgid "Your username and password didn't match. Please try again."
msgstr "Seu usuário e senha estão incorretos. Por favor, tente novamente."
#: documents/templates/registration/login.html:48
msgid "Username"
msgstr "Usuário"
#: documents/templates/registration/login.html:49
msgid "Password"
msgstr "Senha"
#: documents/templates/registration/login.html:54
msgid "Sign in"
msgstr "Entrar"
#: paperless/settings.py:291
msgid "English (US)"
msgstr "Inglês (EUA)"
#: paperless/settings.py:292
msgid "English (GB)"
msgstr "Inglês (GB)"
#: paperless/settings.py:293
msgid "German"
msgstr "Alemão"
#: paperless/settings.py:294
msgid "Dutch"
msgstr "Holandês"
#: paperless/settings.py:295
msgid "French"
msgstr "Francês"
#: paperless/settings.py:296
msgid "Portuguese (Brazil)"
msgstr "Português (Brasil)"
#: paperless/urls.py:118
msgid "Paperless-ng administration"
msgstr "Administração do Paperless-ng"
#: paperless_mail/admin.py:25
msgid "Filter"
msgstr "Filtro"
#: paperless_mail/admin.py:27
msgid ""
"Paperless will only process mails that match ALL of the filters given below."
msgstr ""
"Paperless processará somente e-mails que se encaixam em TODOS os filtros "
"abaixo."
#: paperless_mail/admin.py:37
msgid "Actions"
msgstr "Ações"
#: paperless_mail/admin.py:39
msgid ""
"The action applied to the mail. This action is only performed when documents"
" were consumed from the mail. Mails without attachments will remain entirely"
" untouched."
msgstr ""
"A ação se aplica ao e-mail. Essa ação só é executada quando documentos foram"
" consumidos do e-mail. E-mails sem anexos permanecerão intactos."
#: paperless_mail/admin.py:46
msgid "Metadata"
msgstr "Metadados"
#: paperless_mail/admin.py:48
msgid ""
"Assign metadata to documents consumed from this rule automatically. If you "
"do not assign tags, types or correspondents here, paperless will still "
"process all matching rules that you have defined."
msgstr ""
"Atribua metadados aos documentos consumidos por esta regra automaticamente. "
"Se você não atribuir etiquetas, tipos ou correspondentes aqui, paperless "
"ainda sim processará todas as regras de detecção que você definiu."
#: paperless_mail/apps.py:9
msgid "Paperless mail"
msgstr "Paperless mail"
#: paperless_mail/models.py:11
msgid "mail account"
msgstr "conta de e-mail"
#: paperless_mail/models.py:12
msgid "mail accounts"
msgstr "contas de e-mail"
#: paperless_mail/models.py:19
msgid "No encryption"
msgstr "Sem encriptação"
#: paperless_mail/models.py:20
msgid "Use SSL"
msgstr "Usar SSL"
#: paperless_mail/models.py:21
msgid "Use STARTTLS"
msgstr "Usar STARTTLS"
#: paperless_mail/models.py:29
msgid "IMAP server"
msgstr "Servidor IMAP"
#: paperless_mail/models.py:33
msgid "IMAP port"
msgstr "Porta IMAP"
#: paperless_mail/models.py:36
msgid ""
"This is usually 143 for unencrypted and STARTTLS connections, and 993 for "
"SSL connections."
msgstr ""
"É geralmente 143 para não encriptado e conexões STARTTLS, e 993 para "
"conexões SSL."
#: paperless_mail/models.py:40
msgid "IMAP security"
msgstr "segurança IMAP"
#: paperless_mail/models.py:46
msgid "username"
msgstr "usuário"
#: paperless_mail/models.py:50
msgid "password"
msgstr "senha"
#: paperless_mail/models.py:60
msgid "mail rule"
msgstr "regra de e-mail"
#: paperless_mail/models.py:61
msgid "mail rules"
msgstr "regras de e-mail"
#: paperless_mail/models.py:67
msgid "Only process attachments."
msgstr "Processar somente anexos."
#: paperless_mail/models.py:68
msgid "Process all files, including 'inline' attachments."
msgstr "Processar todos os arquivos, incluindo anexos 'inline'."
#: paperless_mail/models.py:78
msgid "Mark as read, don't process read mails"
msgstr "Marcar como lido, não processar e-mails lidos"
#: paperless_mail/models.py:79
msgid "Flag the mail, don't process flagged mails"
msgstr "Sinalizar o e-mail, não processar e-mails sinalizados"
#: paperless_mail/models.py:80
msgid "Move to specified folder"
msgstr "Mover para pasta especificada"
#: paperless_mail/models.py:81
msgid "Delete"
msgstr "Excluir"
#: paperless_mail/models.py:88
msgid "Use subject as title"
msgstr "Usar assunto como título"
#: paperless_mail/models.py:89
msgid "Use attachment filename as title"
msgstr "Usar nome do arquivo anexo como título"
#: paperless_mail/models.py:99
msgid "Do not assign a correspondent"
msgstr "Não atribuir um correspondente"
#: paperless_mail/models.py:101
msgid "Use mail address"
msgstr "Usar endereço de e-mail"
#: paperless_mail/models.py:103
msgid "Use name (or mail address if not available)"
msgstr "Usar nome (ou endereço de e-mail se não disponível)"
#: paperless_mail/models.py:105
msgid "Use correspondent selected below"
msgstr "Usar correspondente selecionado abaixo"
#: paperless_mail/models.py:113
msgid "order"
msgstr "ordem"
#: paperless_mail/models.py:120
msgid "account"
msgstr "conta"
#: paperless_mail/models.py:124
msgid "folder"
msgstr "pasta"
#: paperless_mail/models.py:128
msgid "filter from"
msgstr "filtrar de"
#: paperless_mail/models.py:131
msgid "filter subject"
msgstr "filtrar assunto"
#: paperless_mail/models.py:134
msgid "filter body"
msgstr "filtrar corpo"
#: paperless_mail/models.py:138
msgid "filter attachment filename"
msgstr "filtrar nome do arquivo anexo"
#: paperless_mail/models.py:140
msgid ""
"Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
msgstr ""
"Consumir somente documentos que correspondem a este nome de arquivo se especificado.\n"
"Curingas como *.pdf ou *invoice* são permitidos. Sem diferenciação de maiúsculas e minúsculas."
#: paperless_mail/models.py:146
msgid "maximum age"
msgstr "idade máxima"
#: paperless_mail/models.py:148
msgid "Specified in days."
msgstr "Especificada em dias."
#: paperless_mail/models.py:151
msgid "attachment type"
msgstr "tipo de anexo"
#: paperless_mail/models.py:154
msgid ""
"Inline attachments include embedded images, so it's best to combine this "
"option with a filename filter."
msgstr ""
"Anexos inline incluem imagens inseridas, por isso é melhor combinar essa "
"opção com um filtro de nome de arquivo."
#: paperless_mail/models.py:159
msgid "action"
msgstr "ação"
#: paperless_mail/models.py:165
msgid "action parameter"
msgstr "parâmetro da ação"
#: paperless_mail/models.py:167
msgid ""
"Additional parameter for the action selected above, i.e., the target folder "
"of the move to folder action."
msgstr ""
"Parâmetro adicional para a ação selecionada acima, por exemplo: a pasta de "
"destino da ação de mover pasta."
#: paperless_mail/models.py:173
msgid "assign title from"
msgstr "atribuir título de"
#: paperless_mail/models.py:183
msgid "assign this tag"
msgstr "atribuir esta etiqueta"
#: paperless_mail/models.py:191
msgid "assign this document type"
msgstr "atribuir este tipo de documento"
#: paperless_mail/models.py:195
msgid "assign correspondent from"
msgstr "atribuir correspondente de"
#: paperless_mail/models.py:205
msgid "assign this correspondent"
msgstr "atribuir este correspondente"

View File

@ -112,7 +112,10 @@ REST_FRAMEWORK = {
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
]
],
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
'DEFAULT_VERSION': '1',
'ALLOWED_VERSIONS': ['1', '2']
}
if DEBUG:
@ -142,7 +145,7 @@ ASGI_APPLICATION = "paperless.asgi.application"
STATIC_URL = os.getenv("PAPERLESS_STATIC_URL", "/static/")
# what is this used for?
# TODO: what is this used for?
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
@ -292,7 +295,8 @@ LANGUAGES = [
("en-gb", _("English (GB)")),
("de", _("German")),
("nl-nl", _("Dutch")),
("fr", _("French"))
("fr", _("French")),
("pt-br", _("Portuguese (Brazil)"))
]
LOCALE_PATHS = [

View File

@ -1 +1 @@
__version__ = (1, 2, 0)
__version__ = (1, 2, 1)