Merge pull request #591 from paperless-ngx/feature-version-checker

Feature: Update checker
This commit is contained in:
Felix E 2022-04-05 20:11:21 +02:00 committed by GitHub
commit ccf9b1291e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 166 additions and 11 deletions

View File

@ -764,3 +764,26 @@ PAPERLESS_OCR_LANGUAGES=<list>
PAPERLESS_OCR_LANGUAGE=tur PAPERLESS_OCR_LANGUAGE=tur
Defaults to none, which does not install any additional languages. Defaults to none, which does not install any additional languages.
.. _configuration-update-checking:
Update Checking
###############
PAPERLESS_ENABLE_UPDATE_CHECK=<bool>
Enable (or disable) the automatic check for available updates. This feature is disabled
by default but if it is not explicitly set Paperless-ngx will show a message about this.
If enabled, the feature works by pinging the the Github API for the latest release e.g.
https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest
to determine whether a new version is available.
Actual updating of the app must still be performed manually.
Note that for users of thirdy-party containers e.g. linuxserver.io this notification
may be 'ahead' of a new release from the third-party maintainers.
In either case, no tracking data is collected by the app in any way.
Defaults to none, which disables the feature.

View File

@ -67,6 +67,7 @@
#PAPERLESS_FILENAME_PARSE_TRANSFORMS=[] #PAPERLESS_FILENAME_PARSE_TRANSFORMS=[]
#PAPERLESS_THUMBNAIL_FONT_NAME= #PAPERLESS_THUMBNAIL_FONT_NAME=
#PAPERLESS_IGNORE_DATES= #PAPERLESS_IGNORE_DATES=
#PAPERLESS_ENABLE_UPDATE_CHECK=
# Tika settings # Tika settings

View File

@ -12,7 +12,7 @@
</a> </a>
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1"> <div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1">
<form (ngSubmit)="search()" class="form-inline flex-grow-1"> <form (ngSubmit)="search()" class="form-inline flex-grow-1">
<svg width="1em" height="1em"> <svg width="1em" height="1em" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#search"/> <use xlink:href="assets/bootstrap-icons.svg#search"/>
</svg> </svg>
<input class="form-control form-control-sm" type="text" placeholder="Search documents" aria-label="Search" <input class="form-control form-control-sm" type="text" placeholder="Search documents" aria-label="Search"
@ -25,7 +25,7 @@
<span *ngIf="displayName" class="navbar-text small me-2 text-light d-none d-sm-inline"> <span *ngIf="displayName" class="navbar-text small me-2 text-light d-none d-sm-inline">
{{displayName}} {{displayName}}
</span> </span>
<svg width="1.3em" height="1.3em"> <svg width="1.3em" height="1.3em" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#person-circle"/> <use xlink:href="assets/bootstrap-icons.svg#person-circle"/>
</svg> </svg>
</button> </button>
@ -170,21 +170,46 @@
<li class="nav-item"> <li class="nav-item">
<div class="d-flex w-100 flex-wrap"> <div class="d-flex w-100 flex-wrap">
<a class="nav-link pe-2 pb-1" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx"> <a class="nav-link pe-2 pb-1" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon bi bi-github" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/> <use xlink:href="assets/bootstrap-icons.svg#github" />
</svg>&nbsp;<ng-container i18n>GitHub</ng-container> </svg>&nbsp;<ng-container i18n>GitHub</ng-container>
</a> </a>
<a class="nav-link-additional small text-muted ms-3" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests" title="Suggest an idea"> <a class="nav-link-additional small text-muted ms-3" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests" title="Suggest an idea">
<svg xmlns="http://www.w3.org/2000/svg" width="1.3em" height="1.3em" fill="currentColor" class="bi bi-lightbulb pe-1" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="1.1em" height="1.1em" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z"/> <use xlink:href="assets/bootstrap-icons.svg#lightbulb" />
</svg> </svg>
<ng-container i18n>Suggest an idea</ng-container> <ng-container i18n>Suggest an idea</ng-container>
</a> </a>
</div> </div>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">
<div class="px-3 py-2 text-muted small"> <div class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap">
{{versionString}} <div class="me-3">{{ versionString }}</div>
<div *ngIf="appRemoteVersion" class="version-check">
<ng-template #updateAvailablePopContent>
<span class="small">Paperless-ngx v{{ appRemoteVersion.version }} <ng-container i18n>is available.</ng-container><br/><ng-container i18n>Click to view.</ng-container></span>
</ng-template>
<ng-template #updateCheckingNotEnabledPopContent>
<span class="small"><ng-container i18n>Checking for updates is disabled.</ng-container><br/><ng-container i18n>Click for more information.</ng-container></span>
</ng-template>
<ng-container *ngIf="appRemoteVersion.feature_is_set; else updateCheckNotSet">
<a *ngIf="appRemoteVersion.update_available" class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/releases"
[ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
<ng-container *ngIf="appRemoteVersion?.update_available" i18n>Update available</ng-container>
</a>
</ng-container>
<ng-template #updateCheckNotSet>
<a class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/configuration.html#update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
</a>
</ng-template>
</div>
</div> </div>
</li> </li>
</ul> </ul>

View File

@ -176,3 +176,22 @@
} }
} }
} }
.version-check {
animation: pulse 2s ease-in-out 0s 1;
}
@keyframes pulse {
0% {
opacity: 0;
}
25% {
opacity: 100%;
}
75% {
opacity: 0;
}
100% {
opacity: 100%;
}
}

