mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Feature: system status (#5743)
This commit is contained in:
parent
23ceb2a5ec
commit
f6084acfc8
@ -59,7 +59,8 @@ ARG GS_VERSION=10.02.1
|
|||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
# Ignore warning from Whitenoise
|
# Ignore warning from Whitenoise
|
||||||
PYTHONWARNINGS="ignore:::django.http.response:517"
|
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
||||||
|
PNGX_CONTAINERIZED=1
|
||||||
|
|
||||||
#
|
#
|
||||||
# Begin installation and configuration
|
# Begin installation and configuration
|
||||||
|
@ -77,7 +77,9 @@
|
|||||||
"scripts": [],
|
"scripts": [],
|
||||||
"allowedCommonJsDependencies": [
|
"allowedCommonJsDependencies": [
|
||||||
"pdfjs-dist",
|
"pdfjs-dist",
|
||||||
"pdfjs-dist/web/pdf_viewer"
|
"pdfjs-dist/web/pdf_viewer",
|
||||||
|
"filesize",
|
||||||
|
"file-saver"
|
||||||
],
|
],
|
||||||
"vendorChunk": true,
|
"vendorChunk": true,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
|
@ -458,7 +458,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">354</context>
|
<context context-type="linenumber">375</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||||
@ -600,7 +600,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">342</context>
|
<context context-type="linenumber">363</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
@ -622,6 +622,10 @@
|
|||||||
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context>
|
||||||
<context context-type="linenumber">23</context>
|
<context context-type="linenumber">23</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">10</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.html</context>
|
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.html</context>
|
||||||
<context context-type="linenumber">15</context>
|
<context context-type="linenumber">15</context>
|
||||||
@ -667,7 +671,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">293</context>
|
<context context-type="linenumber">314</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||||
@ -693,238 +697,249 @@
|
|||||||
<source>Start tour</source>
|
<source>Start tour</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">7</context>
|
<context context-type="linenumber">8</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3276228498925657259" datatype="html">
|
||||||
|
<source>System Status</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">27</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">2</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4798013226763881638" datatype="html">
|
<trans-unit id="4798013226763881638" datatype="html">
|
||||||
<source>Open Django Admin</source>
|
<source>Open Django Admin</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">9</context>
|
<context context-type="linenumber">30</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6439365426343089851" datatype="html">
|
<trans-unit id="6439365426343089851" datatype="html">
|
||||||
<source>General</source>
|
<source>General</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">18</context>
|
<context context-type="linenumber">39</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8671234314555525900" datatype="html">
|
<trans-unit id="8671234314555525900" datatype="html">
|
||||||
<source>Appearance</source>
|
<source>Appearance</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">21</context>
|
<context context-type="linenumber">42</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3777637051272512093" datatype="html">
|
<trans-unit id="3777637051272512093" datatype="html">
|
||||||
<source>Display language</source>
|
<source>Display language</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">25</context>
|
<context context-type="linenumber">46</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="53523152145406584" datatype="html">
|
<trans-unit id="53523152145406584" datatype="html">
|
||||||
<source>You need to reload the page after applying a new language.</source>
|
<source>You need to reload the page after applying a new language.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">38</context>
|
<context context-type="linenumber">59</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3766032098416558788" datatype="html">
|
<trans-unit id="3766032098416558788" datatype="html">
|
||||||
<source>Date display</source>
|
<source>Date display</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">46</context>
|
<context context-type="linenumber">67</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3733378544613473393" datatype="html">
|
<trans-unit id="3733378544613473393" datatype="html">
|
||||||
<source>Date format</source>
|
<source>Date format</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">63</context>
|
<context context-type="linenumber">84</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3407788781115661841" datatype="html">
|
<trans-unit id="3407788781115661841" datatype="html">
|
||||||
<source>Short: <x id="INTERPOLATION" equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}"/></source>
|
<source>Short: <x id="INTERPOLATION" equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">69,70</context>
|
<context context-type="linenumber">90,91</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6290748171049664628" datatype="html">
|
<trans-unit id="6290748171049664628" datatype="html">
|
||||||
<source>Medium: <x id="INTERPOLATION" equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}"/></source>
|
<source>Medium: <x id="INTERPOLATION" equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">73,74</context>
|
<context context-type="linenumber">94,95</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7189855711197998347" datatype="html">
|
<trans-unit id="7189855711197998347" datatype="html">
|
||||||
<source>Long: <x id="INTERPOLATION" equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}"/></source>
|
<source>Long: <x id="INTERPOLATION" equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">77,78</context>
|
<context context-type="linenumber">98,99</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8939587804990976924" datatype="html">
|
<trans-unit id="8939587804990976924" datatype="html">
|
||||||
<source>Items per page</source>
|
<source>Items per page</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">85</context>
|
<context context-type="linenumber">106</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8028535997917730106" datatype="html">
|
<trans-unit id="8028535997917730106" datatype="html">
|
||||||
<source>Document editor</source>
|
<source>Document editor</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">101</context>
|
<context context-type="linenumber">122</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6708098108196142028" datatype="html">
|
<trans-unit id="6708098108196142028" datatype="html">
|
||||||
<source>Use PDF viewer provided by the browser</source>
|
<source>Use PDF viewer provided by the browser</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">105</context>
|
<context context-type="linenumber">126</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9003921625412907981" datatype="html">
|
<trans-unit id="9003921625412907981" datatype="html">
|
||||||
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
|
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">105</context>
|
<context context-type="linenumber">126</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3982403428275430291" datatype="html">
|
<trans-unit id="3982403428275430291" datatype="html">
|
||||||
<source>Sidebar</source>
|
<source>Sidebar</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">112</context>
|
<context context-type="linenumber">133</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4608457133854405683" datatype="html">
|
<trans-unit id="4608457133854405683" datatype="html">
|
||||||
<source>Use 'slim' sidebar (icons only)</source>
|
<source>Use 'slim' sidebar (icons only)</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">116</context>
|
<context context-type="linenumber">137</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1356890996281769972" datatype="html">
|
<trans-unit id="1356890996281769972" datatype="html">
|
||||||
<source>Dark mode</source>
|
<source>Dark mode</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">123</context>
|
<context context-type="linenumber">144</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4913823100518391922" datatype="html">
|
<trans-unit id="4913823100518391922" datatype="html">
|
||||||
<source>Use system settings</source>
|
<source>Use system settings</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">126</context>
|
<context context-type="linenumber">147</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5782828784040423650" datatype="html">
|
<trans-unit id="5782828784040423650" datatype="html">
|
||||||
<source>Enable dark mode</source>
|
<source>Enable dark mode</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">127</context>
|
<context context-type="linenumber">148</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6336642923114460405" datatype="html">
|
<trans-unit id="6336642923114460405" datatype="html">
|
||||||
<source>Invert thumbnails in dark mode</source>
|
<source>Invert thumbnails in dark mode</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">128</context>
|
<context context-type="linenumber">149</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7983234071833154796" datatype="html">
|
<trans-unit id="7983234071833154796" datatype="html">
|
||||||
<source>Theme Color</source>
|
<source>Theme Color</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">134</context>
|
<context context-type="linenumber">155</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7808756054397155068" datatype="html">
|
<trans-unit id="7808756054397155068" datatype="html">
|
||||||
<source>Reset</source>
|
<source>Reset</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">141</context>
|
<context context-type="linenumber">162</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8901931207592071833" datatype="html">
|
<trans-unit id="8901931207592071833" datatype="html">
|
||||||
<source>Update checking</source>
|
<source>Update checking</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">146</context>
|
<context context-type="linenumber">167</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7141691772243630313" datatype="html">
|
<trans-unit id="7141691772243630313" datatype="html">
|
||||||
<source> Update checking works by pinging the public <x id="START_LINK" ctype="x-a" equiv-text="<a href="https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest" target="_blank" rel="noopener noreferrer">"/>GitHub API<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/> for the latest release to determine whether a new version is available.<x id="LINE_BREAK" ctype="lb" equiv-text="<br/>"/> Actual updating of the app must still be performed manually. </source>
|
<source> Update checking works by pinging the public <x id="START_LINK" ctype="x-a" equiv-text="<a href="https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest" target="_blank" rel="noopener noreferrer">"/>GitHub API<x id="CLOSE_LINK" ctype="x-a" equiv-text="</a>"/> for the latest release to determine whether a new version is available.<x id="LINE_BREAK" ctype="lb" equiv-text="<br/>"/> Actual updating of the app must still be performed manually. </source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">150,153</context>
|
<context context-type="linenumber">171,174</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5489945693955857309" datatype="html">
|
<trans-unit id="5489945693955857309" datatype="html">
|
||||||
<source><x id="START_EMPHASISED_TEXT" ctype="x-em" equiv-text=">"/>No tracking data is collected by the app in any way.<x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="</em>"/></source>
|
<source><x id="START_EMPHASISED_TEXT" ctype="x-em" equiv-text=">"/>No tracking data is collected by the app in any way.<x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="</em>"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">155,157</context>
|
<context context-type="linenumber">176,178</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5070799004079086984" datatype="html">
|
<trans-unit id="5070799004079086984" datatype="html">
|
||||||
<source>Enable update checking</source>
|
<source>Enable update checking</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">157</context>
|
<context context-type="linenumber">178</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="908152367861642592" datatype="html">
|
<trans-unit id="908152367861642592" datatype="html">
|
||||||
<source>Document editing</source>
|
<source>Document editing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">161</context>
|
<context context-type="linenumber">182</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2959590948110714366" datatype="html">
|
<trans-unit id="2959590948110714366" datatype="html">
|
||||||
<source>Automatically remove inbox tag(s) on save</source>
|
<source>Automatically remove inbox tag(s) on save</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">165</context>
|
<context context-type="linenumber">186</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8508424367627989968" datatype="html">
|
<trans-unit id="8508424367627989968" datatype="html">
|
||||||
<source>Bulk editing</source>
|
<source>Bulk editing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">169</context>
|
<context context-type="linenumber">190</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8158899674926420054" datatype="html">
|
<trans-unit id="8158899674926420054" datatype="html">
|
||||||
<source>Show confirmation dialogs</source>
|
<source>Show confirmation dialogs</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">173</context>
|
<context context-type="linenumber">194</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6906812245033969309" datatype="html">
|
<trans-unit id="6906812245033969309" datatype="html">
|
||||||
<source>Deleting documents will always ask for confirmation.</source>
|
<source>Deleting documents will always ask for confirmation.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">173</context>
|
<context context-type="linenumber">194</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="290238406234356122" datatype="html">
|
<trans-unit id="290238406234356122" datatype="html">
|
||||||
<source>Apply on close</source>
|
<source>Apply on close</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">174</context>
|
<context context-type="linenumber">195</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8104421162933956065" datatype="html">
|
<trans-unit id="8104421162933956065" datatype="html">
|
||||||
<source>Notes</source>
|
<source>Notes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">178</context>
|
<context context-type="linenumber">199</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@ -939,14 +954,14 @@
|
|||||||
<source>Enable notes</source>
|
<source>Enable notes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">182</context>
|
<context context-type="linenumber">203</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7314814725704332646" datatype="html">
|
<trans-unit id="7314814725704332646" datatype="html">
|
||||||
<source>Permissions</source>
|
<source>Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">190</context>
|
<context context-type="linenumber">211</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
||||||
@ -1001,28 +1016,28 @@
|
|||||||
<source>Default Permissions</source>
|
<source>Default Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">193</context>
|
<context context-type="linenumber">214</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8222269449891326545" datatype="html">
|
<trans-unit id="8222269449891326545" datatype="html">
|
||||||
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI </source>
|
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI </source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">197,199</context>
|
<context context-type="linenumber">218,220</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4292903881380648974" datatype="html">
|
<trans-unit id="4292903881380648974" datatype="html">
|
||||||
<source>Default Owner</source>
|
<source>Default Owner</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">204</context>
|
<context context-type="linenumber">225</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="734147282056744882" datatype="html">
|
<trans-unit id="734147282056744882" datatype="html">
|
||||||
<source>Objects without an owner can be viewed and edited by all users</source>
|
<source>Objects without an owner can be viewed and edited by all users</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">208</context>
|
<context context-type="linenumber">229</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@ -1033,18 +1048,18 @@
|
|||||||
<source>Default View Permissions</source>
|
<source>Default View Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">213</context>
|
<context context-type="linenumber">234</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2191775412581217688" datatype="html">
|
<trans-unit id="2191775412581217688" datatype="html">
|
||||||
<source>Users:</source>
|
<source>Users:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">218</context>
|
<context context-type="linenumber">239</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">245</context>
|
<context context-type="linenumber">266</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@ -1067,11 +1082,11 @@
|
|||||||
<source>Groups:</source>
|
<source>Groups:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">228</context>
|
<context context-type="linenumber">249</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">255</context>
|
<context context-type="linenumber">276</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@ -1094,14 +1109,14 @@
|
|||||||
<source>Default Edit Permissions</source>
|
<source>Default Edit Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">240</context>
|
<context context-type="linenumber">261</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3728984448750213892" datatype="html">
|
<trans-unit id="3728984448750213892" datatype="html">
|
||||||
<source>Edit permissions also grant viewing permissions</source>
|
<source>Edit permissions also grant viewing permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">264</context>
|
<context context-type="linenumber">285</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@ -1116,56 +1131,56 @@
|
|||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">272</context>
|
<context context-type="linenumber">293</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8545554728558600606" datatype="html">
|
<trans-unit id="8545554728558600606" datatype="html">
|
||||||
<source>Document processing</source>
|
<source>Document processing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">275</context>
|
<context context-type="linenumber">296</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3656786776644872398" datatype="html">
|
<trans-unit id="3656786776644872398" datatype="html">
|
||||||
<source>Show notifications when new documents are detected</source>
|
<source>Show notifications when new documents are detected</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">279</context>
|
<context context-type="linenumber">300</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6057053428592387613" datatype="html">
|
<trans-unit id="6057053428592387613" datatype="html">
|
||||||
<source>Show notifications when document processing completes successfully</source>
|
<source>Show notifications when document processing completes successfully</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">280</context>
|
<context context-type="linenumber">301</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="370315664367425513" datatype="html">
|
<trans-unit id="370315664367425513" datatype="html">
|
||||||
<source>Show notifications when document processing fails</source>
|
<source>Show notifications when document processing fails</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">281</context>
|
<context context-type="linenumber">302</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6838309441164918531" datatype="html">
|
<trans-unit id="6838309441164918531" datatype="html">
|
||||||
<source>Suppress notifications on dashboard</source>
|
<source>Suppress notifications on dashboard</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">282</context>
|
<context context-type="linenumber">303</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2741919327232918179" datatype="html">
|
<trans-unit id="2741919327232918179" datatype="html">
|
||||||
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">282</context>
|
<context context-type="linenumber">303</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="472206565520537964" datatype="html">
|
<trans-unit id="472206565520537964" datatype="html">
|
||||||
<source>Saved views</source>
|
<source>Saved views</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">290</context>
|
<context context-type="linenumber">311</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||||
@ -1176,14 +1191,14 @@
|
|||||||
<source>Show warning when closing saved views with unsaved changes</source>
|
<source>Show warning when closing saved views with unsaved changes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">296</context>
|
<context context-type="linenumber">317</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2123659921722214537" datatype="html">
|
<trans-unit id="2123659921722214537" datatype="html">
|
||||||
<source>Views</source>
|
<source>Views</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">300</context>
|
<context context-type="linenumber">321</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@ -1194,7 +1209,7 @@
|
|||||||
<source>Name</source>
|
<source>Name</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">306</context>
|
<context context-type="linenumber">327</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
@ -1301,14 +1316,14 @@
|
|||||||
<source> <x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="visually-hidden">"/>Appears on<x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/></source>
|
<source> <x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="visually-hidden">"/>Appears on<x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">310,311</context>
|
<context context-type="linenumber">331,332</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4104807402967139762" datatype="html">
|
<trans-unit id="4104807402967139762" datatype="html">
|
||||||
<source>Show on dashboard</source>
|
<source>Show on dashboard</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">313</context>
|
<context context-type="linenumber">334</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context>
|
||||||
@ -1319,7 +1334,7 @@
|
|||||||
<source>Show in sidebar</source>
|
<source>Show in sidebar</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">317</context>
|
<context context-type="linenumber">338</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html</context>
|
||||||
@ -1330,7 +1345,7 @@
|
|||||||
<source>Actions</source>
|
<source>Actions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">321</context>
|
<context context-type="linenumber">342</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
@ -1393,7 +1408,7 @@
|
|||||||
<source>Delete</source>
|
<source>Delete</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">324</context>
|
<context context-type="linenumber">345</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.html</context>
|
||||||
@ -1504,28 +1519,28 @@
|
|||||||
<source>No saved views defined.</source>
|
<source>No saved views defined.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">336</context>
|
<context context-type="linenumber">357</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6839066544204061364" datatype="html">
|
<trans-unit id="6839066544204061364" datatype="html">
|
||||||
<source>Use system language</source>
|
<source>Use system language</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">51</context>
|
<context context-type="linenumber">61</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7729897675462249787" datatype="html">
|
<trans-unit id="7729897675462249787" datatype="html">
|
||||||
<source>Use date format of display language</source>
|
<source>Use date format of display language</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">54</context>
|
<context context-type="linenumber">64</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1235706724900303689" datatype="html">
|
<trans-unit id="1235706724900303689" datatype="html">
|
||||||
<source>Error retrieving users</source>
|
<source>Error retrieving users</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">159</context>
|
<context context-type="linenumber">183</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||||
@ -1536,7 +1551,7 @@
|
|||||||
<source>Error retrieving groups</source>
|
<source>Error retrieving groups</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">178</context>
|
<context context-type="linenumber">202</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||||
@ -1547,35 +1562,35 @@
|
|||||||
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source>
|
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">380</context>
|
<context context-type="linenumber">415</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7217000812750597833" datatype="html">
|
<trans-unit id="7217000812750597833" datatype="html">
|
||||||
<source>Settings were saved successfully.</source>
|
<source>Settings were saved successfully.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">506</context>
|
<context context-type="linenumber">541</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="525012668859298131" datatype="html">
|
<trans-unit id="525012668859298131" datatype="html">
|
||||||
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
|
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">510</context>
|
<context context-type="linenumber">545</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8491974984518503778" datatype="html">
|
<trans-unit id="8491974984518503778" datatype="html">
|
||||||
<source>Reload now</source>
|
<source>Reload now</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">511</context>
|
<context context-type="linenumber">546</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3011185103048412841" datatype="html">
|
<trans-unit id="3011185103048412841" datatype="html">
|
||||||
<source>An error occurred while saving settings.</source>
|
<source>An error occurred while saving settings.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">521</context>
|
<context context-type="linenumber">556</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||||
@ -1586,7 +1601,7 @@
|
|||||||
<source>Error while storing settings on server.</source>
|
<source>Error while storing settings on server.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">555</context>
|
<context context-type="linenumber">590</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2991443309752293110" datatype="html">
|
<trans-unit id="2991443309752293110" datatype="html">
|
||||||
@ -4075,6 +4090,10 @@
|
|||||||
<context context-type="sourcefile">src/app/components/common/permissions-select/permissions-select.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/permissions-select/permissions-select.component.html</context>
|
||||||
<context context-type="linenumber">5</context>
|
<context context-type="linenumber">5</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">45</context>
|
||||||
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1230154438678955604" datatype="html">
|
<trans-unit id="1230154438678955604" datatype="html">
|
||||||
<source>Change</source>
|
<source>Change</source>
|
||||||
@ -4139,6 +4158,10 @@
|
|||||||
<context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context>
|
||||||
<context context-type="linenumber">29</context>
|
<context context-type="linenumber">29</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">152</context>
|
||||||
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="595732867213154214" datatype="html">
|
<trans-unit id="595732867213154214" datatype="html">
|
||||||
<source>Regenerate auth token</source>
|
<source>Regenerate auth token</source>
|
||||||
@ -4370,8 +4393,68 @@
|
|||||||
<context context-type="linenumber">151</context>
|
<context context-type="linenumber">151</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="9180110319941008393" datatype="html">
|
||||||
|
<source>Environment</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">18</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5973078531069712831" datatype="html">
|
||||||
|
<source>Paperless-ngx Version</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">22</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6269705781013540301" datatype="html">
|
||||||
|
<source>Install Type</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">24</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7962174670320694437" datatype="html">
|
||||||
|
<source>Server OS</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">26</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2903495470702110128" datatype="html">
|
||||||
|
<source>Media Storage</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">28</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2571831784751497241" datatype="html">
|
||||||
|
<source>available</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">31</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6489441800790477240" datatype="html">
|
||||||
|
<source>total</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">31</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="4198035112366277884" datatype="html">
|
||||||
|
<source>Database</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">41</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="5611592591303869712" datatype="html">
|
<trans-unit id="5611592591303869712" datatype="html">
|
||||||
<source>Status</source>
|
<source>Status</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">47</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/toasts/toasts.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/toasts/toasts.component.html</context>
|
||||||
<context context-type="linenumber">26</context>
|
<context context-type="linenumber">26</context>
|
||||||
@ -4381,6 +4464,76 @@
|
|||||||
<context context-type="linenumber">19</context>
|
<context context-type="linenumber">19</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="2256165083739630668" datatype="html">
|
||||||
|
<source>Migration Status</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">56</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7881311375431899727" datatype="html">
|
||||||
|
<source>Latest Migration</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">64</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="4632965004151576238" datatype="html">
|
||||||
|
<source>Pending Migrations</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">66</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6904866445262015585" datatype="html">
|
||||||
|
<source>Tasks</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">83</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6911698235105017958" datatype="html">
|
||||||
|
<source>Redis Status</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">87</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5349496739889768589" datatype="html">
|
||||||
|
<source>Celery Status</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">96</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="31377277941774469" datatype="html">
|
||||||
|
<source>Search Index</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">105</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="4089509911694721896" datatype="html">
|
||||||
|
<source>Last Updated</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">119</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="46628344485199198" datatype="html">
|
||||||
|
<source>Classifier</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">121</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6096684179126491743" datatype="html">
|
||||||
|
<source>Last Trained</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">135</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="6732151329960766506" datatype="html">
|
<trans-unit id="6732151329960766506" datatype="html">
|
||||||
<source>Copy Raw Error</source>
|
<source>Copy Raw Error</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
|
23
src-ui/package-lock.json
generated
23
src-ui/package-lock.json
generated
@ -29,6 +29,7 @@
|
|||||||
"ngx-color": "^9.0.0",
|
"ngx-color": "^9.0.0",
|
||||||
"ngx-cookie-service": "^17.1.0",
|
"ngx-cookie-service": "^17.1.0",
|
||||||
"ngx-file-drop": "^16.0.0",
|
"ngx-file-drop": "^16.0.0",
|
||||||
|
"ngx-filesize": "^3.0.3",
|
||||||
"ngx-ui-tour-ng-bootstrap": "^14.0.2",
|
"ngx-ui-tour-ng-bootstrap": "^14.0.2",
|
||||||
"pdfjs-dist": "^3.11.174",
|
"pdfjs-dist": "^3.11.174",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
@ -9844,6 +9845,15 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filesize": {
|
||||||
|
"version": "9.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/filesize/-/filesize-9.0.11.tgz",
|
||||||
|
"integrity": "sha512-gTAiTtI0STpKa5xesyTA9hA3LX4ga8sm2nWRcffEa1L/5vQwb4mj2MdzMkoHoGv4QzfDshQZuYscQSf8c4TKOA==",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@ -14105,6 +14115,19 @@
|
|||||||
"@angular/core": ">=14.0.0"
|
"@angular/core": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ngx-filesize": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-filesize/-/ngx-filesize-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-qqP2p4WbbF7R+NXC9NqRQdAfWfMAYJ2Ijf4ezRCq7j3tPY6ybSP9AZ3FY1U7/95n1hmOJ2U5oY+oFb7LhHQRBw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": ">= 14.2.0 < 18.0.0",
|
||||||
|
"@angular/core": ">= 14.2.0 < 18.0.0",
|
||||||
|
"filesize": ">= 6.0.0 < 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ngx-ui-tour-core": {
|
"node_modules/ngx-ui-tour-core": {
|
||||||
"version": "12.0.1",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-12.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-ui-tour-core/-/ngx-ui-tour-core-12.0.1.tgz",
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
"ngx-color": "^9.0.0",
|
"ngx-color": "^9.0.0",
|
||||||
"ngx-cookie-service": "^17.1.0",
|
"ngx-cookie-service": "^17.1.0",
|
||||||
"ngx-file-drop": "^16.0.0",
|
"ngx-file-drop": "^16.0.0",
|
||||||
|
"ngx-filesize": "^3.0.3",
|
||||||
"ngx-ui-tour-ng-bootstrap": "^14.0.2",
|
"ngx-ui-tour-ng-bootstrap": "^14.0.2",
|
||||||
"pdfjs-dist": "^3.11.174",
|
"pdfjs-dist": "^3.11.174",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
@ -114,7 +114,10 @@ import { FileComponent } from './components/common/input/file/file.component'
|
|||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component'
|
import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component'
|
||||||
import { MonetaryComponent } from './components/common/input/monetary/monetary.component'
|
import { MonetaryComponent } from './components/common/input/monetary/monetary.component'
|
||||||
|
import { SystemStatusDialogComponent } from './components/common/system-status-dialog/system-status-dialog.component'
|
||||||
|
import { NgxFilesizeModule } from 'ngx-filesize'
|
||||||
import {
|
import {
|
||||||
|
airplane,
|
||||||
archive,
|
archive,
|
||||||
arrowCounterclockwise,
|
arrowCounterclockwise,
|
||||||
arrowDown,
|
arrowDown,
|
||||||
@ -129,12 +132,14 @@ import {
|
|||||||
boxes,
|
boxes,
|
||||||
calendar,
|
calendar,
|
||||||
calendarEvent,
|
calendarEvent,
|
||||||
|
cardChecklist,
|
||||||
caretDown,
|
caretDown,
|
||||||
caretUp,
|
caretUp,
|
||||||
chatLeftText,
|
chatLeftText,
|
||||||
check,
|
check,
|
||||||
check2All,
|
check2All,
|
||||||
checkAll,
|
checkAll,
|
||||||
|
checkCircleFill,
|
||||||
checkLg,
|
checkLg,
|
||||||
chevronDoubleLeft,
|
chevronDoubleLeft,
|
||||||
chevronDoubleRight,
|
chevronDoubleRight,
|
||||||
@ -148,7 +153,9 @@ import {
|
|||||||
doorOpen,
|
doorOpen,
|
||||||
download,
|
download,
|
||||||
envelope,
|
envelope,
|
||||||
|
exclamationCircleFill,
|
||||||
exclamationTriangle,
|
exclamationTriangle,
|
||||||
|
exclamationTriangleFill,
|
||||||
eye,
|
eye,
|
||||||
fileEarmark,
|
fileEarmark,
|
||||||
fileEarmarkCheck,
|
fileEarmarkCheck,
|
||||||
@ -200,6 +207,7 @@ import {
|
|||||||
} from 'ngx-bootstrap-icons'
|
} from 'ngx-bootstrap-icons'
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
|
airplane,
|
||||||
archive,
|
archive,
|
||||||
arrowCounterclockwise,
|
arrowCounterclockwise,
|
||||||
arrowDown,
|
arrowDown,
|
||||||
@ -214,12 +222,14 @@ const icons = {
|
|||||||
boxes,
|
boxes,
|
||||||
calendar,
|
calendar,
|
||||||
calendarEvent,
|
calendarEvent,
|
||||||
|
cardChecklist,
|
||||||
caretDown,
|
caretDown,
|
||||||
caretUp,
|
caretUp,
|
||||||
chatLeftText,
|
chatLeftText,
|
||||||
check,
|
check,
|
||||||
check2All,
|
check2All,
|
||||||
checkAll,
|
checkAll,
|
||||||
|
checkCircleFill,
|
||||||
checkLg,
|
checkLg,
|
||||||
chevronDoubleLeft,
|
chevronDoubleLeft,
|
||||||
chevronDoubleRight,
|
chevronDoubleRight,
|
||||||
@ -233,7 +243,9 @@ const icons = {
|
|||||||
doorOpen,
|
doorOpen,
|
||||||
download,
|
download,
|
||||||
envelope,
|
envelope,
|
||||||
|
exclamationCircleFill,
|
||||||
exclamationTriangle,
|
exclamationTriangle,
|
||||||
|
exclamationTriangleFill,
|
||||||
eye,
|
eye,
|
||||||
fileEarmark,
|
fileEarmark,
|
||||||
fileEarmarkCheck,
|
fileEarmarkCheck,
|
||||||
@ -445,6 +457,7 @@ function initializeApp(settings: SettingsService) {
|
|||||||
FileComponent,
|
FileComponent,
|
||||||
ConfirmButtonComponent,
|
ConfirmButtonComponent,
|
||||||
MonetaryComponent,
|
MonetaryComponent,
|
||||||
|
SystemStatusDialogComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -459,6 +472,7 @@ function initializeApp(settings: SettingsService) {
|
|||||||
TourNgBootstrapModule,
|
TourNgBootstrapModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
NgxBootstrapIconsModule.pick(icons),
|
NgxBootstrapIconsModule.pick(icons),
|
||||||
|
NgxFilesizeModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -4,10 +4,31 @@
|
|||||||
info="Options to customize appearance, notifications, saved views and more. Settings apply to the <strong>current user only</strong>."
|
info="Options to customize appearance, notifications, saved views and more. Settings apply to the <strong>current user only</strong>."
|
||||||
i18n-info
|
i18n-info
|
||||||
>
|
>
|
||||||
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"><ng-container i18n>Start tour</ng-container></button>
|
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()">
|
||||||
|
<i-bs class="me-1" name="airplane"></i-bs> <ng-container i18n>Start tour</ng-container>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-primary position-relative ms-5" (click)="showSystemStatus()"
|
||||||
|
[disabled]="!systemStatus"
|
||||||
|
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }">
|
||||||
|
@if (!systemStatus) {
|
||||||
|
<div class="spinner-border spinner-border-sm me-1 h-75" role="status"></div>
|
||||||
|
} @else {
|
||||||
|
<i-bs class="me-2" name="card-checklist"></i-bs>
|
||||||
|
@if (systemStatusHasErrors) {
|
||||||
|
<span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0">
|
||||||
|
<i-bs name="exclamation-circle-fill" class="text-danger" width="1.75em" height="1.75em"></i-bs>
|
||||||
|
</span>
|
||||||
|
} @else {
|
||||||
|
<span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0">
|
||||||
|
<i-bs name="check-circle-fill" class="text-primary" width="1.75em" height="1.75em"></i-bs>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<ng-container i18n>System Status</ng-container>
|
||||||
|
</button>
|
||||||
<a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary ms-3" href="admin/" target="_blank">
|
<a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary ms-3" href="admin/" target="_blank">
|
||||||
<ng-container i18n>Open Django Admin</ng-container>
|
<ng-container i18n>Open Django Admin</ng-container>
|
||||||
<i-bs name="arrow-up-right"></i-bs>
|
<i-bs name="arrow-up-right"></i-bs>
|
||||||
</a>
|
</a>
|
||||||
</pngx-page-header>
|
</pngx-page-header>
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
NgbModule,
|
NgbModule,
|
||||||
NgbAlertModule,
|
NgbAlertModule,
|
||||||
NgbNavLink,
|
NgbNavLink,
|
||||||
|
NgbModal,
|
||||||
|
NgbModalModule,
|
||||||
} from '@ng-bootstrap/ng-bootstrap'
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgSelectModule } from '@ng-select/ng-select'
|
import { NgSelectModule } from '@ng-select/ng-select'
|
||||||
import { of, throwError } from 'rxjs'
|
import { of, throwError } from 'rxjs'
|
||||||
@ -39,6 +41,13 @@ import { SettingsComponent } from './settings.component'
|
|||||||
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
||||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||||
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
||||||
|
import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component'
|
||||||
|
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||||
|
import {
|
||||||
|
SystemStatus,
|
||||||
|
InstallType,
|
||||||
|
SystemStatusItemStatus,
|
||||||
|
} from 'src/app/data/system-status'
|
||||||
|
|
||||||
const savedViews = [
|
const savedViews = [
|
||||||
{ id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true },
|
{ id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true },
|
||||||
@ -65,6 +74,8 @@ describe('SettingsComponent', () => {
|
|||||||
let userService: UserService
|
let userService: UserService
|
||||||
let permissionsService: PermissionsService
|
let permissionsService: PermissionsService
|
||||||
let groupService: GroupService
|
let groupService: GroupService
|
||||||
|
let modalService: NgbModal
|
||||||
|
let systemStatusService: SystemStatusService
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@ -96,6 +107,7 @@ describe('SettingsComponent', () => {
|
|||||||
NgbAlertModule,
|
NgbAlertModule,
|
||||||
NgSelectModule,
|
NgSelectModule,
|
||||||
NgxBootstrapIconsModule.pick(allIcons),
|
NgxBootstrapIconsModule.pick(allIcons),
|
||||||
|
NgbModalModule,
|
||||||
],
|
],
|
||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
@ -107,6 +119,8 @@ describe('SettingsComponent', () => {
|
|||||||
settingsService.currentUser = users[0]
|
settingsService.currentUser = users[0]
|
||||||
userService = TestBed.inject(UserService)
|
userService = TestBed.inject(UserService)
|
||||||
permissionsService = TestBed.inject(PermissionsService)
|
permissionsService = TestBed.inject(PermissionsService)
|
||||||
|
modalService = TestBed.inject(NgbModal)
|
||||||
|
systemStatusService = TestBed.inject(SystemStatusService)
|
||||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||||
jest
|
jest
|
||||||
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
|
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
|
||||||
@ -372,4 +386,54 @@ describe('SettingsComponent', () => {
|
|||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(toastErrorSpy).toBeCalled()
|
expect(toastErrorSpy).toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should load system status on initialize, show errors if needed', () => {
|
||||||
|
const status: SystemStatus = {
|
||||||
|
pngx_version: '2.4.3',
|
||||||
|
server_os: 'macOS-14.1.1-arm64-arm-64bit',
|
||||||
|
install_type: InstallType.BareMetal,
|
||||||
|
storage: { total: 494384795648, available: 13573525504 },
|
||||||
|
database: {
|
||||||
|
type: 'sqlite',
|
||||||
|
url: '/paperless-ngx/data/db.sqlite3',
|
||||||
|
status: SystemStatusItemStatus.ERROR,
|
||||||
|
error: null,
|
||||||
|
migration_status: {
|
||||||
|
latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data',
|
||||||
|
unapplied_migrations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tasks: {
|
||||||
|
redis_url: 'redis://localhost:6379',
|
||||||
|
redis_status: SystemStatusItemStatus.ERROR,
|
||||||
|
redis_error:
|
||||||
|
'Error 61 connecting to localhost:6379. Connection refused.',
|
||||||
|
celery_status: SystemStatusItemStatus.ERROR,
|
||||||
|
index_status: SystemStatusItemStatus.OK,
|
||||||
|
index_last_modified: new Date().toISOString(),
|
||||||
|
index_error: null,
|
||||||
|
classifier_status: SystemStatusItemStatus.OK,
|
||||||
|
classifier_last_trained: new Date().toISOString(),
|
||||||
|
classifier_error: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status))
|
||||||
|
completeSetup()
|
||||||
|
expect(component['systemStatus']).toEqual(status) // private
|
||||||
|
expect(component.systemStatusHasErrors).toBeTruthy()
|
||||||
|
// coverage
|
||||||
|
component['systemStatus'].database.status = SystemStatusItemStatus.OK
|
||||||
|
component['systemStatus'].tasks.redis_status = SystemStatusItemStatus.OK
|
||||||
|
component['systemStatus'].tasks.celery_status = SystemStatusItemStatus.OK
|
||||||
|
expect(component.systemStatusHasErrors).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should open system status dialog', () => {
|
||||||
|
const modalOpenSpy = jest.spyOn(modalService, 'open')
|
||||||
|
completeSetup()
|
||||||
|
component.showSystemStatus()
|
||||||
|
expect(modalOpenSpy).toHaveBeenCalledWith(SystemStatusDialogComponent, {
|
||||||
|
size: 'xl',
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,11 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormGroup, FormControl } from '@angular/forms'
|
import { FormGroup, FormControl } from '@angular/forms'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { NgbNavChangeEvent } from '@ng-bootstrap/ng-bootstrap'
|
import {
|
||||||
|
NgbModal,
|
||||||
|
NgbModalRef,
|
||||||
|
NgbNavChangeEvent,
|
||||||
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
|
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
|
||||||
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||||
import {
|
import {
|
||||||
@ -40,6 +44,12 @@ import {
|
|||||||
} from 'src/app/services/settings.service'
|
} from 'src/app/services/settings.service'
|
||||||
import { ToastService, Toast } from 'src/app/services/toast.service'
|
import { ToastService, Toast } from 'src/app/services/toast.service'
|
||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||||
|
import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component'
|
||||||
|
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||||
|
import {
|
||||||
|
SystemStatusItemStatus,
|
||||||
|
SystemStatus,
|
||||||
|
} from 'src/app/data/system-status'
|
||||||
|
|
||||||
enum SettingsNavIDs {
|
enum SettingsNavIDs {
|
||||||
General = 1,
|
General = 1,
|
||||||
@ -111,6 +121,18 @@ export class SettingsComponent
|
|||||||
users: User[]
|
users: User[]
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
|
|
||||||
|
private systemStatus: SystemStatus
|
||||||
|
|
||||||
|
get systemStatusHasErrors(): boolean {
|
||||||
|
return (
|
||||||
|
this.systemStatus.database.status === SystemStatusItemStatus.ERROR ||
|
||||||
|
this.systemStatus.tasks.redis_status === SystemStatusItemStatus.ERROR ||
|
||||||
|
this.systemStatus.tasks.celery_status === SystemStatusItemStatus.ERROR ||
|
||||||
|
this.systemStatus.tasks.index_status === SystemStatusItemStatus.ERROR ||
|
||||||
|
this.systemStatus.tasks.classifier_status === SystemStatusItemStatus.ERROR
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
get computedDateLocale(): string {
|
get computedDateLocale(): string {
|
||||||
return (
|
return (
|
||||||
this.settingsForm.value.dateLocale ||
|
this.settingsForm.value.dateLocale ||
|
||||||
@ -131,7 +153,9 @@ export class SettingsComponent
|
|||||||
private usersService: UserService,
|
private usersService: UserService,
|
||||||
private groupsService: GroupService,
|
private groupsService: GroupService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
public permissionsService: PermissionsService
|
public permissionsService: PermissionsService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private systemStatusService: SystemStatusService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.settings.settingsSaved.subscribe(() => {
|
this.settings.settingsSaved.subscribe(() => {
|
||||||
@ -360,6 +384,17 @@ export class SettingsComponent
|
|||||||
// prevents loss of unsaved changes
|
// prevents loss of unsaved changes
|
||||||
this.settingsForm.patchValue(currentFormValue)
|
this.settingsForm.patchValue(currentFormValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.permissionsService.currentUserCan(
|
||||||
|
PermissionAction.View,
|
||||||
|
PermissionType.Admin
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.systemStatusService.get().subscribe((status) => {
|
||||||
|
this.systemStatus = status
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private emptyGroup(group: FormGroup) {
|
private emptyGroup(group: FormGroup) {
|
||||||
@ -565,4 +600,14 @@ export class SettingsComponent
|
|||||||
clearThemeColor() {
|
clearThemeColor() {
|
||||||
this.settingsForm.get('themeColor').patchValue('')
|
this.settingsForm.get('themeColor').patchValue('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showSystemStatus() {
|
||||||
|
const modal: NgbModalRef = this.modalService.open(
|
||||||
|
SystemStatusDialogComponent,
|
||||||
|
{
|
||||||
|
size: 'xl',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
modal.componentInstance.status = this.systemStatus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,154 @@
|
|||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modal-basic-title" i18n>System Status</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
@if (!status) {
|
||||||
|
<div class="w-100 h-100 d-flex align-items-center justify-content-center">
|
||||||
|
<div>
|
||||||
|
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||||
|
<ng-container i18n>Loading...</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div class="row row-cols-1 row-cols-md-3 g-3">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card bg-light h-100">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title mb-0" i18n>Environment</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="card-text">
|
||||||
|
<dt i18n>Paperless-ngx Version</dt>
|
||||||
|
<dd>{{status.pngx_version}}</dd>
|
||||||
|
<dt i18n>Install Type</dt>
|
||||||
|
<dd>{{status.install_type}}</dd>
|
||||||
|
<dt i18n>Server OS</dt>
|
||||||
|
<dd>{{status.server_os}}</dd>
|
||||||
|
<dt i18n>Media Storage</dt>
|
||||||
|
<dd>
|
||||||
|
<ngb-progressbar style="height: 4px;" class="mt-2 mb-1" type="primary" [max]="status.storage.total" [value]="status.storage.total - status.storage.available"></ngb-progressbar>
|
||||||
|
<span class="small">{{status.storage.available | filesize}} <ng-container i18n>available</ng-container> ({{status.storage.total | filesize}} <ng-container i18n>total</ng-container>)</span>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<div class="card bg-light h-100">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title mb-0" i18n>Database</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="card-text">
|
||||||
|
<dt i18n>Type</dt>
|
||||||
|
<dd>{{status.database.type}}</dd>
|
||||||
|
<dt i18n>Status</dt>
|
||||||
|
<dd class="d-flex align-items-center">
|
||||||
|
{{status.database.status}}
|
||||||
|
@if (status.database.status === 'OK') {
|
||||||
|
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" ngbPopover="{{status.database.url}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
} @else {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.database.url}}: {{status.database.error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
<dt i18n>Migration Status</dt>
|
||||||
|
<dd class="d-flex align-items-center">
|
||||||
|
@if (status.database.migration_status.unapplied_migrations.length === 0) {
|
||||||
|
<ng-container>Up to date</ng-container><i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="migrationStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
} @else {
|
||||||
|
<ng-container>{{status.database.migration_status.unapplied_migrations.length}} Pending</ng-container><i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="migrationStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
<ng-template #migrationStatus>
|
||||||
|
<h6><ng-container i18n>Latest Migration</ng-container>:</h6> <span class="font-monospace small">{{status.database.migration_status.latest_migration}}</span>
|
||||||
|
@if (status.database.migration_status.unapplied_migrations.length > 0) {
|
||||||
|
<h6 class="mt-3"><ng-container i18n>Pending Migrations</ng-container>:</h6>
|
||||||
|
<ul>
|
||||||
|
@for (migration of status.database.migration_status.unapplied_migrations; track migration) {
|
||||||
|
<li class="font-monospace small">{{migration}}</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<div class="card bg-light h-100">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title mb-0" i18n>Tasks</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="card-text">
|
||||||
|
<dt i18n>Redis Status</dt>
|
||||||
|
<dd class="d-flex align-items-center">
|
||||||
|
{{status.tasks.redis_status}}
|
||||||
|
@if (status.tasks.redis_status === 'OK') {
|
||||||
|
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" ngbPopover="{{status.tasks.redis_url}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
} @else {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.redis_url}}: {{status.tasks.redis_error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
<dt i18n>Celery Status</dt>
|
||||||
|
<dd class="d-flex align-items-center">
|
||||||
|
{{status.tasks.celery_status}}
|
||||||
|
@if (status.tasks.celery_status === 'OK') {
|
||||||
|
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1"></i-bs>
|
||||||
|
} @else {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1"></i-bs>
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
<dt i18n>Search Index</dt>
|
||||||
|
<dd class="d-flex align-items-center">
|
||||||
|
{{status.tasks.index_status}}
|
||||||
|
@if (status.tasks.index_status === 'OK') {
|
||||||
|
@if (isStale(status.tasks.index_last_modified)) {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="indexStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
} @else {
|
||||||
|
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="indexStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.index_error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
<ng-template #indexStatus>
|
||||||
|
<h6><ng-container i18n>Last Updated</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.index_last_modified | customDate:'medium'}}</span>
|
||||||
|
</ng-template>
|
||||||
|
<dt i18n>Classifier</dt>
|
||||||
|
<dd class="d-flex align-items-center">
|
||||||
|
{{status.tasks.classifier_status}}
|
||||||
|
@if (status.tasks.classifier_status === 'OK') {
|
||||||
|
@if (isStale(status.tasks.classifier_last_trained)) {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-warning ms-2 lh-1" [ngbPopover]="classifierStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
} @else {
|
||||||
|
<i-bs name="check-circle-fill" class="text-primary ms-2 lh-1" [ngbPopover]="classifierStatus" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
<i-bs name="exclamation-triangle-fill" class="text-danger ms-2 lh-1" ngbPopover="{{status.tasks.classifier_error}}" triggers="mouseenter:mouseleave"></i-bs>
|
||||||
|
}
|
||||||
|
</dd>
|
||||||
|
<ng-template #classifierStatus>
|
||||||
|
<h6><ng-container i18n>Last Trained</ng-container>:</h6> <span class="font-monospace small">{{status.tasks.classifier_last_trained | customDate:'medium'}}</span>
|
||||||
|
</ng-template>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-sm btn-outline-secondary" (click)="copy()">
|
||||||
|
@if (!copied) {
|
||||||
|
<i-bs name="clipboard-fill"></i-bs>
|
||||||
|
}
|
||||||
|
@if (copied) {
|
||||||
|
<i-bs name="clipboard-check-fill"></i-bs>
|
||||||
|
}
|
||||||
|
<ng-container i18n>Copy</ng-container>
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
TestBed,
|
||||||
|
fakeAsync,
|
||||||
|
tick,
|
||||||
|
} from '@angular/core/testing'
|
||||||
|
import {
|
||||||
|
NgbActiveModal,
|
||||||
|
NgbModalModule,
|
||||||
|
NgbPopoverModule,
|
||||||
|
NgbProgressbarModule,
|
||||||
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'
|
||||||
|
import { SystemStatusDialogComponent } from './system-status-dialog.component'
|
||||||
|
import {
|
||||||
|
SystemStatusItemStatus,
|
||||||
|
InstallType,
|
||||||
|
SystemStatus,
|
||||||
|
} from 'src/app/data/system-status'
|
||||||
|
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||||
|
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||||
|
import { NgxFilesizeModule } from 'ngx-filesize'
|
||||||
|
|
||||||
|
const status: SystemStatus = {
|
||||||
|
pngx_version: '2.4.3',
|
||||||
|
server_os: 'macOS-14.1.1-arm64-arm-64bit',
|
||||||
|
install_type: InstallType.BareMetal,
|
||||||
|
storage: { total: 494384795648, available: 13573525504 },
|
||||||
|
database: {
|
||||||
|
type: 'sqlite',
|
||||||
|
url: '/paperless-ngx/data/db.sqlite3',
|
||||||
|
status: SystemStatusItemStatus.ERROR,
|
||||||
|
error: null,
|
||||||
|
migration_status: {
|
||||||
|
latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data',
|
||||||
|
unapplied_migrations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tasks: {
|
||||||
|
redis_url: 'redis://localhost:6379',
|
||||||
|
redis_status: SystemStatusItemStatus.ERROR,
|
||||||
|
redis_error: 'Error 61 connecting to localhost:6379. Connection refused.',
|
||||||
|
celery_status: SystemStatusItemStatus.ERROR,
|
||||||
|
index_status: SystemStatusItemStatus.OK,
|
||||||
|
index_last_modified: new Date().toISOString(),
|
||||||
|
index_error: null,
|
||||||
|
classifier_status: SystemStatusItemStatus.OK,
|
||||||
|
classifier_last_trained: new Date().toISOString(),
|
||||||
|
classifier_error: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('SystemStatusDialogComponent', () => {
|
||||||
|
let component: SystemStatusDialogComponent
|
||||||
|
let fixture: ComponentFixture<SystemStatusDialogComponent>
|
||||||
|
let clipboard: Clipboard
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [SystemStatusDialogComponent],
|
||||||
|
providers: [NgbActiveModal],
|
||||||
|
imports: [
|
||||||
|
NgbModalModule,
|
||||||
|
ClipboardModule,
|
||||||
|
HttpClientTestingModule,
|
||||||
|
NgxBootstrapIconsModule.pick(allIcons),
|
||||||
|
NgxFilesizeModule,
|
||||||
|
NgbPopoverModule,
|
||||||
|
NgbProgressbarModule,
|
||||||
|
],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SystemStatusDialogComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
component.status = status
|
||||||
|
clipboard = TestBed.inject(Clipboard)
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should close the active modal', () => {
|
||||||
|
const closeSpy = jest.spyOn(component.activeModal, 'close')
|
||||||
|
component.close()
|
||||||
|
expect(closeSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should copy the system status to clipboard', fakeAsync(() => {
|
||||||
|
jest.spyOn(clipboard, 'copy')
|
||||||
|
component.copy()
|
||||||
|
expect(clipboard.copy).toHaveBeenCalledWith(
|
||||||
|
JSON.stringify(component.status)
|
||||||
|
)
|
||||||
|
expect(component.copied).toBeTruthy()
|
||||||
|
tick(3000)
|
||||||
|
expect(component.copied).toBeFalsy()
|
||||||
|
}))
|
||||||
|
|
||||||
|
it('should calculate if date is stale', () => {
|
||||||
|
const date = new Date()
|
||||||
|
date.setHours(date.getHours() - 25)
|
||||||
|
expect(component.isStale(date.toISOString())).toBeTruthy()
|
||||||
|
expect(component.isStale(date.toISOString(), 26)).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,39 @@
|
|||||||
|
import { Component, Input } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { SystemStatus } from 'src/app/data/system-status'
|
||||||
|
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||||
|
import { Clipboard } from '@angular/cdk/clipboard'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pngx-system-status-dialog',
|
||||||
|
templateUrl: './system-status-dialog.component.html',
|
||||||
|
styleUrl: './system-status-dialog.component.scss',
|
||||||
|
})
|
||||||
|
export class SystemStatusDialogComponent {
|
||||||
|
public status: SystemStatus
|
||||||
|
|
||||||
|
public copied: boolean = false
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public activeModal: NgbActiveModal,
|
||||||
|
private clipboard: Clipboard
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public close() {
|
||||||
|
this.activeModal.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
public copy() {
|
||||||
|
this.clipboard.copy(JSON.stringify(this.status))
|
||||||
|
this.copied = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copied = false
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
public isStale(dateStr: string, hours: number = 24): boolean {
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
const now = new Date()
|
||||||
|
return now.getTime() - date.getTime() > hours * 60 * 60 * 1000
|
||||||
|
}
|
||||||
|
}
|
41
src-ui/src/app/data/system-status.ts
Normal file
41
src-ui/src/app/data/system-status.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
export enum InstallType {
|
||||||
|
Containerized = 'containerized',
|
||||||
|
BareMetal = 'bare-metal',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SystemStatusItemStatus {
|
||||||
|
OK = 'OK',
|
||||||
|
ERROR = 'ERROR',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemStatus {
|
||||||
|
pngx_version: string
|
||||||
|
server_os: string
|
||||||
|
install_type: InstallType
|
||||||
|
storage: {
|
||||||
|
total: number
|
||||||
|
available: number
|
||||||
|
}
|
||||||
|
database: {
|
||||||
|
type: string
|
||||||
|
url: string
|
||||||
|
status: SystemStatusItemStatus
|
||||||
|
error?: string
|
||||||
|
migration_status: {
|
||||||
|
latest_migration: string
|
||||||
|
unapplied_migrations: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks: {
|
||||||
|
redis_url: string
|
||||||
|
redis_status: SystemStatusItemStatus
|
||||||
|
redis_error: string
|
||||||
|
celery_status: SystemStatusItemStatus
|
||||||
|
index_status: SystemStatusItemStatus
|
||||||
|
index_last_modified: string // ISO date string
|
||||||
|
index_error: string
|
||||||
|
classifier_status: SystemStatusItemStatus
|
||||||
|
classifier_last_trained: string // ISO date string
|
||||||
|
classifier_error: string
|
||||||
|
}
|
||||||
|
}
|
35
src-ui/src/app/services/system-status.service.spec.ts
Normal file
35
src-ui/src/app/services/system-status.service.spec.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { SystemStatusService } from './system-status.service'
|
||||||
|
import {
|
||||||
|
HttpClientTestingModule,
|
||||||
|
HttpTestingController,
|
||||||
|
} from '@angular/common/http/testing'
|
||||||
|
import { environment } from 'src/environments/environment'
|
||||||
|
|
||||||
|
describe('SystemStatusService', () => {
|
||||||
|
let httpTestingController: HttpTestingController
|
||||||
|
let service: SystemStatusService
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [SystemStatusService],
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
})
|
||||||
|
|
||||||
|
httpTestingController = TestBed.inject(HttpTestingController)
|
||||||
|
service = TestBed.inject(SystemStatusService)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
httpTestingController.verify()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls get status endpoint', () => {
|
||||||
|
service.get().subscribe()
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
`${environment.apiBaseUrl}status/`
|
||||||
|
)
|
||||||
|
expect(req.request.method).toEqual('GET')
|
||||||
|
})
|
||||||
|
})
|
20
src-ui/src/app/services/system-status.service.ts
Normal file
20
src-ui/src/app/services/system-status.service.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http'
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Observable } from 'rxjs'
|
||||||
|
import { SystemStatus } from '../data/system-status'
|
||||||
|
import { environment } from 'src/environments/environment'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class SystemStatusService {
|
||||||
|
private endpoint = 'status'
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
get(): Observable<SystemStatus> {
|
||||||
|
return this.http.get<SystemStatus>(
|
||||||
|
`${environment.apiBaseUrl}${this.endpoint}/`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
186
src/documents/tests/test_api_status.py
Normal file
186
src/documents/tests/test_api_status.py
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.test import override_settings
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from documents.classifier import DocumentClassifier
|
||||||
|
from documents.classifier import load_classifier
|
||||||
|
from paperless import version
|
||||||
|
|
||||||
|
|
||||||
|
class TestSystemStatus(APITestCase):
|
||||||
|
ENDPOINT = "/api/status/"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.user = User.objects.create_superuser(
|
||||||
|
username="temp_admin",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_system_status(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- A user is logged in
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains relevant system status information
|
||||||
|
"""
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["pngx_version"], version.__full_version_str__)
|
||||||
|
self.assertIsNotNone(response.data["server_os"])
|
||||||
|
self.assertEqual(response.data["install_type"], "bare-metal")
|
||||||
|
self.assertIsNotNone(response.data["storage"]["total"])
|
||||||
|
self.assertIsNotNone(response.data["storage"]["available"])
|
||||||
|
self.assertEqual(response.data["database"]["type"], "sqlite")
|
||||||
|
self.assertIsNotNone(response.data["database"]["url"])
|
||||||
|
self.assertEqual(response.data["database"]["status"], "OK")
|
||||||
|
self.assertIsNone(response.data["database"]["error"])
|
||||||
|
self.assertIsNotNone(response.data["database"]["migration_status"])
|
||||||
|
self.assertEqual(response.data["tasks"]["redis_url"], "redis://localhost:6379")
|
||||||
|
self.assertEqual(response.data["tasks"]["redis_status"], "ERROR")
|
||||||
|
self.assertIsNotNone(response.data["tasks"]["redis_error"])
|
||||||
|
|
||||||
|
def test_system_status_insufficient_permissions(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- A user is not logged in or does not have permissions
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains a 401 status code or a 403 status code
|
||||||
|
"""
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||||
|
normal_user = User.objects.create_user(username="normal_user")
|
||||||
|
self.client.force_login(normal_user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_system_status_container_detection(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- The application is running in a containerized environment
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains the correct install type
|
||||||
|
"""
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
os.environ["PNGX_CONTAINERIZED"] = "1"
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["install_type"], "docker")
|
||||||
|
os.environ["KUBERNETES_SERVICE_HOST"] = "http://localhost"
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.data["install_type"], "kubernetes")
|
||||||
|
|
||||||
|
@mock.patch("redis.Redis.execute_command")
|
||||||
|
def test_system_status_redis_ping(self, mock_ping):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- Redies ping returns True
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains the correct redis status
|
||||||
|
"""
|
||||||
|
mock_ping.return_value = True
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["tasks"]["redis_status"], "OK")
|
||||||
|
|
||||||
|
@mock.patch("celery.app.control.Inspect.ping")
|
||||||
|
def test_system_status_celery_ping(self, mock_ping):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- Celery ping returns pong
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains the correct celery status
|
||||||
|
"""
|
||||||
|
mock_ping.return_value = {"hostname": {"ok": "pong"}}
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["tasks"]["celery_status"], "OK")
|
||||||
|
|
||||||
|
@override_settings(INDEX_DIR=Path("/tmp/index"))
|
||||||
|
@mock.patch("whoosh.index.FileIndex.last_modified")
|
||||||
|
def test_system_status_index_ok(self, mock_last_modified):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- The index last modified time is set
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains the correct index status
|
||||||
|
"""
|
||||||
|
mock_last_modified.return_value = 1707839087
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["tasks"]["index_status"], "OK")
|
||||||
|
self.assertIsNotNone(response.data["tasks"]["index_last_modified"])
|
||||||
|
|
||||||
|
@override_settings(INDEX_DIR="/tmp/index/")
|
||||||
|
@mock.patch("documents.index.open_index", autospec=True)
|
||||||
|
def test_system_status_index_error(self, mock_open_index):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- The index is not found
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains the correct index status
|
||||||
|
"""
|
||||||
|
mock_open_index.return_value = None
|
||||||
|
mock_open_index.side_effect = Exception("Index error")
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
mock_open_index.assert_called_once()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["tasks"]["index_status"], "ERROR")
|
||||||
|
self.assertIsNotNone(response.data["tasks"]["index_error"])
|
||||||
|
|
||||||
|
@override_settings(DATA_DIR="/tmp/does_not_exist/data/")
|
||||||
|
def test_system_status_classifier_ok(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- The classifier is found
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains the correct classifier status
|
||||||
|
"""
|
||||||
|
load_classifier()
|
||||||
|
test_classifier = DocumentClassifier()
|
||||||
|
test_classifier.save()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["tasks"]["classifier_status"], "OK")
|
||||||
|
self.assertIsNone(response.data["tasks"]["classifier_error"])
|
||||||
|
|
||||||
|
def test_system_status_classifier_error(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- The classifier is not found
|
||||||
|
WHEN:
|
||||||
|
- The user requests the system status
|
||||||
|
THEN:
|
||||||
|
- The response contains an error classifier status
|
||||||
|
"""
|
||||||
|
with override_settings(MODEL_FILE="does_not_exist"):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(self.ENDPOINT)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(response.data["tasks"]["classifier_status"], "ERROR")
|
||||||
|
self.assertIsNotNone(response.data["tasks"]["classifier_error"])
|
@ -2,6 +2,7 @@ import itertools
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import urllib
|
import urllib
|
||||||
@ -13,8 +14,12 @@ from unicodedata import normalize
|
|||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.db import connections
|
||||||
|
from django.db.migrations.loader import MigrationLoader
|
||||||
|
from django.db.migrations.recorder import MigrationRecorder
|
||||||
from django.db.models import Case
|
from django.db.models import Case
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.db.models import IntegerField
|
from django.db.models import IntegerField
|
||||||
@ -31,6 +36,7 @@ from django.http import HttpResponseRedirect
|
|||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.timezone import make_aware
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.decorators.cache import cache_control
|
from django.views.decorators.cache import cache_control
|
||||||
@ -40,6 +46,7 @@ from django.views.generic import TemplateView
|
|||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from langdetect import detect
|
from langdetect import detect
|
||||||
from packaging import version as packaging_version
|
from packaging import version as packaging_version
|
||||||
|
from redis import Redis
|
||||||
from rest_framework import parsers
|
from rest_framework import parsers
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
@ -61,6 +68,7 @@ from rest_framework.viewsets import ReadOnlyModelViewSet
|
|||||||
from rest_framework.viewsets import ViewSet
|
from rest_framework.viewsets import ViewSet
|
||||||
|
|
||||||
from documents import bulk_edit
|
from documents import bulk_edit
|
||||||
|
from documents import index
|
||||||
from documents.bulk_download import ArchiveOnlyStrategy
|
from documents.bulk_download import ArchiveOnlyStrategy
|
||||||
from documents.bulk_download import OriginalAndArchiveStrategy
|
from documents.bulk_download import OriginalAndArchiveStrategy
|
||||||
from documents.bulk_download import OriginalsOnlyStrategy
|
from documents.bulk_download import OriginalsOnlyStrategy
|
||||||
@ -138,6 +146,7 @@ from documents.serialisers import WorkflowTriggerSerializer
|
|||||||
from documents.signals import document_updated
|
from documents.signals import document_updated
|
||||||
from documents.tasks import consume_file
|
from documents.tasks import consume_file
|
||||||
from paperless import version
|
from paperless import version
|
||||||
|
from paperless.celery import app as celery_app
|
||||||
from paperless.config import GeneralConfig
|
from paperless.config import GeneralConfig
|
||||||
from paperless.db import GnuPG
|
from paperless.db import GnuPG
|
||||||
from paperless.views import StandardPagination
|
from paperless.views import StandardPagination
|
||||||
@ -1539,3 +1548,132 @@ class CustomFieldViewSet(ModelViewSet):
|
|||||||
model = CustomField
|
model = CustomField
|
||||||
|
|
||||||
queryset = CustomField.objects.all().order_by("-created")
|
queryset = CustomField.objects.all().order_by("-created")
|
||||||
|
|
||||||
|
|
||||||
|
class SystemStatusView(GenericAPIView, PassUserMixin):
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
def get(self, request, format=None):
|
||||||
|
if not request.user.has_perm("admin.view_logentry"):
|
||||||
|
return HttpResponseForbidden("Insufficient permissions")
|
||||||
|
|
||||||
|
current_version = version.__full_version_str__
|
||||||
|
|
||||||
|
install_type = "bare-metal"
|
||||||
|
if os.environ.get("KUBERNETES_SERVICE_HOST") is not None:
|
||||||
|
install_type = "kubernetes"
|
||||||
|
elif os.environ.get("PNGX_CONTAINERIZED") == "1":
|
||||||
|
install_type = "docker"
|
||||||
|
|
||||||
|
db_conn = connections["default"]
|
||||||
|
db_url = db_conn.settings_dict["NAME"]
|
||||||
|
db_error = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_conn.ensure_connection()
|
||||||
|
db_status = "OK"
|
||||||
|
loader = MigrationLoader(connection=db_conn)
|
||||||
|
all_migrations = [f"{app}.{name}" for app, name in loader.graph.nodes]
|
||||||
|
applied_migrations = [
|
||||||
|
f"{m.app}.{m.name}"
|
||||||
|
for m in MigrationRecorder.Migration.objects.all().order_by("id")
|
||||||
|
]
|
||||||
|
except Exception as e: # pragma: no cover
|
||||||
|
applied_migrations = []
|
||||||
|
db_status = "ERROR"
|
||||||
|
logger.exception(f"System status error connecting to database: {e}")
|
||||||
|
db_error = "Error connecting to database, check logs for more detail."
|
||||||
|
|
||||||
|
media_stats = os.statvfs(settings.MEDIA_ROOT)
|
||||||
|
|
||||||
|
redis_url = settings._CHANNELS_REDIS_URL
|
||||||
|
redis_error = None
|
||||||
|
with Redis.from_url(url=redis_url) as client:
|
||||||
|
try:
|
||||||
|
client.ping()
|
||||||
|
redis_status = "OK"
|
||||||
|
except Exception as e:
|
||||||
|
redis_status = "ERROR"
|
||||||
|
logger.exception(f"System status error connecting to redis: {e}")
|
||||||
|
redis_error = "Error connecting to redis, check logs for more detail."
|
||||||
|
|
||||||
|
try:
|
||||||
|
celery_ping = celery_app.control.inspect().ping()
|
||||||
|
first_worker_ping = celery_ping[next(iter(celery_ping.keys()))]
|
||||||
|
if first_worker_ping["ok"] == "pong":
|
||||||
|
celery_active = "OK"
|
||||||
|
except Exception:
|
||||||
|
celery_active = "ERROR"
|
||||||
|
|
||||||
|
index_error = None
|
||||||
|
try:
|
||||||
|
ix = index.open_index()
|
||||||
|
index_status = "OK"
|
||||||
|
index_last_modified = make_aware(
|
||||||
|
datetime.fromtimestamp(ix.last_modified()),
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
index_status = "ERROR"
|
||||||
|
index_error = "Error opening index, check logs for more detail."
|
||||||
|
logger.exception(f"System status error opening index: {e}")
|
||||||
|
index_last_modified = None
|
||||||
|
|
||||||
|
classifier_error = None
|
||||||
|
try:
|
||||||
|
classifier = load_classifier()
|
||||||
|
if classifier is None:
|
||||||
|
raise Exception("Classifier not loaded")
|
||||||
|
classifier_status = "OK"
|
||||||
|
task_result_model = apps.get_model("django_celery_results", "taskresult")
|
||||||
|
result = (
|
||||||
|
task_result_model.objects.filter(
|
||||||
|
task_name="documents.tasks.train_classifier",
|
||||||
|
status="SUCCESS",
|
||||||
|
)
|
||||||
|
.order_by(
|
||||||
|
"-date_done",
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
classifier_last_trained = result.date_done if result else None
|
||||||
|
except Exception as e:
|
||||||
|
classifier_status = "ERROR"
|
||||||
|
classifier_last_trained = None
|
||||||
|
classifier_error = "Error loading classifier, check logs for more detail."
|
||||||
|
logger.exception(f"System status error loading classifier: {e}")
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
"pngx_version": current_version,
|
||||||
|
"server_os": platform.platform(),
|
||||||
|
"install_type": install_type,
|
||||||
|
"storage": {
|
||||||
|
"total": media_stats.f_frsize * media_stats.f_blocks,
|
||||||
|
"available": media_stats.f_frsize * media_stats.f_bavail,
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"type": db_conn.vendor,
|
||||||
|
"url": db_url,
|
||||||
|
"status": db_status,
|
||||||
|
"error": db_error,
|
||||||
|
"migration_status": {
|
||||||
|
"latest_migration": applied_migrations[-1],
|
||||||
|
"unapplied_migrations": [
|
||||||
|
m for m in all_migrations if m not in applied_migrations
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"redis_url": redis_url,
|
||||||
|
"redis_status": redis_status,
|
||||||
|
"redis_error": redis_error,
|
||||||
|
"celery_status": celery_active,
|
||||||
|
"index_status": index_status,
|
||||||
|
"index_last_modified": index_last_modified,
|
||||||
|
"index_error": index_error,
|
||||||
|
"classifier_status": classifier_status,
|
||||||
|
"classifier_last_trained": classifier_last_trained,
|
||||||
|
"classifier_error": classifier_error,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@ -32,6 +32,7 @@ from documents.views import SharedLinkView
|
|||||||
from documents.views import ShareLinkViewSet
|
from documents.views import ShareLinkViewSet
|
||||||
from documents.views import StatisticsView
|
from documents.views import StatisticsView
|
||||||
from documents.views import StoragePathViewSet
|
from documents.views import StoragePathViewSet
|
||||||
|
from documents.views import SystemStatusView
|
||||||
from documents.views import TagViewSet
|
from documents.views import TagViewSet
|
||||||
from documents.views import TasksViewSet
|
from documents.views import TasksViewSet
|
||||||
from documents.views import UiSettingsView
|
from documents.views import UiSettingsView
|
||||||
@ -147,6 +148,11 @@ urlpatterns = [
|
|||||||
ProfileView.as_view(),
|
ProfileView.as_view(),
|
||||||
name="profile_view",
|
name="profile_view",
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
"^status/",
|
||||||
|
SystemStatusView.as_view(),
|
||||||
|
name="system_status",
|
||||||
|
),
|
||||||
*api_router.urls,
|
*api_router.urls,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user