kopia lustrzana https://github.com/hholzgra/maposmatic/
Porównaj commity
10 Commity
5c374b08f5
...
4080469a5c
Autor | SHA1 | Data |
---|---|---|
Hartmut Holzgraefe | 4080469a5c | |
Hartmut Holzgraefe | efe09e6d27 | |
Hartmut Holzgraefe | 804686e292 | |
Hartmut Holzgraefe | 5c0cd973aa | |
Hartmut Holzgraefe | 1752a0e7d5 | |
Hartmut Holzgraefe | 91b78165eb | |
Hartmut Holzgraefe | 5435dd0e5c | |
Hartmut Holzgraefe | 22c34f35be | |
Hartmut Holzgraefe | b43b18db2e | |
Hartmut Holzgraefe | febb510b3c |
|
@ -265,6 +265,10 @@ class RenderingsGarbageCollector:
|
|||
pass
|
||||
|
||||
|
||||
# records = UploadFile.objects.filter(keep_until__lte = datetime.now())
|
||||
|
||||
# for record in records:
|
||||
|
||||
if __name__ == '__main__':
|
||||
if (not os.path.exists(RENDERING_RESULT_PATH)
|
||||
or not os.path.isdir(RENDERING_RESULT_PATH)):
|
||||
|
|
|
@ -462,6 +462,7 @@ class JobRenderer(threading.Thread):
|
|||
|
||||
config.logo = self.job.logo
|
||||
config.extra_logo = self.job.extra_logo
|
||||
config.extra_text = self.job.extra_text
|
||||
|
||||
config.stylesheet = renderer.get_stylesheet_by_name(
|
||||
self.job.stylesheet)
|
||||
|
@ -489,27 +490,7 @@ class JobRenderer(threading.Thread):
|
|||
|
||||
config.paper_width_mm = self.job.paper_width_mm
|
||||
config.paper_height_mm = self.job.paper_height_mm
|
||||
except KeyboardInterrupt:
|
||||
self.result = RESULT_KEYBOARD_INTERRUPT
|
||||
LOG.info("Rendering of job #%d interrupted!" % self.job.id)
|
||||
return self.result
|
||||
except MemoryError:
|
||||
self.result = RESULT_MEMORY_EXCEEDED
|
||||
LOG.exception("Not enough memory to render job #%d" % self.job.id)
|
||||
self._email_exception(sys.exc_info())
|
||||
return self.result
|
||||
except Exception as e:
|
||||
self.result = RESULT_PREPARATION_EXCEPTION
|
||||
LOG.exception("Rendering of job #%d failed (exception occurred during"
|
||||
" data preparation)!" % self.job.id)
|
||||
errfile = result_file_prefix + "-errors.txt"
|
||||
fp = open(errfile, "w")
|
||||
traceback.print_exc(file=fp)
|
||||
fp.close()
|
||||
self._email_exception(sys.exc_info())
|
||||
return self.result
|
||||
|
||||
try:
|
||||
# Get the list of output formats (PNG, PDF, SVGZ, CSV)
|
||||
# that the renderer accepts.
|
||||
renderer_cls = renderers.get_renderer_class_by_name(self.job.layout)
|
||||
|
@ -537,16 +518,13 @@ class JobRenderer(threading.Thread):
|
|||
else:
|
||||
self.result = RESULT_RENDERING_EXCEPTION
|
||||
LOG.exception("Rendering of job #%d faild, no output files generated" % self.job.id)
|
||||
return self.result
|
||||
except KeyboardInterrupt:
|
||||
self.result = RESULT_KEYBOARD_INTERRUPT
|
||||
LOG.info("Rendering of job #%d interrupted!" % self.job.id)
|
||||
return self.result
|
||||
except MemoryError:
|
||||
self.result = RESULT_MEMORY_EXCEEDED
|
||||
LOG.exception("Not enough memory to render job #%d" % self.job.id)
|
||||
self._email_exception(sys.exc_info())
|
||||
return self.result
|
||||
except Exception as e:
|
||||
self.result = RESULT_RENDERING_EXCEPTION
|
||||
LOG.exception("Rendering of job #%d failed (exception occurred during"
|
||||
|
@ -556,9 +534,13 @@ class JobRenderer(threading.Thread):
|
|||
traceback.print_exc(file=fp)
|
||||
fp.close()
|
||||
self._email_exception(sys.exc_info())
|
||||
return self.result
|
||||
|
||||
self._email_submitter("render_email_success.txt")
|
||||
if self.result == RESULT_SUCCESS:
|
||||
self._email_submitter("render_email_success.txt")
|
||||
|
||||
for file in self.job.uploads.all():
|
||||
if file.keep_until is None:
|
||||
os.remove(os.path.join(MEDIA_ROOT, file.uploaded_file.name))
|
||||
|
||||
return self.result
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ class MapRenderingJobForm(forms.ModelForm):
|
|||
fields=(forms.FloatField(), forms.FloatField(),
|
||||
forms.FloatField(), forms.FloatField()))
|
||||
uploadfile = MultiFileField(required=False)
|
||||
delete_files_after_rendering = forms.BooleanField(required=False)
|
||||
|
||||
map_lang_flag_list = []
|
||||
for lang_key, lang_name in www.settings.MAP_LANGUAGES_LIST:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.4 on 2023-09-17 16:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('maposmatic', '0032_maprenderingjob_extra_text'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='uploadfile',
|
||||
name='keep_until',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
|
@ -113,6 +113,7 @@ class MapRenderingJob(models.Model):
|
|||
submittermail = models.EmailField(null=True,blank=True)
|
||||
index_queue_at_submission = models.IntegerField()
|
||||
map_language = models.CharField(max_length=16, null=True, blank=True, default='en_US.UTF-8')
|
||||
extra_text = models.CharField(max_length=200, null=True, blank=True)
|
||||
|
||||
renderstep = models.CharField(max_length=80,null=True,blank=True)
|
||||
|
||||
|
@ -367,3 +368,5 @@ class UploadFile(models.Model):
|
|||
file_type = models.CharField(max_length = 4, choices = FILE_TYPES)
|
||||
|
||||
job = models.ManyToManyField(MapRenderingJob, related_name = 'uploads')
|
||||
|
||||
keep_until = models.DateTimeField(null = True, blank = True)
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="col-lg-12" >
|
||||
|
||||
<div class="tab" style="display: block" id="wizard-step-location">
|
||||
|
||||
<ul class="nav nav-tabs" id="locTabs">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="step-location-bbox-tab" data-bs-toggle="tab" data-bs-target="#step-location-bbox" type="button" role="tab" aria-controls="bt-step-location-bbox" aria-selected="true"><i class="fas fa-globe-africa"></i> {% trans "Geographic area" %}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="step-location-admin-tab" data-bs-toggle="tab" data-bs-target="#step-location-admin" type="button" role="tab" aria-controls="bt-step-location-bbox" aria-selected="false"><i class="fas fa-search-location"></i> {% trans "City search" %}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="step-location-file-tab" data-bs-toggle="tab" data-bs-target="#step-location-file" type="button" role="tab" aria-controls="bt-step-location-file" aria-selected="false"><i class="fas fa-upload"></i> {% trans "File upload" %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="step-location-tabs">
|
||||
<div class="tab-pane active" id="step-location-bbox" role="tabpanel">
|
||||
{{ form.bbox }}
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Geographic area selection" %}</h4>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Start by choosing the geographic area you want to
|
||||
render. Use <em>Select area</em> to select a specific bounding box,
|
||||
or just use the visible area.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="step-location-admin" role="tabpanel">
|
||||
<div style="position: relative;">
|
||||
{{ form.administrative_city }}
|
||||
<i class="fas fa-redo suggest-icon" id="loading-icon"></i>
|
||||
<i class="fas fa-exclamation-triangle suggest-icon" id="error-icon"></i>
|
||||
<ul id="suggest" class="dropdown-menu" role="menu"
|
||||
aria-labelledby="id_administrative_city">
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
{{ form.administrative_osmid }}
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "City search" %}</h4>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Start by choosing the city or geographic area you want to
|
||||
render. Suggestions will appear as you start typing, based on what
|
||||
is found in the local database.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="step-location-file" role="tabpanel">
|
||||
<div class="col-lg-12">
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<fieldset id="track-file">
|
||||
<legend><i class="fas fa-file-upload"></i> {% trans "Upload files" %}</legend>
|
||||
{{ form.uploadfile }}
|
||||
<tt id="file-list">
|
||||
</tt>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
{{ form.delete_files_after_rendering }}
|
||||
<label>{% trans "Delete files after rendering" %}</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="alert alert-danger">
|
||||
{% blocktrans trimmed %}
|
||||
<b>Note:</b>
|
||||
The uploaded files will not be made available to anyone,
|
||||
the map created using this
|
||||
file <b>will be publicly visible</b> though.<br/>Do <b>NOT</b> upload any data you don't want to be publicly
|
||||
visible.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "File upload" %}</h4>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Upload GPX, Umap, or general GeoJSON files here.
|
||||
<br/>
|
||||
You can select multiple files at once.
|
||||
<br/>
|
||||
If you re-open the file selection dialog to select files
|
||||
your selection will replace the previous one.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,35 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
{% if form.errors %}
|
||||
<div class="modal" tabindex="-1" role="dialog" id="error-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Form Errors</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{field.label}} {{ error|escape }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{ error|escape }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -0,0 +1,70 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="tab" id="wizard-step-lang-title">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<fieldset>
|
||||
<legend>{% trans "Map title" %}</legend>
|
||||
{{ form.maptitle }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<fieldset>
|
||||
<legend>{% trans "Locale" %}</legend>
|
||||
<div class="dropdown">
|
||||
<button id="map_language_button" type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expand="false">en_US.UTF-8</button>
|
||||
<!-- {{ form.map_language.value }} -->
|
||||
</button>
|
||||
<input type="hidden" name="{{ form.map_language.name }}" id="{{ form.map_language.name }}" value="en_US.UTF-8"/>
|
||||
<ul id="maplang_choices" class="dropdown-menu bg-light" style="height: 400px; overflow-y: auto;" area-labeledby="map_language_button">
|
||||
{% for choice in form.map_language.field.choices %}
|
||||
<li><a class="dropdown-item" href="#" data-langcode="{{choice.0}}" onclick="$('#map_language_button').html($(this).html()); $('#{{ form.map_language.name }}').val('{{choice.0}}');">{{choice.1}} ({{choice.0|locale_base}})</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<br/>
|
||||
<fieldset>
|
||||
<legend>{% trans "Your Email address (for notifications, optional)" %}</legend>
|
||||
{{ form.submittermail }}
|
||||
{% if SUBMITTER_MAIL_LIFETIME %}
|
||||
({% trans "will be deleted after" %} {{ SUBMITTER_MAIL_LIFETIME }} {% trans "hours" %})
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 30px;">
|
||||
<div class="col-lg-12">
|
||||
<fieldset id="summary">
|
||||
<legend>{% trans "Summary" %}</legend>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr><td>{% trans "Location:" %}</td><td id="summary-location"></td></tr>
|
||||
<tr><td>{% trans "Layout:" %}</td><td id="summary-layout"></td></tr>
|
||||
<tr><td>{% trans "Stylesheet:" %}</td><td id="summary-stylesheet"></td></tr>
|
||||
<tr><td>{% trans "Overlay:" %}</td><td id="summary-overlay"></td></tr>
|
||||
<tr><td>{% trans "Paper format:" %}</td><td id="summary-paper-size"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Almost there!" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You're almost ready to request to map rendering! Select the map language,
|
||||
eventually adjust the title of your map, and you're good to go!
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,40 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="tab" id="wizard-step-layout">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<fieldset>
|
||||
<legend>{% trans "Layout" %}</legend>
|
||||
{{ form.layout }}
|
||||
</fieldset>
|
||||
<fieldset id="fieldset-indexer">
|
||||
<legend>{% trans "Indexer" %}</legend>
|
||||
{{ form.indexer }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img id="layout-preview" align="right"
|
||||
src="/media/img/layout/{{request.session.new_layout}}.png"
|
||||
srcset="/media/img/layout/{{request.session.new_layout}}.png, /media/img/layout/{{request.session.new_layout}}-1.5x.png 1.5x, /media/img/layout/{{request.session.new_layout}}-2x.png 2x"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Map layout" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The map layout determines how the map and the index are
|
||||
rendered. The <em>Multi-page layout</em> produces a booklet very suitable for
|
||||
printing and binding.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="tab" id="wizard-step-overlay">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<fieldset>
|
||||
<legend>{% trans "Overlays" %}</legend>
|
||||
{{ form.overlay }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img id="overlay-preview" align="right" src="/media/img/empty.png"/> {# TODO: select default here, too #}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Map overlays" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Overlays render extra objects on top of the chosen base style.
|
||||
Multiple overlays can be selected to add different kinds of additional information on top of the map.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="tab" id="wizard-step-paper-size">
|
||||
<div class="row" id="paper-size">
|
||||
<div class="col-lg-6">
|
||||
<div align="center">
|
||||
<fieldset>
|
||||
<legend>{% trans "Paper size" %} {% trans "(width x height)" %}</legend>
|
||||
<div id="papersize_formline">
|
||||
{{ form.paper_width_mm }}mm <i class="fas fa-arrows-alt-h"></i>
|
||||
×
|
||||
{{ form.paper_height_mm }}mm <i class="fas fa-arrows-alt-v"></i>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div height="1ex"> </div>
|
||||
<div align="center">
|
||||
<canvas id="paper_canvas" width="400" height="400" align="center">
|
||||
</div>
|
||||
</canvas>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div id='single_page_sizes'>
|
||||
<fieldset>
|
||||
<legend>Paper size suggestions</legend>
|
||||
{{ papersize_suggestions }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id='multi_page_sizes' style='display: none'>
|
||||
<fieldset>
|
||||
<legend>Multipage size suggestions</legend>
|
||||
{{ multipage_papersize_suggestions }}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-error" id="paper-size-loading-error">
|
||||
{% blocktrans %}An error occured while retrieving compatible paper sizes.{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info" id="paper-size-loading">
|
||||
<i class="fas fa-redo"></i>
|
||||
{% blocktrans %}Calculating available paper formats for your map...{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Paper format and size" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Select the desired format, size and orientation for your map.
|
||||
{% endblocktrans %}
|
||||
{% blocktrans trimmed %}
|
||||
You can choose from the suggested standard sizes, the "best
|
||||
fit" size for the map area you selected, or specify a width
|
||||
and height of your own choice.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="row justify-content-around">
|
||||
<div class="col-lg-2">
|
||||
<button type="button" class="btn btn-default" id="prevlink" onclick="nextPrev(-1)"><i class="fas fa-arrow-left"></i> {% trans "Back" %}</button>
|
||||
</div>
|
||||
<div class="stepwizard col-lg-6">
|
||||
<div class="stepwizard-row setup-panel">
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-globe active"></i></i>
|
||||
<p>{% trans "Map area" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-file"></i></i>
|
||||
<p>{% trans "Layout" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-image"></i></i>
|
||||
<p>{% trans "Style" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-bars"></i></i>
|
||||
<p>{% trans "Overlays" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-file"></i></i>
|
||||
<p>{% trans "Paper" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-check"></i></i>
|
||||
<p>{% trans "Submit" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2" style="align: right">
|
||||
<button type="button" class="btn btn-success" id="nextlink" onclick="nextPrev(1)"><i class="fas fa-arrow-right"></i> {% trans "Next" %}</button>
|
||||
<button id="formsubmit" type="submit" class="btn btn-success" value="submit" onclick="$('#submitme').click()">
|
||||
{% trans "Generate" %} <i class="fas fa-check-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load extratags %}
|
||||
|
||||
<div class="tab" id="wizard-step-stylesheet">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<fieldset>
|
||||
<legend>{% trans "Stylesheet" %}</legend>
|
||||
{{ form.stylesheet }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img id="style-preview" align="right" src="/media/img/style/{{request.session.new_stylesheet}}.jpg"/> {# TODO: need to pick first style from list here #}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Map stylesheet" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The map stylesheet determines the style and appearance of the map itself.
|
||||
Note that the stylesheet also drives what details will be visible on the map.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -81,37 +81,7 @@ $('#error-modal').modal('show')
|
|||
|
||||
{% block page %}
|
||||
|
||||
{% if form.errors %}
|
||||
<div class="modal" tabindex="-1" role="dialog" id="error-modal">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Form Errors</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{field.label}} {{ error|escape }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="alert alert-danger">
|
||||
<strong>{{ error|escape }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "./new-parts/error-messages.html" %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
@ -119,376 +89,20 @@ $('#error-modal').modal('show')
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-around">
|
||||
<div class="col-lg-2">
|
||||
<button type="button" class="btn btn-default" id="prevlink" onclick="nextPrev(-1)"><i class="fas fa-arrow-left"></i> {% trans "Back" %}</button>
|
||||
</div>
|
||||
<div class="stepwizard col-lg-6">
|
||||
<div class="stepwizard-row setup-panel">
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-globe active"></i></i>
|
||||
<p>{% trans "Map area" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-file"></i></i>
|
||||
<p>{% trans "Layout" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-image"></i></i>
|
||||
<p>{% trans "Style" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-bars"></i></i>
|
||||
<p>{% trans "Overlays" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-file"></i></i>
|
||||
<p>{% trans "Paper" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="stepwizard-step">
|
||||
<i class="btn btn-light btn-circle"><i class="fas fa-check"></i></i>
|
||||
<p>{% trans "Submit" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-2" style="align: right">
|
||||
<button type="button" class="btn btn-success" id="nextlink" onclick="nextPrev(1)"><i class="fas fa-arrow-right"></i> {% trans "Next" %}</button>
|
||||
<button id="formsubmit" type="submit" class="btn btn-success" value="submit" onclick="$('#submitme').click()">
|
||||
{% trans "Generate" %} <i class="fas fa-check-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% include "./new-parts/step-bar.html" %}
|
||||
|
||||
<form id="mainfrm" method="post" enctype="multipart/form-data" onkeydown="return event.key != 'Enter';" action="{% url "new" %}#submitmapform">
|
||||
{{ form.mode }}
|
||||
|
||||
<div class="row" style="margin-top: 30px;" >
|
||||
<div class="col-lg-12" >
|
||||
|
||||
<div class="tab" style="display: block" id="wizard-step-location">
|
||||
|
||||
<ul class="nav nav-tabs" id="locTabs">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="step-location-bbox-tab" data-bs-toggle="tab" data-bs-target="#step-location-bbox" type="button" role="tab" aria-controls="bt-step-location-bbox" aria-selected="true"><i class="fas fa-globe-africa"></i> {% trans "Geographic area" %}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="step-location-admin-tab" data-bs-toggle="tab" data-bs-target="#step-location-admin" type="button" role="tab" aria-controls="bt-step-location-bbox" aria-selected="false"><i class="fas fa-search-location"></i> {% trans "City search" %}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="step-location-file-tab" data-bs-toggle="tab" data-bs-target="#step-location-file" type="button" role="tab" aria-controls="bt-step-location-file" aria-selected="false"><i class="fas fa-upload"></i> {% trans "File upload" %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="step-location-tabs">
|
||||
<div class="tab-pane active" id="step-location-bbox" role="tabpanel">
|
||||
{{ form.bbox }}
|
||||
</div>
|
||||
<div class="tab-pane" id="step-location-admin" role="tabpanel">
|
||||
<div style="position: relative;">
|
||||
{{ form.administrative_city }}
|
||||
<i class="fas fa-redo suggest-icon" id="loading-icon"></i>
|
||||
<i class="fas fa-exclamation-triangle suggest-icon" id="error-icon"></i>
|
||||
<ul id="suggest" class="dropdown-menu" role="menu"
|
||||
aria-labelledby="id_administrative_city">
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
{{ form.administrative_osmid }}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="step-location-file" role="tabpanel">
|
||||
<div class="col-lg-12">
|
||||
<fieldset id="track-file">
|
||||
<legend><i class="fas fa-file-upload"></i> {% trans "Upload files" %}</legend>
|
||||
{{ form.uploadfile }}
|
||||
<tt id="file-list">
|
||||
</tt>
|
||||
</fieldset>
|
||||
|
||||
<div style='height: 2ex'></div>
|
||||
|
||||
<div class="alert alert-secondary">
|
||||
{% blocktrans trimmed %}
|
||||
Upload GPX, Umap, or general GeoJSON files here.
|
||||
<br/>
|
||||
You can select multiple files at once. If you re-open the
|
||||
file selection dialog to select files, your selection will
|
||||
replace the previous one.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
|
||||
<div style='height: 2ex'></div>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
{% blocktrans trimmed %}
|
||||
<b>Note:</b> The uploaded files will not be made available to anyone, the map created using this
|
||||
file <b>will be publicly visible</b> though.<br/>Do <b>NOT</b> upload any data you don't want to be publicly
|
||||
visible.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Geographic area selection" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Start by choosing the city or geographic area you want to
|
||||
render. Suggestions will appear as you start typing. If you can't find the city
|
||||
you want, or if you prefer to choose the exact area you want to render
|
||||
yourself, select the <em>Geographic area</em> tab and use the mini-map to set
|
||||
the limits of the rendered area.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab" id="wizard-step-layout">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<fieldset>
|
||||
<legend>{% trans "Layout" %}</legend>
|
||||
{{ form.layout }}
|
||||
</fieldset>
|
||||
<fieldset id="fieldset-indexer">
|
||||
<legend>{% trans "Indexer" %}</legend>
|
||||
{{ form.indexer }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img id="layout-preview" align="right"
|
||||
src="/media/img/layout/{{request.session.new_layout}}.png"
|
||||
srcset="/media/img/layout/{{request.session.new_layout}}.png, /media/img/layout/{{request.session.new_layout}}-1.5x.png 1.5x, /media/img/layout/{{request.session.new_layout}}-2x.png 2x"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Map layout" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The map layout determines how the map and the index are
|
||||
rendered. The <em>Multi-page layout</em> produces a booklet very suitable for
|
||||
printing and binding.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab" id="wizard-step-stylesheet">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<fieldset>
|
||||
<legend>{% trans "Stylesheet" %}</legend>
|
||||
{{ form.stylesheet }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img id="style-preview" align="right" src="/media/img/style/{{request.session.new_stylesheet}}.jpg"/> {# TODO: need to pick first style from list here #}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Map stylesheet" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The map stylesheet determines the style and appearance of the map itself.
|
||||
Note that the stylesheet also drives what details will be visible on the map.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab" id="wizard-step-overlay">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<fieldset>
|
||||
<legend>{% trans "Overlays" %}</legend>
|
||||
{{ form.overlay }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<img id="overlay-preview" align="right" src="/media/img/empty.png"/> {# TODO: select default here, too #}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Map overlays" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Overlays render extra objects on top of the chosen base style.
|
||||
Multiple overlays can be selected to add different kinds of additional information on top of the map.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab" id="wizard-step-paper-size">
|
||||
<div class="row" id="paper-size">
|
||||
<div class="col-lg-6">
|
||||
<div align="center">
|
||||
<fieldset>
|
||||
<legend>{% trans "Paper size" %} {% trans "(width x height)" %}</legend>
|
||||
<div id="papersize_formline">
|
||||
{{ form.paper_width_mm }}mm <i class="fas fa-arrows-alt-h"></i>
|
||||
×
|
||||
{{ form.paper_height_mm }}mm <i class="fas fa-arrows-alt-v"></i>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div height="1ex"> </div>
|
||||
<div align="center">
|
||||
<canvas id="paper_canvas" width="400" height="400" align="center">
|
||||
</div>
|
||||
</canvas>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div id='single_page_sizes'>
|
||||
<fieldset>
|
||||
<legend>Paper size suggestions</legend>
|
||||
{{ papersize_suggestions }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id='multi_page_sizes' style='display: none'>
|
||||
<fieldset>
|
||||
<legend>Multipage size suggestions</legend>
|
||||
{{ multipage_papersize_suggestions }}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-error" id="paper-size-loading-error">
|
||||
{% blocktrans %}An error occured while retrieving compatible paper sizes.{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info" id="paper-size-loading">
|
||||
<i class="fas fa-redo"></i>
|
||||
{% blocktrans %}Calculating available paper formats for your map...{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Paper format and size" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Select the desired format, size and orientation for your map.
|
||||
{% endblocktrans %}
|
||||
{% blocktrans trimmed %}
|
||||
You can choose from the suggested standard sizes, the "best
|
||||
fit" size for the map area you selected, or specify a width
|
||||
and height of your own choice.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab" id="wizard-step-lang-title">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<fieldset>
|
||||
<legend>{% trans "Map title" %}</legend>
|
||||
{{ form.maptitle }}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<fieldset>
|
||||
<legend>{% trans "Locale" %}</legend>
|
||||
<div class="dropdown">
|
||||
<button id="map_language_button" type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expand="false">en_US.UTF-8</button>
|
||||
<!-- {{ form.map_language.value }} -->
|
||||
</button>
|
||||
<input type="hidden" name="{{ form.map_language.name }}" id="{{ form.map_language.name }}" value="en_US.UTF-8"/>
|
||||
<ul id="maplang_choices" class="dropdown-menu bg-light" style="height: 400px; overflow-y: auto;" area-labeledby="map_language_button">
|
||||
{% for choice in form.map_language.field.choices %}
|
||||
<li><a class="dropdown-item" href="#" data-langcode="{{choice.0}}" onclick="$('#map_language_button').html($(this).html()); $('#{{ form.map_language.name }}').val('{{choice.0}}');">{{choice.1}} ({{choice.0|locale_base}})</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<br/>
|
||||
<fieldset>
|
||||
<legend>{% trans "Your Email address (for notifications, optional)" %}</legend>
|
||||
{{ form.submittermail }}
|
||||
{% if SUBMITTER_MAIL_LIFETIME %}
|
||||
({% trans "will be deleted after" %} {{ SUBMITTER_MAIL_LIFETIME }} {% trans "hours" %})
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 30px;">
|
||||
<div class="col-lg-12">
|
||||
<fieldset id="summary">
|
||||
<legend>{% trans "Summary" %}</legend>
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr><td>{% trans "Location:" %}</td><td id="summary-location"></td></tr>
|
||||
<tr><td>{% trans "Layout:" %}</td><td id="summary-layout"></td></tr>
|
||||
<tr><td>{% trans "Stylesheet:" %}</td><td id="summary-stylesheet"></td></tr>
|
||||
<tr><td>{% trans "Overlay:" %}</td><td id="summary-overlay"></td></tr>
|
||||
<tr><td>{% trans "Paper format:" %}</td><td id="summary-paper-size"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h4>{% trans "Almost there!" %}</h4>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You're almost ready to request to map rendering! Select the map language,
|
||||
eventually adjust the title of your map, and you're good to go!
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% include "./new-parts/area-step.html" %}
|
||||
{% include "./new-parts/layout-step.html" %}
|
||||
{% include "./new-parts/stylesheet-step.html" %}
|
||||
{% include "./new-parts/overlay-step.html" %}
|
||||
{% include "./new-parts/papersize-step.html" %}
|
||||
{% include "./new-parts/final-step.html" %}
|
||||
</div>
|
||||
|
||||
<input id="submitme" type="submit" style="display: none"/>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -50,6 +50,8 @@ import www.settings
|
|||
|
||||
import psycopg2
|
||||
|
||||
from ipware import get_client_ip
|
||||
|
||||
LOG = logging.getLogger('maposmatic')
|
||||
|
||||
def index(request):
|
||||
|
@ -129,14 +131,16 @@ def donate_thanks(request):
|
|||
"""The thanks for donation page."""
|
||||
return render(request, 'maposmatic/donate-thanks.html')
|
||||
|
||||
def create_upload_file(job, file):
|
||||
def create_upload_file(job, file, keep_until = None):
|
||||
first_line = file.readline().decode("utf-8-sig")
|
||||
LOG.info("firstline type %s" % type(first_line))
|
||||
if first_line.startswith(u'<?xml'):
|
||||
file_type = 'gpx'
|
||||
else:
|
||||
file_type = 'umap'
|
||||
file_instance = models.UploadFile(uploaded_file = file, file_type = file_type)
|
||||
file_instance = models.UploadFile(uploaded_file = file,
|
||||
file_type = file_type,
|
||||
keep_until = keep_until)
|
||||
file_instance.save()
|
||||
file_instance.job.add(job)
|
||||
|
||||
|
@ -177,11 +181,21 @@ def new(request):
|
|||
.queue_size(job.queue) + 1)
|
||||
job.nonce = helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
|
||||
|
||||
client_ip, is_routable = get_client_ip(request)
|
||||
if www.settings.EXTRA_IP is None or ( client_ip is not None and client_ip == www.settings.EXTRA_IP ):
|
||||
job.extra_text = www.settings.EXTRA_FOOTER
|
||||
job.logo = "bundled:osm-logo.svg"
|
||||
job.extra_logo = www.settings.EXTRA_LOGO
|
||||
|
||||
job.save()
|
||||
|
||||
files = request.FILES.getlist('uploadfile')
|
||||
if form.cleaned_data.get('delete_files_after_rendering'):
|
||||
keep_until = None
|
||||
else:
|
||||
keep_until = '1999-01-01'
|
||||
for file in files:
|
||||
create_upload_file(job, file)
|
||||
create_upload_file(job, file, keep_until)
|
||||
|
||||
return HttpResponseRedirect(reverse('map-by-id-and-nonce',
|
||||
args=[job.id, job.nonce]))
|
||||
|
@ -350,6 +364,7 @@ def recreate(request):
|
|||
|
||||
newjob.logo = job.logo
|
||||
newjob.extra_logo = job.extra_logo
|
||||
newjob.extra_text = job.extra_text
|
||||
|
||||
newjob.queue = "default"
|
||||
if job.layout.startswith('multi'):
|
||||
|
|
|
@ -350,10 +350,13 @@ logconfig.setup_maposmatic_logging(
|
|||
LOG = logging.getLogger('maposmatic')
|
||||
|
||||
# File upload settings
|
||||
# make sure files and dirs are group-writable
|
||||
# render user should be in www-data group to be able to clean up
|
||||
|
||||
# make sure that files that exceed FILE_UPLOAD_MAX_MEMORY_SIZE
|
||||
# are still readable
|
||||
FILE_UPLOAD_PERMISSIONS = 0o644
|
||||
FILE_UPLOAD_PERMISSIONS = 0o664
|
||||
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o664
|
||||
|
||||
# Maintenance mode settings
|
||||
|
||||
MAINTENANCE_MODE = False # True or False, *NOT* None
|
||||
MAINTENANCE_MODE_IGNORE_IP_ADDRESSES = ('217.146.146.90',)
|
||||
|
|
|
@ -179,8 +179,10 @@ WEBLATE_BASE_URL = 'https://translate.get-map.org/'
|
|||
CONTACT_EMAIL = 'your-name@example.org'
|
||||
CONTACT_CHAT = 'irc://irc.oftc.org/#maposmatic'
|
||||
|
||||
# custom footer text
|
||||
EXTRA_FOOTER = ''
|
||||
# custom branding
|
||||
EXTRA_FOOTER = '' # extra text for the annotation footer
|
||||
EXTRA_LOGO = '' # custom logo to put in header
|
||||
EXTRA_IP = None # optionally: add extra info for specific client IP only
|
||||
|
||||
# show this in a warning box on top of the page when set
|
||||
MAINTENANCE_NOTICE = ''
|
||||
|
|
Ładowanie…
Reference in New Issue