mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge pull request #591 from paperless-ngx/feature-version-checker
Feature: Update checker
This commit is contained in:
		| @@ -764,3 +764,26 @@ PAPERLESS_OCR_LANGUAGES=<list> | ||||
|         PAPERLESS_OCR_LANGUAGE=tur | ||||
|  | ||||
|     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. | ||||
|   | ||||
| @@ -67,6 +67,7 @@ | ||||
| #PAPERLESS_FILENAME_PARSE_TRANSFORMS=[] | ||||
| #PAPERLESS_THUMBNAIL_FONT_NAME= | ||||
| #PAPERLESS_IGNORE_DATES= | ||||
| #PAPERLESS_ENABLE_UPDATE_CHECK= | ||||
|  | ||||
| # Tika settings | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|   </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"> | ||||
|     <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"/> | ||||
|       </svg> | ||||
|       <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"> | ||||
|           {{displayName}} | ||||
|         </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"/> | ||||
|         </svg> | ||||
|       </button> | ||||
| @@ -170,21 +170,46 @@ | ||||
|           <li class="nav-item"> | ||||
|             <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"> | ||||
|                 <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon bi bi-github" 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"/> | ||||
|                 <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon" viewBox="0 0 16 16"> | ||||
|                   <use xlink:href="assets/bootstrap-icons.svg#github" /> | ||||
|                 </svg> <ng-container i18n>GitHub</ng-container> | ||||
|               </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"> | ||||
|                 <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"> | ||||
|                   <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"/> | ||||
|                 <svg xmlns="http://www.w3.org/2000/svg" width="1.1em" height="1.1em" fill="currentColor" class="me-1" viewBox="0 0 16 16"> | ||||
|                   <use xlink:href="assets/bootstrap-icons.svg#lightbulb" /> | ||||
|                 </svg> | ||||
|                 <ng-container i18n>Suggest an idea</ng-container> | ||||
|               </a> | ||||
|             </div> | ||||
|           </li> | ||||
|           <li class="nav-item mt-2"> | ||||
|             <div class="px-3 py-2 text-muted small"> | ||||
|               {{versionString}} | ||||
|             <div class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap"> | ||||
|               <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> | ||||
|           </li> | ||||
|         </ul> | ||||
|   | ||||
| @@ -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%; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -18,6 +18,10 @@ import { DocumentDetailComponent } from '../document-detail/document-detail.comp | ||||
| import { Meta } from '@angular/platform-browser' | ||||
| import { DocumentListViewService } from 'src/app/services/document-list-view.service' | ||||
| import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type' | ||||
| import { | ||||
|   RemoteVersionService, | ||||
|   AppRemoteVersion, | ||||
| } from 'src/app/services/rest/remote-version.service' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-app-frame', | ||||
| @@ -32,10 +36,18 @@ export class AppFrameComponent { | ||||
|     private searchService: SearchService, | ||||
|     public savedViewService: SavedViewService, | ||||
|     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}` | ||||
|   appRemoteVersion | ||||
|  | ||||
|   isMenuCollapsed: boolean = true | ||||
|  | ||||
|   | ||||
							
								
								
									
										23
									
								
								src-ui/src/app/services/rest/remote-version.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src-ui/src/app/services/rest/remote-version.service.ts
									
									
									
									
									
										Normal 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 | 
| @@ -271,6 +271,7 @@ table.table { | ||||
|   .popover-body { | ||||
|     background-color: var(--ngx-bg-alt); | ||||
|     border-color: var(--bs-border-color); | ||||
|     color: var(--bs-body-color); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| import json | ||||
| import logging | ||||
| import os | ||||
| import tempfile | ||||
| import urllib | ||||
| import uuid | ||||
| import zipfile | ||||
| from datetime import datetime | ||||
| @@ -24,6 +26,8 @@ from django.views.decorators.cache import cache_control | ||||
| from django.views.generic import TemplateView | ||||
| from django_filters.rest_framework import DjangoFilterBackend | ||||
| 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.views import StandardPagination | ||||
| from rest_framework import parsers | ||||
| @@ -666,3 +670,40 @@ class BulkDownloadView(GenericAPIView): | ||||
|             ) | ||||
|  | ||||
|             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, | ||||
|             }, | ||||
|         ) | ||||
|   | ||||
| @@ -566,3 +566,7 @@ if os.getenv("PAPERLESS_IGNORE_DATES", ""): | ||||
|         d = dateparser.parse(s) | ||||
|         if d: | ||||
|             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") | ||||
|   | ||||
| @@ -14,6 +14,7 @@ from documents.views import DocumentTypeViewSet | ||||
| from documents.views import IndexView | ||||
| from documents.views import LogViewSet | ||||
| from documents.views import PostDocumentView | ||||
| from documents.views import RemoteVersionView | ||||
| from documents.views import SavedViewViewSet | ||||
| from documents.views import SearchAutoCompleteView | ||||
| from documents.views import SelectionDataView | ||||
| @@ -72,6 +73,11 @@ urlpatterns = [ | ||||
|                     BulkDownloadView.as_view(), | ||||
|                     name="bulk_download", | ||||
|                 ), | ||||
|                 re_path( | ||||
|                     r"^remote_version/", | ||||
|                     RemoteVersionView.as_view(), | ||||
|                     name="remoteversion", | ||||
|                 ), | ||||
|                 path("token/", views.obtain_auth_token), | ||||
|             ] | ||||
|             + api_router.urls, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Felix E
					Felix E