{# Shared advertisement form fields (create + edit). Rendered inside a
supplied by the page. Pass: form, ad. #} {{ form.hidden_tag() }}
{{ form.title(class="form-control" ~ (" is-invalid" if form.title.errors else ""), data_ad_title="1", maxlength="150") }} {% for e in form.title.errors %}
{{ e }}
{% endfor %}
{{ form.description(class="form-control" ~ (" is-invalid" if form.description.errors else ""), rows="3", data_ad_desc="1") }} {% for e in form.description.errors %}
{{ e }}
{% endfor %}
{{ form.redirect_url(class="form-control" ~ (" is-invalid" if form.redirect_url.errors else ""), placeholder="https://example.com") }} {% for e in form.redirect_url.errors %}
{{ e }}
{% endfor %}
{{ form.advertisement_type(class="form-select", data_ad_type="1") }}
Static image advertisement displayed throughout the marketplace. Advertisement shown when users unlock additional chat messages. Choose whether users will watch an image or a video advertisement.
{# Placement dropdown — shown for Banner. The stays in the DOM even when hidden (for Banner) so ``media_type=image`` is always submitted; the backend and its Banner+Video validation are unchanged. #}
{{ form.media_type(class="form-select", data_ad_media_type="1") }}
Image Advertisement — static rewarded advertisement. Video Advertisement — watch-to-unlock advertisement supporting MP4/WebM uploads.
{# Shown in place of the selector when Banner is chosen (image-only). #}
Banner advertisements support images only.
{# Image fields — shown when media type is "image". #}
{% if ad and ad.image_path %}
Current image
Current image. Uploading a new one replaces it; leaving this empty keeps it.
{% endif %}

Drag & drop an image here, or click to browse

PNG, JPG, JPEG, WEBP · Max {{ config.MAX_AD_IMAGE_SIZE // (1024 * 1024) }} MB

{{ form.image(class=("visually-hidden" ~ (" is-invalid" if form.image.errors else "")), accept=".png,.jpg,.jpeg,.webp", data_ad_image="1", data_dropzone_input="1") }}
{% for e in form.image.errors %}
{{ e }}
{% endfor %}
PNG, JPG, JPEG or WEBP. Optional — icon-only advertisements are supported.
Preparing image…
{# Video fields — shown when media type is "video". #}
{# Current video (edit only) — filename, preview and a remove option. #} {% if ad and ad.media_url %}
{{ ad.media_url.split('/')|last }}
Preview current video
{{ form.remove_video(class="form-check-input") }}

Uploading a new file replaces the current video; leaving this empty keeps it.

{% endif %}

Drag & drop a video here, or click to browse

MP4, WebM, Ogg · Max {{ config.MAX_AD_VIDEO_SIZE // (1024 * 1024) }} MB · Recommended 720p, 20–30s

{{ form.video(class=("visually-hidden" ~ (" is-invalid" if form.video.errors else "")), accept=".mp4,.webm,.ogg", data_ad_video_input="1", data_dropzone_input="1") }}
{% for e in form.video.errors %}
{{ e }}
{% endfor %}
The file is stored on the Student Bazaar server and used directly in rewarded advertisements. An image (above) is used as a fallback if the video fails to load.
{{ form.video_duration(class="form-control" ~ (" is-invalid" if form.video_duration.errors else ""), min="5", max="600") }} {% for e in form.video_duration.errors %}
{{ e }}
{% endfor %}
Default 20 seconds.
{{ form.start_at(class="form-control" ~ (" is-invalid" if form.start_at.errors else "")) }} {% for e in form.start_at.errors %}
{{ e }}
{% endfor %}
{{ form.end_at(class="form-control" ~ (" is-invalid" if form.end_at.errors else "")) }} {% for e in form.end_at.errors %}
{{ e }}
{% endfor %}
Leave dates empty to run immediately and indefinitely. Scheduling is automatic.
{{ form.is_active(class="form-check-input") }}
Cancel