mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Feature: app branding (#5357)
This commit is contained in:
parent
2da5e46386
commit
2a6e79acc8
@ -4,9 +4,9 @@ Paperless provides a wide range of customizations. Depending on how you
|
||||
run paperless, these settings have to be defined in different places.
|
||||
|
||||
Certain configuration options may be set via the UI. This currently includes
|
||||
common [OCR](#ocr) related settings. If set, these will take preference over the
|
||||
settings via environment variables. If not set, the environment setting or applicable
|
||||
default will be utilized instead.
|
||||
common [OCR](#ocr) related settings and some frontend settings. If set, these will take
|
||||
preference over the settings via environment variables. If not set, the environment setting
|
||||
or applicable default will be utilized instead.
|
||||
|
||||
- If you run paperless on docker, `paperless.conf` is not used.
|
||||
Rather, configure paperless by copying necessary options to
|
||||
@ -1329,7 +1329,15 @@ started by the container.
|
||||
|
||||
You can read more about this in the [advanced documentation](advanced_usage.md#celery-monitoring).
|
||||
|
||||
## Update Checking {#update-checking}
|
||||
## Frontend Settings
|
||||
|
||||
#### [`PAPERLESS_APP_TITLE=<bool>`](#PAPERLESS_APP_TITLE) {#PAPERLESS_APP_TITLE}
|
||||
|
||||
: If set, overrides the default name "Paperless-ngx"
|
||||
|
||||
#### [`PAPERLESS_APP_LOGO=<path>`](#PAPERLESS_APP_LOGO) {#PAPERLESS_APP_LOGO}
|
||||
|
||||
: Path to an image file in the /media/logo directory, must include 'logo', e.g. `/logo/Atari_logo.svg`
|
||||
|
||||
#### [`PAPERLESS_ENABLE_UPDATE_CHECK=<bool>`](#PAPERLESS_ENABLE_UPDATE_CHECK) {#PAPERLESS_ENABLE_UPDATE_CHECK}
|
||||
|
||||
|
@ -439,7 +439,7 @@
|
||||
<source>Discard</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
<context context-type="linenumber">49</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||
@ -450,7 +450,7 @@
|
||||
<source>Save</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
<context context-type="linenumber">52</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||
@ -513,28 +513,42 @@
|
||||
<source>Error retrieving config</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
<context context-type="linenumber">81</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1172622527269118932" datatype="html">
|
||||
<source>Invalid JSON</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||
<context context-type="linenumber">105</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5103146006962696736" datatype="html">
|
||||
<source>Configuration updated</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||
<context context-type="linenumber">148</context>
|
||||
<context context-type="linenumber">151</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1664963291286452273" datatype="html">
|
||||
<source>An error occurred updating configuration</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||
<context context-type="linenumber">153</context>
|
||||
<context context-type="linenumber">156</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2653081282186526824" datatype="html">
|
||||
<source>File successfully updated</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||
<context context-type="linenumber">178</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5902783625859504265" datatype="html">
|
||||
<source>An error occurred uploading file</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||
<context context-type="linenumber">183</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4804785061014590286" datatype="html">
|
||||
@ -545,11 +559,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">309</context>
|
||||
<context context-type="linenumber">319</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">314</context>
|
||||
<context context-type="linenumber">324</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8838884664569764142" datatype="html">
|
||||
@ -646,15 +660,15 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">59</context>
|
||||
<context context-type="linenumber">69</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">267</context>
|
||||
<context context-type="linenumber">277</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">271</context>
|
||||
<context context-type="linenumber">281</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1685061484835793745" datatype="html">
|
||||
@ -1123,7 +1137,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">116</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1595668988802980095" datatype="html">
|
||||
@ -1517,7 +1531,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||
<context context-type="linenumber">117</context>
|
||||
<context context-type="linenumber">121</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5260584511980773458" datatype="html">
|
||||
@ -1535,7 +1549,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">296</context>
|
||||
<context context-type="linenumber">306</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="103921551219467537" datatype="html">
|
||||
@ -1722,11 +1736,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">285</context>
|
||||
<context context-type="linenumber">295</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">289</context>
|
||||
<context context-type="linenumber">299</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4555457172864212828" datatype="html">
|
||||
@ -2035,66 +2049,65 @@
|
||||
<context context-type="linenumber">180</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2173456130768795374" datatype="html">
|
||||
<source>Paperless-ngx</source>
|
||||
<trans-unit id="7931334600001636863" datatype="html">
|
||||
<source>by Paperless-ngx</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">app title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="7100953725264790651" datatype="html">
|
||||
<source>Search documents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2448391510242468907" datatype="html">
|
||||
<source>Logged in as <x id="INTERPOLATION" equiv-text="{{this.settingsService.displayName}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">57</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2127032578120864096" datatype="html">
|
||||
<source>My Profile</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3797778920049399855" datatype="html">
|
||||
<source>Logout</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">64</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4895326106573044490" datatype="html">
|
||||
<source>Documentation</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
<context context-type="linenumber">81</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">319</context>
|
||||
<context context-type="linenumber">329</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">324</context>
|
||||
<context context-type="linenumber">334</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6570363013146073520" datatype="html">
|
||||
<source>Dashboard</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">96</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">100</context>
|
||||
<context context-type="linenumber">110</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.html</context>
|
||||
@ -2105,11 +2118,11 @@
|
||||
<source>Documents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">105</context>
|
||||
<context context-type="linenumber">115</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">109</context>
|
||||
<context context-type="linenumber">119</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
@ -2136,36 +2149,36 @@
|
||||
<source>Open documents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">150</context>
|
||||
<context context-type="linenumber">160</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5687256342387781369" datatype="html">
|
||||
<source>Close all</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">174</context>
|
||||
<context context-type="linenumber">184</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">178</context>
|
||||
<context context-type="linenumber">188</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3897348120591552265" datatype="html">
|
||||
<source>Manage</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">186</context>
|
||||
<context context-type="linenumber">196</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7437910965833684826" datatype="html">
|
||||
<source>Correspondents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">192</context>
|
||||
<context context-type="linenumber">202</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">196</context>
|
||||
<context context-type="linenumber">206</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
|
||||
@ -2176,11 +2189,11 @@
|
||||
<source>Tags</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">201</context>
|
||||
<context context-type="linenumber">211</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">206</context>
|
||||
<context context-type="linenumber">216</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/input/tags/tags.component.ts</context>
|
||||
@ -2207,11 +2220,11 @@
|
||||
<source>Document Types</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">212</context>
|
||||
<context context-type="linenumber">222</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">216</context>
|
||||
<context context-type="linenumber">226</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
|
||||
@ -2222,11 +2235,11 @@
|
||||
<source>Storage Paths</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">221</context>
|
||||
<context context-type="linenumber">231</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">225</context>
|
||||
<context context-type="linenumber">235</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
|
||||
@ -2237,11 +2250,11 @@
|
||||
<source>Custom Fields</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">230</context>
|
||||
<context context-type="linenumber">240</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">234</context>
|
||||
<context context-type="linenumber">244</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.html</context>
|
||||
@ -2256,11 +2269,11 @@
|
||||
<source>Workflows</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">241</context>
|
||||
<context context-type="linenumber">251</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">245</context>
|
||||
<context context-type="linenumber">255</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
|
||||
@ -2271,99 +2284,99 @@
|
||||
<source>Mail</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">250</context>
|
||||
<context context-type="linenumber">260</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">255</context>
|
||||
<context context-type="linenumber">265</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7844706011418789951" datatype="html">
|
||||
<source>Administration</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">261</context>
|
||||
<context context-type="linenumber">271</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3008420115644088420" datatype="html">
|
||||
<source>Configuration</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">276</context>
|
||||
<context context-type="linenumber">286</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">280</context>
|
||||
<context context-type="linenumber">290</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6626289114556551491" datatype="html">
|
||||
<source>File Tasks<x id="START_BLOCK_IF" equiv-text="@if (tasksService.failedFileTasks.length > 0) {"/><x id="START_TAG_SPAN_1" ctype="x-span_1" equiv-text="<span>"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-danger ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.failedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">303,305</context>
|
||||
<context context-type="linenumber">313,315</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1534029177398918729" datatype="html">
|
||||
<source>GitHub</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">331</context>
|
||||
<context context-type="linenumber">341</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4112664765954374539" datatype="html">
|
||||
<source>is available.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">340,341</context>
|
||||
<context context-type="linenumber">350,351</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1175891574282637937" datatype="html">
|
||||
<source>Click to view.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">341</context>
|
||||
<context context-type="linenumber">351</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="9811291095862612" datatype="html">
|
||||
<source>Paperless-ngx can automatically check for updates</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">345</context>
|
||||
<context context-type="linenumber">355</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="894819944961861800" datatype="html">
|
||||
<source> How does this work? </source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">352,354</context>
|
||||
<context context-type="linenumber">362,364</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="509090351011426949" datatype="html">
|
||||
<source>Update available</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||
<context context-type="linenumber">368</context>
|
||||
<context context-type="linenumber">378</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1542489069631984294" datatype="html">
|
||||
<source>Sidebar views updated</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||
<context context-type="linenumber">259</context>
|
||||
<context context-type="linenumber">263</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3547923076537026828" datatype="html">
|
||||
<source>Error updating sidebar views</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||
<context context-type="linenumber">262</context>
|
||||
<context context-type="linenumber">266</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2526035785704676448" datatype="html">
|
||||
<source>An error occurred while saving update checking settings.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||
<context context-type="linenumber">283</context>
|
||||
<context context-type="linenumber">287</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8700121026680200191" datatype="html">
|
||||
@ -3696,6 +3709,14 @@
|
||||
<context context-type="sourcefile">src/app/components/common/input/document-link/document-link.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/input/file/file.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/input/file/file.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/input/number/number.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
@ -3757,6 +3778,13 @@
|
||||
<context context-type="linenumber">44</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6932865105766151309" datatype="html">
|
||||
<source>Upload</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/input/file/file.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5554528553553249088" datatype="html">
|
||||
<source>Show password</source>
|
||||
<context-group purpose="location">
|
||||
@ -4188,32 +4216,32 @@
|
||||
<context context-type="linenumber">44</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1865646076514070962" datatype="html">
|
||||
<source>Hello <x id="PH" equiv-text="this.settingsService.displayName"/>, welcome to Paperless-ngx</source>
|
||||
<trans-unit id="6581372518205328477" datatype="html">
|
||||
<source>Hello <x id="PH" equiv-text="this.settingsService.displayName"/>, welcome to <x id="PH_1" equiv-text="appTitle"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5334686081082652461" datatype="html">
|
||||
<source>Welcome to Paperless-ngx</source>
|
||||
<trans-unit id="2901300640157872718" datatype="html">
|
||||
<source>Welcome to <x id="PH" equiv-text="appTitle"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
|
||||
<context context-type="linenumber">40</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1325877348738783391" datatype="html">
|
||||
<source>Dashboard updated</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3214475953924351473" datatype="html">
|
||||
<source>Error updating dashboard</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
<context context-type="linenumber">77</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2946624699882754313" datatype="html">
|
||||
@ -6443,102 +6471,123 @@
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="432834967329800065" datatype="html">
|
||||
<source>General Settings</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2762851116637676072" datatype="html">
|
||||
<source>OCR Settings</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">49</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1313137480169642057" datatype="html">
|
||||
<source>Output Type</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">73</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2826581353496868063" datatype="html">
|
||||
<source>Language</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">81</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3817498941817715969" datatype="html">
|
||||
<source>Pages</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">88</context>
|
||||
<context context-type="linenumber">90</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1713271461473302108" datatype="html">
|
||||
<source>Mode</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">95</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6114528299376689399" datatype="html">
|
||||
<source>Skip Archive File</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
<context context-type="linenumber">105</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1115402553541327390" datatype="html">
|
||||
<source>Image DPI</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">111</context>
|
||||
<context context-type="linenumber">113</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6352596107300820129" datatype="html">
|
||||
<source>Clean</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">118</context>
|
||||
<context context-type="linenumber">120</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="725308589819024010" datatype="html">
|
||||
<source>Deskew</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
<context context-type="linenumber">128</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6256076128297775802" datatype="html">
|
||||
<source>Rotate Pages</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">133</context>
|
||||
<context context-type="linenumber">135</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8527188778859256947" datatype="html">
|
||||
<source>Rotate Pages Threshold</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">140</context>
|
||||
<context context-type="linenumber">142</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3762131309176747817" datatype="html">
|
||||
<source>Max Image Pixels</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">147</context>
|
||||
<context context-type="linenumber">149</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7846583355792281769" datatype="html">
|
||||
<source>Color Conversion Strategy</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">154</context>
|
||||
<context context-type="linenumber">156</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4696480417479207939" datatype="html">
|
||||
<source>OCR Arguments</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">162</context>
|
||||
<context context-type="linenumber">164</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7106327322456204362" datatype="html">
|
||||
<source>Application Logo</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">171</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2684743776608068095" datatype="html">
|
||||
<source>Application Title</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
<context context-type="linenumber">178</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5948496158474272829" datatype="html">
|
||||
|
@ -110,6 +110,7 @@ import { DocumentLinkComponent } from './components/common/input/document-link/d
|
||||
import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component'
|
||||
import { SwitchComponent } from './components/common/input/switch/switch.component'
|
||||
import { ConfigComponent } from './components/admin/config/config.component'
|
||||
import { FileComponent } from './components/common/input/file/file.component'
|
||||
|
||||
import localeAf from '@angular/common/locales/af'
|
||||
import localeAr from '@angular/common/locales/ar'
|
||||
@ -267,6 +268,7 @@ function initializeApp(settings: SettingsService) {
|
||||
PreviewPopupComponent,
|
||||
SwitchComponent,
|
||||
ConfigComponent,
|
||||
FileComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -30,6 +30,7 @@
|
||||
@case (ConfigOptionType.Boolean) { <pngx-input-switch [formControlName]="option.key" [error]="errors[option.key]" [showUnsetNote]="true" [horizontal]="true" title="Enable" i18n-title></pngx-input-switch> }
|
||||
@case (ConfigOptionType.String) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
|
||||
@case (ConfigOptionType.JSON) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
|
||||
@case (ConfigOptionType.File) { <pngx-input-file [formControlName]="option.key" (upload)="uploadFile($event, option.key)" [error]="errors[option.key]"></pngx-input-file> }
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,12 +15,15 @@ import { SwitchComponent } from '../../common/input/switch/switch.component'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||
import { SelectComponent } from '../../common/input/select/select.component'
|
||||
import { FileComponent } from '../../common/input/file/file.component'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
|
||||
describe('ConfigComponent', () => {
|
||||
let component: ConfigComponent
|
||||
let fixture: ComponentFixture<ConfigComponent>
|
||||
let configService: ConfigService
|
||||
let toastService: ToastService
|
||||
let settingService: SettingsService
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
@ -30,6 +33,7 @@ describe('ConfigComponent', () => {
|
||||
SelectComponent,
|
||||
NumberComponent,
|
||||
SwitchComponent,
|
||||
FileComponent,
|
||||
PageHeaderComponent,
|
||||
],
|
||||
imports: [
|
||||
@ -44,6 +48,7 @@ describe('ConfigComponent', () => {
|
||||
|
||||
configService = TestBed.inject(ConfigService)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
settingService = TestBed.inject(SettingsService)
|
||||
fixture = TestBed.createComponent(ConfigComponent)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
@ -100,4 +105,39 @@ describe('ConfigComponent', () => {
|
||||
component.configForm.patchValue({ user_args: '{ "foo": "bar" }' })
|
||||
expect(component.errors).toEqual({ user_args: null })
|
||||
})
|
||||
|
||||
it('should upload file, show error if necessary', () => {
|
||||
const uploadSpy = jest.spyOn(configService, 'uploadFile')
|
||||
const errorSpy = jest.spyOn(toastService, 'showError')
|
||||
uploadSpy.mockReturnValueOnce(
|
||||
throwError(() => new Error('Error uploading file'))
|
||||
)
|
||||
component.uploadFile(new File([], 'test.png'), 'app_logo')
|
||||
expect(uploadSpy).toHaveBeenCalled()
|
||||
expect(errorSpy).toHaveBeenCalled()
|
||||
uploadSpy.mockReturnValueOnce(
|
||||
of({ app_logo: 'https://example.com/logo/test.png' } as any)
|
||||
)
|
||||
component.uploadFile(new File([], 'test.png'), 'app_logo')
|
||||
expect(component.initialConfig).toEqual({
|
||||
app_logo: 'https://example.com/logo/test.png',
|
||||
})
|
||||
})
|
||||
|
||||
it('should refresh ui settings after save or upload', () => {
|
||||
const saveSpy = jest.spyOn(configService, 'saveConfig')
|
||||
const initSpy = jest.spyOn(settingService, 'initializeSettings')
|
||||
saveSpy.mockReturnValueOnce(
|
||||
of({ output_type: OutputTypeConfig.PDF_A } as any)
|
||||
)
|
||||
component.saveConfig()
|
||||
expect(initSpy).toHaveBeenCalled()
|
||||
|
||||
const uploadSpy = jest.spyOn(configService, 'uploadFile')
|
||||
uploadSpy.mockReturnValueOnce(
|
||||
of({ app_logo: 'https://example.com/logo/test.png' } as any)
|
||||
)
|
||||
component.uploadFile(new File([], 'test.png'), 'app_logo')
|
||||
expect(initSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
@ -19,6 +19,7 @@ import { ConfigService } from 'src/app/services/config.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
|
||||
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-config',
|
||||
@ -55,7 +56,8 @@ export class ConfigComponent
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private toastService: ToastService
|
||||
private toastService: ToastService,
|
||||
private settingsService: SettingsService
|
||||
) {
|
||||
super()
|
||||
this.configForm.addControl('id', new FormControl())
|
||||
@ -145,6 +147,7 @@ export class ConfigComponent
|
||||
this.loading = false
|
||||
this.initialize(config)
|
||||
this.store.next(config)
|
||||
this.settingsService.initializeSettings().subscribe()
|
||||
this.toastService.showInfo($localize`Configuration updated`)
|
||||
},
|
||||
error: (e) => {
|
||||
@ -160,4 +163,27 @@ export class ConfigComponent
|
||||
public discardChanges() {
|
||||
this.configForm.reset(this.initialConfig)
|
||||
}
|
||||
|
||||
public uploadFile(file: File, key: string) {
|
||||
this.loading = true
|
||||
this.configService
|
||||
.uploadFile(file, this.configForm.value['id'], key)
|
||||
.pipe(takeUntil(this.unsubscribeNotifier), first())
|
||||
.subscribe({
|
||||
next: (config) => {
|
||||
this.loading = false
|
||||
this.initialize(config)
|
||||
this.store.next(config)
|
||||
this.settingsService.initializeSettings().subscribe()
|
||||
this.toastService.showInfo($localize`File successfully updated`)
|
||||
},
|
||||
error: (e) => {
|
||||
this.loading = false
|
||||
this.toastService.showError(
|
||||
$localize`An error occurred uploading file`,
|
||||
e
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,25 @@
|
||||
(click)="isMenuCollapsed = !isMenuCollapsed">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<a class="navbar-brand col-auto col-md-3 col-lg-2 me-0 px-3 py-3 order-sm-0"
|
||||
[ngClass]="slimSidebarEnabled ? 'slim' : 'col-auto col-md-3 col-lg-2'" routerLink="/dashboard"
|
||||
<a class="navbar-brand d-flex col-auto col-md-3 col-lg-2 me-0 px-3 py-3 order-sm-0"
|
||||
[ngClass]="{ 'slim': slimSidebarEnabled, 'd-flex col-auto col-md-3 col-lg-2' : !slimSidebarEnabled, 'py-3' : !customAppTitle?.length || slimSidebarEnabled, 'py-2': customAppTitle?.length }"
|
||||
routerLink="/dashboard"
|
||||
tourAnchor="tour.intro">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.43 238.91" width="1em" class="me-2" fill="currentColor">
|
||||
<path
|
||||
d="M194.7,0C164.22,70.94,17.64,79.74,64.55,194.06c.58,1.47-10.85,17-18.47,29.9-1.76-6.45-3.81-13.48-3.52-14.07,38.11-45.14-27.26-70.65-30.78-107.58C-4.64,131.62-10.5,182.92,39,212.53c.3,0,2.64,11.14,3.81,16.71a58.55,58.55,0,0,0-2.93,6.45c-1.17,2.93,7.62,2.64,7.62,3.22.88-.29,21.7-36.93,22.28-37.23C187.67,174.72,208.48,68.6,194.7,0ZM134.61,74.75C79.5,124,70.12,160.64,71.88,178.53,53.41,134.85,107.64,86.77,134.61,74.75ZM28.2,145.11c10.55,9.67,28.14,39.28,13.19,56.57C44.91,193.77,46.08,175.89,28.2,145.11Z"
|
||||
transform="translate(0 0)" />
|
||||
</svg>
|
||||
<span class="ms-2" [class.visually-hidden]="slimSidebarEnabled" i18n="app title">Paperless-ngx</span>
|
||||
<div class="ms-2 d-inline-block" [class.visually-hidden]="slimSidebarEnabled">
|
||||
@if (customAppTitle?.length) {
|
||||
<div class="d-flex flex-column align-items-start">
|
||||
<span class="title">{{customAppTitle}}</span>
|
||||
<span class="byline text-uppercase font-monospace" i18n>by Paperless-ngx</span>
|
||||
</div>
|
||||
} @else {
|
||||
Paperless-ngx
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1"
|
||||
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
|
||||
|
@ -217,9 +217,16 @@ main {
|
||||
*/
|
||||
|
||||
.navbar-brand {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
font-size: 1rem;
|
||||
|
||||
.flex-column {
|
||||
padding: 0.15rem 0;
|
||||
}
|
||||
|
||||
.byline {
|
||||
font-size: 0.5rem;
|
||||
letter-spacing: 0.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
|
@ -102,6 +102,10 @@ export class AppFrameComponent
|
||||
}, 200) // slightly longer than css animation for slim sidebar
|
||||
}
|
||||
|
||||
get customAppTitle(): string {
|
||||
return this.settingsService.get(SETTINGS_KEYS.APP_TITLE)
|
||||
}
|
||||
|
||||
get slimSidebarEnabled(): boolean {
|
||||
return this.settingsService.get(SETTINGS_KEYS.SLIM_SIDEBAR)
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
<div class="mb-3" [class.pb-3]="error">
|
||||
<div class="row">
|
||||
<div class="d-flex align-items-center position-relative hidden-button-container" [class.col-md-3]="horizontal">
|
||||
@if (title) {
|
||||
<label class="form-label" [class.mb-md-0]="horizontal" [for]="inputId">{{title}}</label>
|
||||
}
|
||||
@if (removable) {
|
||||
<button type="button" class="btn btn-sm btn-danger position-absolute left-0" (click)="removed.emit(this)">
|
||||
<svg class="sidebaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||
</svg> <ng-container i18n>Remove</ng-container>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class="input-group" [class.col-md-9]="horizontal" [class.is-invalid]="error">
|
||||
<input #fileInput type="file" class="form-control" [id]="inputId" (change)="onFile($event)" [disabled]="disabled">
|
||||
<button class="btn btn-outline-primary py-0" type="button" (click)="uploadClicked()" [disabled]="disabled || !file" i18n>Upload</button>
|
||||
</div>
|
||||
@if (filename) {
|
||||
<div class="form-text d-flex align-items-center">
|
||||
<span class="text-muted">{{filename}}</span>
|
||||
<button type="button" class="btn btn-link btn-sm text-danger ms-2" (click)="clear()">
|
||||
<svg class="sidebaricon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||
</svg><small class="ms-1" i18n>Remove</small>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
<input #inputField type="hidden" class="form-control small" [(ngModel)]="value" [disabled]="true">
|
||||
@if (hint) {
|
||||
<small class="form-text text-muted" [innerHTML]="hint | safeHtml"></small>
|
||||
}
|
||||
<div class="invalid-feedback position-absolute top-100">
|
||||
{{error}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,41 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
|
||||
import { FileComponent } from './file.component'
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
|
||||
describe('FileComponent', () => {
|
||||
let component: FileComponent
|
||||
let fixture: ComponentFixture<FileComponent>
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [FileComponent],
|
||||
imports: [FormsModule, ReactiveFormsModule, HttpClientTestingModule],
|
||||
}).compileComponents()
|
||||
|
||||
fixture = TestBed.createComponent(FileComponent)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should update file on change', () => {
|
||||
const event = { target: { files: [new File([], 'test.png')] } }
|
||||
component.onFile(event as any)
|
||||
expect(component.file.name).toEqual('test.png')
|
||||
})
|
||||
|
||||
it('should get filename', () => {
|
||||
component.value = 'https://example.com:8000/logo/filename.svg'
|
||||
expect(component.filename).toEqual('filename.svg')
|
||||
})
|
||||
|
||||
it('should fire upload event', () => {
|
||||
let firedFile
|
||||
component.file = new File([], 'test.png')
|
||||
component.upload.subscribe((file) => (firedFile = file))
|
||||
component.uploadClicked()
|
||||
expect(firedFile.name).toEqual('test.png')
|
||||
expect(component.file).toBeUndefined()
|
||||
})
|
||||
})
|
@ -0,0 +1,53 @@
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Output,
|
||||
ViewChild,
|
||||
forwardRef,
|
||||
} from '@angular/core'
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
import { AbstractInputComponent } from '../abstract-input'
|
||||
|
||||
@Component({
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => FileComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
selector: 'pngx-input-file',
|
||||
templateUrl: './file.component.html',
|
||||
styleUrl: './file.component.scss',
|
||||
})
|
||||
export class FileComponent extends AbstractInputComponent<string> {
|
||||
@Output()
|
||||
upload = new EventEmitter<File>()
|
||||
|
||||
public file: File
|
||||
|
||||
@ViewChild('fileInput') fileInput: ElementRef
|
||||
|
||||
get filename(): string {
|
||||
return this.value
|
||||
? this.value.substring(this.value.lastIndexOf('/') + 1)
|
||||
: null
|
||||
}
|
||||
|
||||
onFile(event: Event) {
|
||||
this.file = (event.target as HTMLInputElement).files[0]
|
||||
}
|
||||
|
||||
uploadClicked() {
|
||||
this.upload.emit(this.file)
|
||||
this.clear()
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.file = undefined
|
||||
this.fileInput.nativeElement.value = null
|
||||
this.writeValue(null)
|
||||
this.onChange(null)
|
||||
}
|
||||
}
|
@ -1,18 +1,22 @@
|
||||
<svg [class]="getClasses()" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2897.4 896.6" [attr.style]="'height:'+height">
|
||||
<path class="leaf" d="M140,713.7c-3.4-16.4-10.3-49.1-11.2-49.1c-145.7-87.1-128.4-238-80.2-324.2C59,449,251.2,524,139.1,656.8 c-0.9,1.7,5.2,22.4,10.3,41.4c22.4-37.9,56-83.6,54.3-87.9C65.9,273.9,496.9,248.1,586.6,39.4c40.5,201.8-20.7,513.9-367.2,593.2 c-1.7,0.9-62.9,108.6-65.5,109.5c0-1.7-25.9-0.9-22.4-9.5C133.1,727.4,136.6,720.6,140,713.7L140,713.7z M135.7,632.6 c44-50.9-7.8-137.9-38.8-166.4C149.5,556.7,146,609.3,135.7,632.6L135.7,632.6z" transform="translate(0)" style="fill:#17541f"/>
|
||||
<g class="text" style="fill:#000">
|
||||
<path d="M1022.3,428.7c-17.8-19.9-42.7-29.8-74.7-29.8c-22.3,0-42.4,5.7-60.5,17.3c-18.1,11.6-32.3,27.5-42.5,47.8 s-15.3,42.9-15.3,67.8c0,24.9,5.1,47.5,15.3,67.8c10.3,20.3,24.4,36.2,42.5,47.8c18.1,11.5,38.3,17.3,60.5,17.3 c32,0,56.9-9.9,74.7-29.8v20.4v0.2h84.5V408.3h-84.5V428.7z M1010.5,575c-10.2,11.7-23.6,17.6-40.2,17.6s-29.9-5.9-40-17.6 s-15.1-26.1-15.1-43.3c0-17.1,5-31.6,15.1-43.3s23.4-17.6,40-17.6c16.6,0,30,5.9,40.2,17.6s15.3,26.1,15.3,43.3 S1020.7,563.3,1010.5,575z" transform="translate(0)"/>
|
||||
<path d="M1381,416.1c-18.1-11.5-38.3-17.3-60.5-17.4c-32,0-56.9,9.9-74.7,29.8v-20.4h-84.5v390.7h84.5v-164 c17.8,19.9,42.7,29.8,74.7,29.8c22.3,0,42.4-5.7,60.5-17.3s32.3-27.5,42.5-47.8c10.2-20.3,15.3-42.9,15.3-67.8s-5.1-47.5-15.3-67.8 C1413.2,443.6,1399.1,427.7,1381,416.1z M1337.9,575c-10.1,11.7-23.4,17.6-40,17.6s-29.9-5.9-40-17.6s-15.1-26.1-15.1-43.3 c0-17.1,5-31.6,15.1-43.3s23.4-17.6,40-17.6s29.9,5.9,40,17.6s15.1,26.1,15.1,43.3S1347.9,563.3,1337.9,575z" transform="translate(0)"/>
|
||||
<path d="M1672.2,416.8c-20.5-12-43-18-67.6-18c-24.9,0-47.6,5.9-68,17.6c-20.4,11.7-36.5,27.7-48.2,48s-17.6,42.7-17.6,67.3 c0.3,25.2,6.2,47.8,17.8,68c11.5,20.2,28,36,49.3,47.6c21.3,11.5,45.9,17.3,73.8,17.3c48.6,0,86.8-14.7,114.7-44l-52.5-48.9 c-8.6,8.3-17.6,14.6-26.7,19c-9.3,4.3-21.1,6.4-35.3,6.4c-11.6,0-22.5-3.6-32.7-10.9c-10.3-7.3-17.1-16.5-20.7-27.8h180l0.4-11.6 c0-29.6-6-55.7-18-78.2S1692.6,428.8,1672.2,416.8z M1558.3,503.2c2.1-12.1,7.5-21.8,16.2-29.1s18.7-10.9,30-10.9 s21.2,3.6,29.8,10.9c8.6,7.2,13.9,16.9,16,29.1H1558.3z" transform="translate(0)"/>
|
||||
<path d="M1895.3,411.7c-11,5.6-20.3,13.7-28,24.4h-0.1v-28h-84.5v247.3h84.5V536.3c0-22.6,4.7-38.1,14.2-46.5 c9.5-8.5,22.7-12.7,39.6-12.7c6.2,0,13.5,1,21.8,3.1l10.7-72c-5.9-3.3-14.5-4.9-25.8-4.9C1917.1,403.3,1906.3,406.1,1895.3,411.7z" transform="translate(0)"/>
|
||||
<rect x="1985" y="277.4" width="84.5" height="377.8" transform="translate(0)"/>
|
||||
<path d="M2313.2,416.8c-20.5-12-43-18-67.6-18c-24.9,0-47.6,5.9-68,17.6s-36.5,27.7-48.2,48c-11.7,20.3-17.6,42.7-17.6,67.3 c0.3,25.2,6.2,47.8,17.8,68c11.5,20.2,28,36,49.3,47.6c21.3,11.5,45.9,17.3,73.8,17.3c48.6,0,86.8-14.7,114.7-44l-52.5-48.9 c-8.6,8.3-17.6,14.6-26.7,19c-9.3,4.3-21.1,6.4-35.3,6.4c-11.6,0-22.5-3.6-32.7-10.9c-10.3-7.3-17.1-16.5-20.7-27.8h180l0.4-11.6 c0-29.6-6-55.7-18-78.2S2333.6,428.8,2313.2,416.8z M2199.3,503.2c2.1-12.1,7.5-21.8,16.2-29.1s18.7-10.9,30-10.9 s21.2,3.6,29.8,10.9c8.6,7.2,13.9,16.9,16,29.1H2199.3z" transform="translate(0)"/>
|
||||
<path d="M2583.6,507.7c-13.8-4.4-30.6-8.1-50.5-11.1c-15.1-2.7-26.1-5.2-32.9-7.6c-6.8-2.4-10.2-6.1-10.2-11.1s2.3-8.7,6.7-10.9 c4.4-2.2,11.5-3.3,21.3-3.3c11.6,0,24.3,2.4,38.1,7.2c13.9,4.8,26.2,11,36.9,18.4l32.4-58.2c-11.3-7.4-26.2-14.7-44.9-21.8 c-18.7-7.1-39.6-10.7-62.7-10.7c-33.7,0-60.2,7.6-79.3,22.7c-19.1,15.1-28.7,36.1-28.7,63.1c0,19,4.8,33.9,14.4,44.7 c9.6,10.8,21,18.5,34,22.9c13.1,4.5,28.9,8.3,47.6,11.6c14.6,2.7,25.1,5.3,31.6,7.8s9.8,6.5,9.8,11.8c0,10.4-9.7,15.6-29.3,15.6 c-13.7,0-28.5-2.3-44.7-6.9c-16.1-4.6-29.2-11.3-39.3-20.2l-33.3,60c9.2,7.4,24.6,14.7,46.2,22c21.7,7.3,45.2,10.9,70.7,10.9 c34.7,0,62.9-7.4,84.5-22.4c21.7-15,32.5-37.3,32.5-66.9c0-19.3-5-34.2-15.1-44.9S2597.4,512.1,2583.6,507.7z" transform="translate(0)"/>
|
||||
<path d="M2883.4,575.3c0-19.3-5-34.2-15.1-44.9s-22-18.3-35.8-22.7c-13.8-4.4-30.6-8.1-50.5-11.1c-15.1-2.7-26.1-5.2-32.9-7.6 c-6.8-2.4-10.2-6.1-10.2-11.1s2.3-8.7,6.7-10.9c4.4-2.2,11.5-3.3,21.3-3.3c11.6,0,24.3,2.4,38.1,7.2c13.9,4.8,26.2,11,36.9,18.4 l32.4-58.2c-11.3-7.4-26.2-14.7-44.9-21.8c-18.7-7.1-39.6-10.7-62.7-10.7c-33.7,0-60.2,7.6-79.3,22.7 c-19.1,15.1-28.7,36.1-28.7,63.1c0,19,4.8,33.9,14.4,44.7c9.6,10.8,21,18.5,34,22.9c13.1,4.5,28.9,8.3,47.6,11.6 c14.6,2.7,25.1,5.3,31.6,7.8s9.8,6.5,9.8,11.8c0,10.4-9.7,15.6-29.3,15.6c-13.7,0-28.5-2.3-44.7-6.9c-16.1-4.6-29.2-11.3-39.3-20.2 l-33.3,60c9.2,7.4,24.6,14.7,46.2,22c21.7,7.3,45.2,10.9,70.7,10.9c34.7,0,62.9-7.4,84.5-22.4 C2872.6,627.2,2883.4,604.9,2883.4,575.3z" transform="translate(0)"/>
|
||||
<rect x="2460.7" y="738.7" width="59.6" height="17.2" transform="translate(0)"/>
|
||||
<path d="M2596.5,706.4c-5.7,0-11,1-15.8,3s-9,5-12.5,8.9v-9.4h-19.4v93.6h19.4v-52c0-8.6,2.1-15.3,6.3-20c4.2-4.7,9.5-7.1,15.9-7.1 c7.8,0,13.4,2.3,16.8,6.7c3.4,4.5,5.1,11.3,5.1,20.5v52h19.4v-56.8c0-12.8-3.2-22.6-9.5-29.3 C2615.8,709.8,2607.3,706.4,2596.5,706.4z" transform="translate(0)"/>
|
||||
<path d="M2733.8,717.7c-3.6-3.4-7.9-6.1-13.1-8.2s-10.6-3.1-16.2-3.1c-8.7,0-16.5,2.1-23.5,6.3s-12.5,10-16.5,17.3 c-4,7.3-6,15.4-6,24.4c0,8.9,2,17.1,6,24.3c4,7.3,9.5,13,16.5,17.2s14.9,6.3,23.5,6.3c5.6,0,11-1,16.2-3.1 c5.1-2.1,9.5-4.8,13.1-8.2v24.4c0,8.5-2.5,14.8-7.6,18.7c-5,3.9-11,5.9-18,5.9c-6.7,0-12.4-1.6-17.3-4.7c-4.8-3.1-7.6-7.7-8.3-13.8 h-19.4c0.6,7.7,2.9,14.2,7.1,19.5s9.6,9.3,16.2,12c6.6,2.7,13.8,4,21.7,4c12.8,0,23.5-3.4,32-10.1c8.6-6.7,12.8-17.1,12.8-31.1 V708.9h-19.2V717.7z M2732.2,770.1c-2.5,4.7-6,8.3-10.4,11.2c-4.4,2.7-9.4,4-14.9,4c-5.7,0-10.8-1.4-15.2-4.3s-7.8-6.7-10.2-11.4 c-2.3-4.8-3.5-9.8-3.5-15.2c0-5.5,1.1-10.6,3.5-15.3s5.8-8.5,10.2-11.3s9.5-4.2,15.2-4.2c5.5,0,10.5,1.4,14.9,4s7.9,6.3,10.4,11 s3.8,10,3.8,15.8S2734.7,765.4,2732.2,770.1z" transform="translate(0)"/>
|
||||
<polygon points="2867.9,708.9 2846.5,708.9 2820.9,741.9 2795.5,708.9 2773.1,708.9 2809.1,755 2771.5,802.5 2792.9,802.5 2820.1,767.9 2847.2,802.6 2869.6,802.6 2832,754.4 " transform="translate(0)"/>
|
||||
<path d="M757.6,293.7c-20-10.8-42.6-16.2-67.8-16.2H600c-8.5,39.2-21.1,76.4-37.6,111.3c-9.9,20.8-21.1,40.6-33.6,59.4v207.2h88.9 V521.5h72c25.2,0,47.8-5.4,67.8-16.2s35.7-25.6,47.1-44.2c11.4-18.7,17.1-39.1,17.1-61.3c0.1-22.7-5.6-43.3-17-61.9 C793.3,319.2,777.6,304.5,757.6,293.7z M716.6,434.3c-9.3,8.9-21.6,13.3-36.7,13.3l-62.2,0.4v-92.5l62.2-0.4 c15.1,0,27.3,4.4,36.7,13.3c9.4,8.9,14,19.9,14,32.9C730.6,414.5,726,425.4,716.6,434.3z" transform="translate(0)"/>
|
||||
</g>
|
||||
</svg>
|
||||
@if (customLogo) {
|
||||
<img src="{{customLogo}}" height="100%" width="100%" [attr.style]="'height:'+height" />
|
||||
} @else {
|
||||
<svg [class]="getClasses()" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2897.4 896.6" [attr.style]="'height:'+height">
|
||||
<path class="leaf" d="M140,713.7c-3.4-16.4-10.3-49.1-11.2-49.1c-145.7-87.1-128.4-238-80.2-324.2C59,449,251.2,524,139.1,656.8 c-0.9,1.7,5.2,22.4,10.3,41.4c22.4-37.9,56-83.6,54.3-87.9C65.9,273.9,496.9,248.1,586.6,39.4c40.5,201.8-20.7,513.9-367.2,593.2 c-1.7,0.9-62.9,108.6-65.5,109.5c0-1.7-25.9-0.9-22.4-9.5C133.1,727.4,136.6,720.6,140,713.7L140,713.7z M135.7,632.6 c44-50.9-7.8-137.9-38.8-166.4C149.5,556.7,146,609.3,135.7,632.6L135.7,632.6z" transform="translate(0)" style="fill:#17541f"/>
|
||||
<g class="text" style="fill:#000">
|
||||
<path d="M1022.3,428.7c-17.8-19.9-42.7-29.8-74.7-29.8c-22.3,0-42.4,5.7-60.5,17.3c-18.1,11.6-32.3,27.5-42.5,47.8 s-15.3,42.9-15.3,67.8c0,24.9,5.1,47.5,15.3,67.8c10.3,20.3,24.4,36.2,42.5,47.8c18.1,11.5,38.3,17.3,60.5,17.3 c32,0,56.9-9.9,74.7-29.8v20.4v0.2h84.5V408.3h-84.5V428.7z M1010.5,575c-10.2,11.7-23.6,17.6-40.2,17.6s-29.9-5.9-40-17.6 s-15.1-26.1-15.1-43.3c0-17.1,5-31.6,15.1-43.3s23.4-17.6,40-17.6c16.6,0,30,5.9,40.2,17.6s15.3,26.1,15.3,43.3 S1020.7,563.3,1010.5,575z" transform="translate(0)"/>
|
||||
<path d="M1381,416.1c-18.1-11.5-38.3-17.3-60.5-17.4c-32,0-56.9,9.9-74.7,29.8v-20.4h-84.5v390.7h84.5v-164 c17.8,19.9,42.7,29.8,74.7,29.8c22.3,0,42.4-5.7,60.5-17.3s32.3-27.5,42.5-47.8c10.2-20.3,15.3-42.9,15.3-67.8s-5.1-47.5-15.3-67.8 C1413.2,443.6,1399.1,427.7,1381,416.1z M1337.9,575c-10.1,11.7-23.4,17.6-40,17.6s-29.9-5.9-40-17.6s-15.1-26.1-15.1-43.3 c0-17.1,5-31.6,15.1-43.3s23.4-17.6,40-17.6s29.9,5.9,40,17.6s15.1,26.1,15.1,43.3S1347.9,563.3,1337.9,575z" transform="translate(0)"/>
|
||||
<path d="M1672.2,416.8c-20.5-12-43-18-67.6-18c-24.9,0-47.6,5.9-68,17.6c-20.4,11.7-36.5,27.7-48.2,48s-17.6,42.7-17.6,67.3 c0.3,25.2,6.2,47.8,17.8,68c11.5,20.2,28,36,49.3,47.6c21.3,11.5,45.9,17.3,73.8,17.3c48.6,0,86.8-14.7,114.7-44l-52.5-48.9 c-8.6,8.3-17.6,14.6-26.7,19c-9.3,4.3-21.1,6.4-35.3,6.4c-11.6,0-22.5-3.6-32.7-10.9c-10.3-7.3-17.1-16.5-20.7-27.8h180l0.4-11.6 c0-29.6-6-55.7-18-78.2S1692.6,428.8,1672.2,416.8z M1558.3,503.2c2.1-12.1,7.5-21.8,16.2-29.1s18.7-10.9,30-10.9 s21.2,3.6,29.8,10.9c8.6,7.2,13.9,16.9,16,29.1H1558.3z" transform="translate(0)"/>
|
||||
<path d="M1895.3,411.7c-11,5.6-20.3,13.7-28,24.4h-0.1v-28h-84.5v247.3h84.5V536.3c0-22.6,4.7-38.1,14.2-46.5 c9.5-8.5,22.7-12.7,39.6-12.7c6.2,0,13.5,1,21.8,3.1l10.7-72c-5.9-3.3-14.5-4.9-25.8-4.9C1917.1,403.3,1906.3,406.1,1895.3,411.7z" transform="translate(0)"/>
|
||||
<rect x="1985" y="277.4" width="84.5" height="377.8" transform="translate(0)"/>
|
||||
<path d="M2313.2,416.8c-20.5-12-43-18-67.6-18c-24.9,0-47.6,5.9-68,17.6s-36.5,27.7-48.2,48c-11.7,20.3-17.6,42.7-17.6,67.3 c0.3,25.2,6.2,47.8,17.8,68c11.5,20.2,28,36,49.3,47.6c21.3,11.5,45.9,17.3,73.8,17.3c48.6,0,86.8-14.7,114.7-44l-52.5-48.9 c-8.6,8.3-17.6,14.6-26.7,19c-9.3,4.3-21.1,6.4-35.3,6.4c-11.6,0-22.5-3.6-32.7-10.9c-10.3-7.3-17.1-16.5-20.7-27.8h180l0.4-11.6 c0-29.6-6-55.7-18-78.2S2333.6,428.8,2313.2,416.8z M2199.3,503.2c2.1-12.1,7.5-21.8,16.2-29.1s18.7-10.9,30-10.9 s21.2,3.6,29.8,10.9c8.6,7.2,13.9,16.9,16,29.1H2199.3z" transform="translate(0)"/>
|
||||
<path d="M2583.6,507.7c-13.8-4.4-30.6-8.1-50.5-11.1c-15.1-2.7-26.1-5.2-32.9-7.6c-6.8-2.4-10.2-6.1-10.2-11.1s2.3-8.7,6.7-10.9 c4.4-2.2,11.5-3.3,21.3-3.3c11.6,0,24.3,2.4,38.1,7.2c13.9,4.8,26.2,11,36.9,18.4l32.4-58.2c-11.3-7.4-26.2-14.7-44.9-21.8 c-18.7-7.1-39.6-10.7-62.7-10.7c-33.7,0-60.2,7.6-79.3,22.7c-19.1,15.1-28.7,36.1-28.7,63.1c0,19,4.8,33.9,14.4,44.7 c9.6,10.8,21,18.5,34,22.9c13.1,4.5,28.9,8.3,47.6,11.6c14.6,2.7,25.1,5.3,31.6,7.8s9.8,6.5,9.8,11.8c0,10.4-9.7,15.6-29.3,15.6 c-13.7,0-28.5-2.3-44.7-6.9c-16.1-4.6-29.2-11.3-39.3-20.2l-33.3,60c9.2,7.4,24.6,14.7,46.2,22c21.7,7.3,45.2,10.9,70.7,10.9 c34.7,0,62.9-7.4,84.5-22.4c21.7-15,32.5-37.3,32.5-66.9c0-19.3-5-34.2-15.1-44.9S2597.4,512.1,2583.6,507.7z" transform="translate(0)"/>
|
||||
<path d="M2883.4,575.3c0-19.3-5-34.2-15.1-44.9s-22-18.3-35.8-22.7c-13.8-4.4-30.6-8.1-50.5-11.1c-15.1-2.7-26.1-5.2-32.9-7.6 c-6.8-2.4-10.2-6.1-10.2-11.1s2.3-8.7,6.7-10.9c4.4-2.2,11.5-3.3,21.3-3.3c11.6,0,24.3,2.4,38.1,7.2c13.9,4.8,26.2,11,36.9,18.4 l32.4-58.2c-11.3-7.4-26.2-14.7-44.9-21.8c-18.7-7.1-39.6-10.7-62.7-10.7c-33.7,0-60.2,7.6-79.3,22.7 c-19.1,15.1-28.7,36.1-28.7,63.1c0,19,4.8,33.9,14.4,44.7c9.6,10.8,21,18.5,34,22.9c13.1,4.5,28.9,8.3,47.6,11.6 c14.6,2.7,25.1,5.3,31.6,7.8s9.8,6.5,9.8,11.8c0,10.4-9.7,15.6-29.3,15.6c-13.7,0-28.5-2.3-44.7-6.9c-16.1-4.6-29.2-11.3-39.3-20.2 l-33.3,60c9.2,7.4,24.6,14.7,46.2,22c21.7,7.3,45.2,10.9,70.7,10.9c34.7,0,62.9-7.4,84.5-22.4 C2872.6,627.2,2883.4,604.9,2883.4,575.3z" transform="translate(0)"/>
|
||||
<rect x="2460.7" y="738.7" width="59.6" height="17.2" transform="translate(0)"/>
|
||||
<path d="M2596.5,706.4c-5.7,0-11,1-15.8,3s-9,5-12.5,8.9v-9.4h-19.4v93.6h19.4v-52c0-8.6,2.1-15.3,6.3-20c4.2-4.7,9.5-7.1,15.9-7.1 c7.8,0,13.4,2.3,16.8,6.7c3.4,4.5,5.1,11.3,5.1,20.5v52h19.4v-56.8c0-12.8-3.2-22.6-9.5-29.3 C2615.8,709.8,2607.3,706.4,2596.5,706.4z" transform="translate(0)"/>
|
||||
<path d="M2733.8,717.7c-3.6-3.4-7.9-6.1-13.1-8.2s-10.6-3.1-16.2-3.1c-8.7,0-16.5,2.1-23.5,6.3s-12.5,10-16.5,17.3 c-4,7.3-6,15.4-6,24.4c0,8.9,2,17.1,6,24.3c4,7.3,9.5,13,16.5,17.2s14.9,6.3,23.5,6.3c5.6,0,11-1,16.2-3.1 c5.1-2.1,9.5-4.8,13.1-8.2v24.4c0,8.5-2.5,14.8-7.6,18.7c-5,3.9-11,5.9-18,5.9c-6.7,0-12.4-1.6-17.3-4.7c-4.8-3.1-7.6-7.7-8.3-13.8 h-19.4c0.6,7.7,2.9,14.2,7.1,19.5s9.6,9.3,16.2,12c6.6,2.7,13.8,4,21.7,4c12.8,0,23.5-3.4,32-10.1c8.6-6.7,12.8-17.1,12.8-31.1 V708.9h-19.2V717.7z M2732.2,770.1c-2.5,4.7-6,8.3-10.4,11.2c-4.4,2.7-9.4,4-14.9,4c-5.7,0-10.8-1.4-15.2-4.3s-7.8-6.7-10.2-11.4 c-2.3-4.8-3.5-9.8-3.5-15.2c0-5.5,1.1-10.6,3.5-15.3s5.8-8.5,10.2-11.3s9.5-4.2,15.2-4.2c5.5,0,10.5,1.4,14.9,4s7.9,6.3,10.4,11 s3.8,10,3.8,15.8S2734.7,765.4,2732.2,770.1z" transform="translate(0)"/>
|
||||
<polygon points="2867.9,708.9 2846.5,708.9 2820.9,741.9 2795.5,708.9 2773.1,708.9 2809.1,755 2771.5,802.5 2792.9,802.5 2820.1,767.9 2847.2,802.6 2869.6,802.6 2832,754.4 " transform="translate(0)"/>
|
||||
<path d="M757.6,293.7c-20-10.8-42.6-16.2-67.8-16.2H600c-8.5,39.2-21.1,76.4-37.6,111.3c-9.9,20.8-21.1,40.6-33.6,59.4v207.2h88.9 V521.5h72c25.2,0,47.8-5.4,67.8-16.2s35.7-25.6,47.1-44.2c11.4-18.7,17.1-39.1,17.1-61.3c0.1-22.7-5.6-43.3-17-61.9 C793.3,319.2,777.6,304.5,757.6,293.7z M716.6,434.3c-9.3,8.9-21.6,13.3-36.7,13.3l-62.2,0.4v-92.5l62.2-0.4 c15.1,0,27.3,4.4,36.7,13.3c9.4,8.9,14,19.9,14,32.9C730.6,414.5,726,425.4,716.6,434.3z" transform="translate(0)"/>
|
||||
</g>
|
||||
</svg>
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.5 KiB |
@ -2,15 +2,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
|
||||
import { LogoComponent } from './logo.component'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
|
||||
describe('LogoComponent', () => {
|
||||
let component: LogoComponent
|
||||
let fixture: ComponentFixture<LogoComponent>
|
||||
let settingsService: SettingsService
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LogoComponent],
|
||||
imports: [HttpClientTestingModule],
|
||||
})
|
||||
settingsService = TestBed.inject(SettingsService)
|
||||
fixture = TestBed.createComponent(LogoComponent)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
@ -33,4 +39,9 @@ describe('LogoComponent', () => {
|
||||
'height:10em'
|
||||
)
|
||||
})
|
||||
|
||||
it('should support getting custom logo', () => {
|
||||
settingsService.set(SETTINGS_KEYS.APP_LOGO, '/logo/test.png')
|
||||
expect(component.customLogo).toEqual('http://localhost:8000/logo/test.png')
|
||||
})
|
||||
})
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { environment } from 'src/environments/environment'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-logo',
|
||||
@ -12,6 +15,17 @@ export class LogoComponent {
|
||||
@Input()
|
||||
height = '6em'
|
||||
|
||||
get customLogo(): string {
|
||||
return this.settingsService.get(SETTINGS_KEYS.APP_LOGO)?.length
|
||||
? environment.apiBaseUrl.replace(
|
||||
/\/api\/$/,
|
||||
this.settingsService.get(SETTINGS_KEYS.APP_LOGO)
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
||||
constructor(private settingsService: SettingsService) {}
|
||||
|
||||
getClasses() {
|
||||
return ['logo'].concat(this.extra_classes).join(' ')
|
||||
}
|
||||
|
@ -5,13 +5,13 @@ import { ComponentWithPermissions } from '../with-permissions/with-permissions.c
|
||||
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||
import { SavedView } from 'src/app/data/saved-view'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
import {
|
||||
CdkDragDrop,
|
||||
CdkDragEnd,
|
||||
CdkDragStart,
|
||||
moveItemInArray,
|
||||
} from '@angular/cdk/drag-drop'
|
||||
import { environment } from 'src/environments/environment'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-dashboard',
|
||||
@ -35,9 +35,9 @@ export class DashboardComponent extends ComponentWithPermissions {
|
||||
|
||||
get subtitle() {
|
||||
if (this.settingsService.displayName) {
|
||||
return $localize`Hello ${this.settingsService.displayName}, welcome to Paperless-ngx`
|
||||
return $localize`Hello ${this.settingsService.displayName}, welcome to ${environment.appTitle}`
|
||||
} else {
|
||||
return $localize`Welcome to Paperless-ngx`
|
||||
return $localize`Welcome to ${environment.appTitle}`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { NotFoundComponent } from './not-found.component'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { LogoComponent } from '../common/logo/logo.component'
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||
|
||||
describe('NotFoundComponent', () => {
|
||||
let component: NotFoundComponent
|
||||
@ -10,6 +11,7 @@ describe('NotFoundComponent', () => {
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [NotFoundComponent, LogoComponent],
|
||||
imports: [HttpClientTestingModule],
|
||||
}).compileComponents()
|
||||
|
||||
fixture = TestBed.createComponent(NotFoundComponent)
|
||||
|
@ -43,9 +43,11 @@ export enum ConfigOptionType {
|
||||
Select = 'select',
|
||||
Boolean = 'boolean',
|
||||
JSON = 'json',
|
||||
File = 'file',
|
||||
}
|
||||
|
||||
export const ConfigCategory = {
|
||||
General: $localize`General Settings`,
|
||||
OCR: $localize`OCR Settings`,
|
||||
}
|
||||
|
||||
@ -164,6 +166,20 @@ export const PaperlessConfigOptions: ConfigOption[] = [
|
||||
config_key: 'PAPERLESS_OCR_USER_ARGS',
|
||||
category: ConfigCategory.OCR,
|
||||
},
|
||||
{
|
||||
key: 'app_logo',
|
||||
title: $localize`Application Logo`,
|
||||
type: ConfigOptionType.File,
|
||||
config_key: 'PAPERLESS_APP_LOGO',
|
||||
category: ConfigCategory.General,
|
||||
},
|
||||
{
|
||||
key: 'app_title',
|
||||
title: $localize`Application Title`,
|
||||
type: ConfigOptionType.String,
|
||||
config_key: 'PAPERLESS_APP_TITLE',
|
||||
category: ConfigCategory.General,
|
||||
},
|
||||
]
|
||||
|
||||
export interface PaperlessConfig extends ObjectWithId {
|
||||
@ -180,4 +196,6 @@ export interface PaperlessConfig extends ObjectWithId {
|
||||
max_image_pixels: number
|
||||
color_conversion_strategy: ColorConvertConfig
|
||||
user_args: object
|
||||
app_logo: string
|
||||
app_title: string
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ export interface UiSetting {
|
||||
|
||||
export const SETTINGS_KEYS = {
|
||||
LANGUAGE: 'language',
|
||||
APP_LOGO: 'app_logo',
|
||||
APP_TITLE: 'app_title',
|
||||
// maintain old general-settings: for backwards compatibility
|
||||
BULK_EDIT_CONFIRMATION_DIALOGS:
|
||||
'general-settings:bulk-edit:confirmation-dialogs',
|
||||
@ -194,4 +196,14 @@ export const SETTINGS: UiSetting[] = [
|
||||
type: 'array',
|
||||
default: [],
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.APP_LOGO,
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.APP_TITLE,
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
]
|
||||
|
@ -39,4 +39,26 @@ describe('ConfigService', () => {
|
||||
)
|
||||
expect(req.request.method).toEqual('PATCH')
|
||||
})
|
||||
|
||||
it('should support upload file with form data', () => {
|
||||
service.uploadFile(new File([], 'test.png'), 1, 'app_logo').subscribe()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}config/1/`
|
||||
)
|
||||
expect(req.request.method).toEqual('PATCH')
|
||||
expect(req.request.body).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not pass string app_logo', () => {
|
||||
service
|
||||
.saveConfig({
|
||||
id: 1,
|
||||
app_logo: '/logo/foobar.png',
|
||||
} as PaperlessConfig)
|
||||
.subscribe()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}config/1/`
|
||||
)
|
||||
expect(req.request.body).toEqual({ id: 1 })
|
||||
})
|
||||
})
|
||||
|
@ -20,8 +20,22 @@ export class ConfigService {
|
||||
}
|
||||
|
||||
saveConfig(config: PaperlessConfig): Observable<PaperlessConfig> {
|
||||
// dont pass string
|
||||
if (typeof config.app_logo === 'string') delete config.app_logo
|
||||
return this.http
|
||||
.patch<PaperlessConfig>(`${this.baseUrl}${config.id}/`, config)
|
||||
.pipe(first())
|
||||
}
|
||||
|
||||
uploadFile(
|
||||
file: File,
|
||||
configID: number,
|
||||
configKey: string
|
||||
): Observable<PaperlessConfig> {
|
||||
let formData = new FormData()
|
||||
formData.append(configKey, file, file.name)
|
||||
return this.http
|
||||
.patch<PaperlessConfig>(`${this.baseUrl}${configID}/`, formData)
|
||||
.pipe(first())
|
||||
}
|
||||
}
|
||||
|
@ -301,4 +301,16 @@ describe('SettingsService', () => {
|
||||
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
.flush(ui_settings)
|
||||
})
|
||||
|
||||
it('should update environment app title if set', () => {
|
||||
const settings = Object.assign({}, ui_settings)
|
||||
settings.settings['app_title'] = 'FooBar'
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}ui_settings/`
|
||||
)
|
||||
req.flush(settings)
|
||||
expect(environment.appTitle).toEqual('FooBar')
|
||||
// post for migrate
|
||||
httpTestingController.expectOne(`${environment.apiBaseUrl}ui_settings/`)
|
||||
})
|
||||
})
|
||||
|
@ -270,6 +270,9 @@ export class SettingsService {
|
||||
first(),
|
||||
tap((uisettings) => {
|
||||
Object.assign(this.settings, uisettings.settings)
|
||||
if (this.get(SETTINGS_KEYS.APP_TITLE)?.length) {
|
||||
environment.appTitle = this.get(SETTINGS_KEYS.APP_TITLE)
|
||||
}
|
||||
this.maybeMigrateSettings()
|
||||
// to update lang cookie
|
||||
if (this.settings['language']?.length)
|
||||
|
@ -11,6 +11,9 @@ $grid-breakpoints: (
|
||||
xxxl: 2400px
|
||||
);
|
||||
|
||||
$form-file-button-bg: var(--bs-body-bg);
|
||||
$form-file-button-hover-bg: var(--pngx-bg-alt);
|
||||
|
||||
@import "node_modules/bootstrap/scss/bootstrap";
|
||||
@import "theme";
|
||||
@import "~@ng-select/ng-select/themes/default.theme.css";
|
||||
|
@ -1,4 +1,5 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import status
|
||||
@ -49,10 +50,34 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
||||
"rotate_pages_threshold": None,
|
||||
"max_image_pixels": None,
|
||||
"color_conversion_strategy": None,
|
||||
"app_title": None,
|
||||
"app_logo": None,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def test_api_get_ui_settings_with_config(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Existing config with app_title, app_logo specified
|
||||
WHEN:
|
||||
- API to retrieve uisettings is called
|
||||
THEN:
|
||||
- app_title and app_logo are included
|
||||
"""
|
||||
config = ApplicationConfiguration.objects.first()
|
||||
config.app_title = "Fancy New Title"
|
||||
config.app_logo = "/logo/example.jpg"
|
||||
config.save()
|
||||
response = self.client.get("/api/ui_settings/", format="json")
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"app_title": config.app_title,
|
||||
"app_logo": config.app_logo,
|
||||
},
|
||||
response.data["settings"],
|
||||
)
|
||||
|
||||
def test_api_update_config(self):
|
||||
"""
|
||||
GIVEN:
|
||||
@ -100,3 +125,37 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
||||
config = ApplicationConfiguration.objects.first()
|
||||
self.assertEqual(config.user_args, None)
|
||||
self.assertEqual(config.language, None)
|
||||
|
||||
def test_api_replace_app_logo(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Existing config with app_logo specified
|
||||
WHEN:
|
||||
- API to replace app_logo is called
|
||||
THEN:
|
||||
- old app_logo file is deleted
|
||||
"""
|
||||
with open(
|
||||
os.path.join(os.path.dirname(__file__), "samples", "simple.jpg"),
|
||||
"rb",
|
||||
) as f:
|
||||
self.client.patch(
|
||||
f"{self.ENDPOINT}1/",
|
||||
{
|
||||
"app_logo": f,
|
||||
},
|
||||
)
|
||||
config = ApplicationConfiguration.objects.first()
|
||||
old_logo = config.app_logo
|
||||
self.assertTrue(os.path.exists(old_logo.path))
|
||||
with open(
|
||||
os.path.join(os.path.dirname(__file__), "samples", "simple.png"),
|
||||
"rb",
|
||||
) as f:
|
||||
self.client.patch(
|
||||
f"{self.ENDPOINT}1/",
|
||||
{
|
||||
"app_logo": f,
|
||||
},
|
||||
)
|
||||
self.assertFalse(os.path.exists(old_logo.path))
|
||||
|
@ -35,6 +35,8 @@ class TestApiUiSettings(DirectoriesMixin, APITestCase):
|
||||
self.assertDictEqual(
|
||||
response.data["settings"],
|
||||
{
|
||||
"app_title": None,
|
||||
"app_logo": None,
|
||||
"update_checking": {
|
||||
"backend_setting": "default",
|
||||
},
|
||||
|
@ -120,6 +120,7 @@ from documents.serialisers import WorkflowTriggerSerializer
|
||||
from documents.signals import document_updated
|
||||
from documents.tasks import consume_file
|
||||
from paperless import version
|
||||
from paperless.config import GeneralConfig
|
||||
from paperless.db import GnuPG
|
||||
from paperless.views import StandardPagination
|
||||
|
||||
@ -1164,6 +1165,16 @@ class UiSettingsView(GenericAPIView):
|
||||
ui_settings["update_checking"] = {
|
||||
"backend_setting": settings.ENABLE_UPDATE_CHECK,
|
||||
}
|
||||
|
||||
general_config = GeneralConfig()
|
||||
|
||||
ui_settings["app_title"] = settings.APP_TITLE
|
||||
if general_config.app_title is not None and len(general_config.app_title) > 0:
|
||||
ui_settings["app_title"] = general_config.app_title
|
||||
ui_settings["app_logo"] = settings.APP_LOGO
|
||||
if general_config.app_logo is not None and len(general_config.app_logo) > 0:
|
||||
ui_settings["app_logo"] = general_config.app_logo
|
||||
|
||||
user_resp = {
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
|
@ -8,13 +8,11 @@ from paperless.models import ApplicationConfiguration
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class OutputTypeConfig:
|
||||
class BaseConfig:
|
||||
"""
|
||||
Almost all parsers care about the chosen PDF output format
|
||||
"""
|
||||
|
||||
output_type: str = dataclasses.field(init=False)
|
||||
|
||||
@staticmethod
|
||||
def _get_config_instance() -> ApplicationConfiguration:
|
||||
app_config = ApplicationConfiguration.objects.all().first()
|
||||
@ -24,6 +22,15 @@ class OutputTypeConfig:
|
||||
app_config = ApplicationConfiguration.objects.all().first()
|
||||
return app_config
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class OutputTypeConfig(BaseConfig):
|
||||
"""
|
||||
Almost all parsers care about the chosen PDF output format
|
||||
"""
|
||||
|
||||
output_type: str = dataclasses.field(init=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
app_config = self._get_config_instance()
|
||||
|
||||
@ -86,3 +93,19 @@ class OcrConfig(OutputTypeConfig):
|
||||
user_args = {}
|
||||
|
||||
self.user_args = user_args
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class GeneralConfig(BaseConfig):
|
||||
"""
|
||||
General application settings that require global scope
|
||||
"""
|
||||
|
||||
app_title: str = dataclasses.field(init=False)
|
||||
app_logo: str = dataclasses.field(init=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
app_config = self._get_config_instance()
|
||||
|
||||
self.app_title = app_config.app_title or None
|
||||
self.app_logo = app_config.app_logo.url if app_config.app_logo else None
|
||||
|
@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.9 on 2024-01-12 05:33
|
||||
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("paperless", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="applicationconfiguration",
|
||||
name="app_logo",
|
||||
field=models.FileField(
|
||||
blank=True,
|
||||
null=True,
|
||||
upload_to="",
|
||||
verbose_name="Application logo",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="applicationconfiguration",
|
||||
name="app_title",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
max_length=48,
|
||||
null=True,
|
||||
verbose_name="Application title",
|
||||
),
|
||||
),
|
||||
]
|
@ -1,3 +1,4 @@
|
||||
from django.core.validators import FileExtensionValidator
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -166,6 +167,23 @@ class ApplicationConfiguration(AbstractSingletonModel):
|
||||
null=True,
|
||||
)
|
||||
|
||||
app_title = models.CharField(
|
||||
verbose_name=_("Application title"),
|
||||
null=True,
|
||||
blank=True,
|
||||
max_length=48,
|
||||
)
|
||||
|
||||
app_logo = models.FileField(
|
||||
verbose_name=_("Application logo"),
|
||||
null=True,
|
||||
blank=True,
|
||||
validators=[
|
||||
FileExtensionValidator(allowed_extensions=["jpg", "png", "gif", "svg"]),
|
||||
],
|
||||
upload_to="logo/",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("paperless application settings")
|
||||
|
||||
|
@ -132,6 +132,11 @@ class ApplicationConfigurationSerializer(serializers.ModelSerializer):
|
||||
data["language"] = None
|
||||
return super().run_validation(data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
if instance.app_logo and "app_logo" in validated_data:
|
||||
instance.app_logo.delete()
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
class Meta:
|
||||
model = ApplicationConfiguration
|
||||
fields = "__all__"
|
||||
|
@ -367,6 +367,7 @@ STORAGES = {
|
||||
"staticfiles": {
|
||||
"BACKEND": _static_backend,
|
||||
},
|
||||
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
|
||||
}
|
||||
|
||||
_CELERY_REDIS_URL, _CHANNELS_REDIS_URL = _parse_redis_url(
|
||||
@ -999,6 +1000,9 @@ ENABLE_UPDATE_CHECK = os.getenv("PAPERLESS_ENABLE_UPDATE_CHECK", "default")
|
||||
if ENABLE_UPDATE_CHECK != "default":
|
||||
ENABLE_UPDATE_CHECK = __get_boolean("PAPERLESS_ENABLE_UPDATE_CHECK")
|
||||
|
||||
APP_TITLE = os.getenv("PAPERLESS_APP_TITLE", None)
|
||||
APP_LOGO = os.getenv("PAPERLESS_APP_LOGO", None)
|
||||
|
||||
###############################################################################
|
||||
# Machine Learning #
|
||||
###############################################################################
|
||||
|
@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
@ -8,6 +10,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.views.generic import RedirectView
|
||||
from django.views.static import serve
|
||||
from rest_framework.authtoken import views
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
@ -181,6 +184,12 @@ urlpatterns = [
|
||||
url=settings.STATIC_URL + "frontend/en-US/assets/%(path)s",
|
||||
),
|
||||
),
|
||||
# App logo
|
||||
re_path(
|
||||
r"^logo(?P<path>.*)$",
|
||||
serve,
|
||||
kwargs={"document_root": os.path.join(settings.MEDIA_ROOT, "logo")},
|
||||
),
|
||||
# TODO: with localization, this is even worse! :/
|
||||
# login, logout
|
||||
path("accounts/", include("django.contrib.auth.urls")),
|
||||
|
Loading…
x
Reference in New Issue
Block a user