View File

@ -18,6 +18,10 @@ import { DocumentDetailComponent } from '../document-detail/document-detail.comp
import { Meta } from '@angular/platform-browser' import { Meta } from '@angular/platform-browser'
import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type' import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
import {
RemoteVersionService,
AppRemoteVersion,
} from 'src/app/services/rest/remote-version.service'
@Component({ @Component({
selector: 'app-app-frame', selector: 'app-app-frame',
@ -32,10 +36,18 @@ export class AppFrameComponent {
private searchService: SearchService, private searchService: SearchService,
public savedViewService: SavedViewService, public savedViewService: SavedViewService,
private list: DocumentListViewService, private list: DocumentListViewService,
private meta: Meta private meta: Meta,
) {} private remoteVersionService: RemoteVersionService
) {
this.remoteVersionService
.checkForUpdates()
.subscribe((appRemoteVersion: AppRemoteVersion) => {
this.appRemoteVersion = appRemoteVersion
})
}
versionString = `${environment.appTitle} ${environment.version}` versionString = `${environment.appTitle} ${environment.version}`
appRemoteVersion
isMenuCollapsed: boolean = true isMenuCollapsed: boolean = true

View File

@ -0,0 +1,23 @@
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { map, Observable } from 'rxjs'
import { environment } from 'src/environments/environment'
export interface AppRemoteVersion {
version: string
update_available: boolean
feature_is_set: boolean
}
@Injectable({
providedIn: 'root',
})
export class RemoteVersionService {
constructor(private http: HttpClient) {}
public checkForUpdates(): Observable<AppRemoteVersion> {
return this.http.get<AppRemoteVersion>(
`${environment.apiBaseUrl}remote_version/`
)
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 579 KiB

After

Width:  |  Height:  |  Size: 861 KiB

View File

@ -271,6 +271,7 @@ table.table {
.popover-body { .popover-body {
background-color: var(--ngx-bg-alt); background-color: var(--ngx-bg-alt);
border-color: var(--bs-border-color); border-color: var(--bs-border-color);
color: var(--bs-body-color);
} }
} }

View File

@ -1,6 +1,8 @@
import json
import logging import logging
import os import os
import tempfile import tempfile
import urllib
import uuid import uuid
import zipfile import zipfile
from datetime import datetime from datetime import datetime
@ -24,6 +26,8 @@ from django.views.decorators.cache import cache_control
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from django_q.tasks import async_task from django_q.tasks import async_task
from packaging import version as packaging_version
from paperless import version
from paperless.db import GnuPG from paperless.db import GnuPG
from paperless.views import StandardPagination from paperless.views import StandardPagination
from rest_framework import parsers from rest_framework import parsers
@ -666,3 +670,40 @@ class BulkDownloadView(GenericAPIView):
) )
return response return response
class RemoteVersionView(GenericAPIView):
def get(self, request, format=None):
remote_version = "0.0.0"
is_greater_than_current = False
# TODO: this can likely be removed when frontend settings are saved to DB
feature_is_set = settings.ENABLE_UPDATE_CHECK != "default"
if feature_is_set and settings.ENABLE_UPDATE_CHECK:
try:
with urllib.request.urlopen(
"https://api.github.com/repos/"
+ "paperless-ngx/paperless-ngx/releases/latest",
) as response:
remote = response.read().decode("utf-8")
try:
remote_json = json.loads(remote)
remote_version = remote_json["tag_name"].replace("ngx-", "")
except ValueError:
logger.debug("An error occured parsing remote version json")
except urllib.error.URLError:
logger.debug("An error occured checking for available updates")
current_version = ".".join([str(_) for _ in version.__version__[:3]])
is_greater_than_current = packaging_version.parse(
remote_version,
) > packaging_version.parse(
current_version,
)
return Response(
{
"version": remote_version,
"update_available": is_greater_than_current,
"feature_is_set": feature_is_set,
},
)

View File

@ -566,3 +566,7 @@ if os.getenv("PAPERLESS_IGNORE_DATES", ""):
d = dateparser.parse(s) d = dateparser.parse(s)
if d: if d:
IGNORE_DATES.add(d.date()) IGNORE_DATES.add(d.date())
ENABLE_UPDATE_CHECK = os.getenv("PAPERLESS_ENABLE_UPDATE_CHECK", "default")
if ENABLE_UPDATE_CHECK != "default":
ENABLE_UPDATE_CHECK = __get_boolean("PAPERLESS_ENABLE_UPDATE_CHECK")

View File

@ -14,6 +14,7 @@ from documents.views import DocumentTypeViewSet
from documents.views import IndexView from documents.views import IndexView
from documents.views import LogViewSet from documents.views import LogViewSet
from documents.views import PostDocumentView from documents.views import PostDocumentView
from documents.views import RemoteVersionView
from documents.views import SavedViewViewSet from documents.views import SavedViewViewSet
from documents.views import SearchAutoCompleteView from documents.views import SearchAutoCompleteView
from documents.views import SelectionDataView from documents.views import SelectionDataView
@ -72,6 +73,11 @@ urlpatterns = [
BulkDownloadView.as_view(), BulkDownloadView.as_view(),
name="bulk_download", name="bulk_download",
), ),
re_path(
r"^remote_version/",
RemoteVersionView.as_view(),
name="remoteversion",
),
path("token/", views.obtain_auth_token), path("token/", views.obtain_auth_token),
] ]
+ api_router.urls, + api_router.urls,