Commit 324d2797 authored by Kostas Papadimitriou's avatar Kostas Papadimitriou
Browse files

Merge branch 'feature-branding' into develop

parents 28fb25d2 0967d42d
......@@ -920,6 +920,211 @@ To verify that the image was registered successfully use:
.. snf-branding
Snf-branding allows you to adapt Astakos, Pithos and Cyclades UI to your
company’s visual identity.
The settings for the snf-branding application can be found in the configuration
file <path_to_conf_file>/15-snf-branding.conf
By default, the service name is Synnefo and the company name is GRNET.
These name and their respective logos and urls are used throughout Astakos,
Pithos and Cyclades UI.
**Name and logos:**
The first group of branding customization regards company and service info.
You can overwrite the company and the service name and url respectively by
uncommenting and setting the following:
.. code-block:: console
BRANDING_COMPANY_NAME and BRANDING_COMPANY_URL appear in Astakos, Cyclades and
Pithos footer.
In addition to that, BRANDING_COMPANY_NAME appears in Astakos project tab.
**Copyright options:**
By default, no copyright message is shown in the UI. If you want to make it
visible to the footer of Astakos, Cyclades and Pithos UI, you can uncomment and
set True the setting:
.. code-block:: console
Copyright message defaults to 'Copyright (c) 2011-<current_year> GRNET.'
but you can alter it using the setting:
.. code-block:: console
#BRANDING_COPYRIGHT_MESSAGE = 'Copyright (c) 2011-<current_year> GRNET S.A.'
The branding-related images are presented in the following table:
=============== ============================ =========
Image Name/extension convention Usage
=============== ============================ =========
Favicon favicon.ico Favicon for all services
Dashboard logo dashboard_logo.png Visible in all Astakos UI pages
Compute logo compute_logo.png Visible in all Cyclades UI pages
Console logo console_logo.png Visible in Console Page for VMs
Storage logo storage_logo.png Visible in Pithos UI pages
=============== ============================ =========
There are two methods available for replacing all or individual
branding-related images:
1. Create a new directory and place there some or all of your images.
If you want to replace all images, keep the name/extension conventions as
indicated in the above table and set:
.. code-block:: console
to the relative path of your directory.
If you wish to replace individual images, **do not uncomment**
BRANDING_IMAGE_MEDIA_URL but instead provide a relative path,
pointing to the file inside your directory for each BRANDING_<image>_URL that
will be replaced.
2. Upload some or all of your images to a server and replace each
BRANDING_<image>_URL with the absolute url of the image ( i.e.
Note that the alternative text for each image tag inside html documents is
alt=“BRANDING_SERVICE_NAME {Dashboad, Compute. Console, Storage}” respectively.
.. note:: Retina optimised images:
Synnefo UI is optimised for Retina displays. As far as images are concerned,
`retina.js <>`_ is used.
Retina.js checks each image on a page to see if there is a high-resolution
version of that image on your server. If a high-resolution variant exists,
the script will swap in that image in-place.
The script assumes you use `Apple's prescribed high-resolution modifier (@2x)
.html#//apple_ref/doc/uid/TP40010156-CH15-SW1>`_ to denote high-resolution
image variants on your server.
For each of the images that you wish the script to replace you must have a
high-resolution variant in the same folder named correctly and it will be
detected automatically. For example if your image is in <my_directory> and is
named "my_image.jpg" the script will look in the same directory for an image
named "my_image@2x.jpg".
In case that you don’t want to use a high-resolution image, the
normal-resolution image will be visible.
Branding-related issues:
Although, it is not strictly-speaking branding-related, further verbal
customization is feasible.
The output of all email *.txt files will be already customized to contain your
company and service names but you can further alter their content if you feel
it best fits your needs using django’s TEMPLATE_DIRS setting.
In order to overwrite a template you need to create a new one with the same
name, place it to a folder of your choice and provide its path to the
For more information visit
Below is a list of all emails sent by Synnefo to users along with a short
description and a link to their content:
* ``snf-astakos-app/astakos/im/templates/im/email.txt`` Base email template.
Contains a contact email and a “ thank you” message.
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/activation_email.txt`` Email sent to user that prompts him/her to click on a link provided to activate the account.
Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/invitation.txt:`` Email sent to invited user. He/she has to click on a link provided to activate the account.
Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/switch_accounts_email.txt`` Email sent to user upon his/her request to associate this email address with a
shibboleth account. He/she has to click on a link provided to activate the
association. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/welcome_email.txt`` Email sent to inform the user that his/ her account has been activated. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/registration/email_change_email.txt`` Email sent to user when he/she has requested new email address assignment. The user has to click on link provided to validate this action. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/registration/password_email.txt`` Email sent for resetting password purpose. The user has to click on link provided to validate this action. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_approval_notification.txt`` Informs the project owner that his/her project has been approved. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_denial_notification.txt`` Informs the project owner that his/her project application has been denied explaining the reasons. Extends “email.txt”
( Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_membership_change_notification.txt`` An email is sent to a user containing information about his project membership (whether he has been accepted, rejected or removed). Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_membership_enroll_notification.txt`` Informs a user that he/she has been enrolled to a project. Extends “email.txt”
( Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_membership_leave_request_notification.txt`` An email is sent to the project owner to make him aware of a user having requested to leave his project. Extends “email.txt”
( Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_membership_request_notification.txt`` An email is sent to the project owner to make him/her aware of a user having requested to join his project. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_suspension_notification.txt`` An email is sent to the project owner to make him/her aware of his/her project having been suspended. Extends “email.txt”
(Link: )
* ``snf-astakos-app/astakos/im/templates/im/projects/project_termination_notification.txt`` An email is sent to the project owner to make him/her aware of his/her project having been terminated. Extends “email.txt”
(Link: )
.. warning:: Django templates language:
If you choose to overwrite these email templates, be mindful of the necessary
information contained in django template variables that must not be omitted,
such as the activation link for activating one’s account and many more.
These variables are contained into {{}} inside the templates.
.. RabbitMQ
RabbitMQ Broker
......@@ -35,7 +35,7 @@ import logging
import socket
from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
from synnefo_branding.utils import render_to_string
from django.core.mail import send_mail, get_connection
from django.core.urlresolvers import reverse
from django.template import Context, loader
......@@ -147,7 +147,7 @@ SWITCH_ACCOUNT_LINK_SENT = 'This email is already associated wi
inside the verification email.'
NOTIFICATION_SENT = 'Your request for an account has been submitted successfully, and is now pending approval. \
You will be notified by email in the next few days. \
Thanks for your interest in ~okeanos! The GRNET team.'
Thanks for your interest!'
ACTIVATION_SENT = 'An email containing your activation link has been sent to your email address.'
REGISTRATION_COMPLETED = 'Your registration completed successfully. You can now login to your new account!'
from django.conf import settings
from synnefo_branding import settings as synnefo_settings
# Set the expiration time of newly created auth tokens
# to be this many hours after their creation time.
......@@ -45,7 +46,7 @@ MODERATION_ENABLED = getattr(settings, 'ASTAKOS_MODERATION_ENABLED', True)
BASEURL = getattr(settings, 'ASTAKOS_BASEURL', '')
# Set service name
SITENAME = getattr(settings, 'ASTAKOS_SITENAME', 'Synnefo Cloud')
SITENAME = getattr(settings, 'ASTAKOS_SITENAME', synnefo_settings.SERVICE_NAME)
# Set recaptcha keys
......@@ -59,8 +60,8 @@ RECAPTCHA_ENABLED = getattr(settings, 'ASTAKOS_RECAPTCHA_ENABLED', False)
BILLING_FIELDS = getattr(settings, 'ASTAKOS_BILLING_FIELDS', ['is_active'])
# Queue for billing.
QUEUE_CONNECTION = getattr(settings, 'ASTAKOS_QUEUE_CONNECTION', None) # Example: 'rabbitmq://guest:guest@localhost:5672/astakos'
# Example: 'rabbitmq://guest:guest@localhost:5672/astakos'
# Set where the user should be redirected after logout
LOGOUT_NEXT = getattr(settings, 'ASTAKOS_LOGOUT_NEXT', '')
......@@ -85,7 +86,7 @@ PROFILE_MESSAGES = getattr(settings, 'ASTAKOS_PROFILE_MESSAGES', [])
# messages to display as extra actions in account forms
# e.g. {'': 'Back to ~okeanos'}
# e.g. {'': 'Back to <service_name>'}
# The number of unsuccessful login requests per minute allowed for a specific user
......@@ -106,56 +107,56 @@ LOGGING_LEVEL = getattr(settings, 'ASTAKOS_LOGGING_LEVEL', INFO)
# Configurable email subjects
'Invitation to %s alpha2 testing' % SITENAME)
'Invitation to %s' % SITENAME)
'Welcome to %s alpha2 testing' % SITENAME)
'Welcome to %s' % SITENAME)
'Feedback from %s alpha2 testing' % SITENAME)
'Feedback from %s' % SITENAME)
'%s alpha2 testing account activation is needed' % SITENAME)
'%s account activation is needed' % SITENAME)
'%s alpha2 testing account created (%%(user)s)' % SITENAME)
'%s account created (%%(user)s)' % SITENAME)
'%s alpha2 testing group created (%%(group)s)' % SITENAME)
'%s group created (%%(group)s)' % SITENAME)
'%s alpha2 testing account activated (%%(user)s)' % SITENAME)
'%s account activated (%%(user)s)' % SITENAME)
'Email change on %s alpha2 testing' % SITENAME)
'Email change on %s ' % SITENAME)
'Password reset on %s alpha2 testing' % SITENAME)
'Password reset on %s ' % SITENAME)
'%s alpha2 testing project application created (%%(name)s)' % SITENAME)
'%s project application created (%%(name)s)' % SITENAME)
'%s alpha2 testing project application approved (%%(name)s)' % SITENAME)
'%s project application approved (%%(name)s)' % SITENAME)
'%s alpha2 testing project application denied (%%(name)s)' % SITENAME)
'%s project application denied (%%(name)s)' % SITENAME)
'%s alpha2 testing project terminated (%%(name)s)' % SITENAME)
'%s project terminated (%%(name)s)' % SITENAME)
'%s alpha2 testing project suspended (%%(name)s)' % SITENAME)
'%s testing project suspended (%%(name)s)' % SITENAME)
'%s alpha2 testing project membership changed (%%(name)s)' % SITENAME)
'%s testing project membership changed (%%(name)s)' % SITENAME)
'%s alpha2 testing project enrollment (%%(name)s)' % SITENAME)
'%s testing project enrollment (%%(name)s)' % SITENAME)
'%s alpha2 testing project membership request (%%(name)s)' % SITENAME)
'%s testing project membership request (%%(name)s)' % SITENAME)
'%s alpha2 testing project membership leave request (%%(name)s)' % SITENAME)
'%s testing project membership leave request (%%(name)s)' % SITENAME)
# Set the quota holder component URI
......@@ -214,8 +215,7 @@ LINKEDIN_TOKEN = getattr(settings, 'ASTAKOS_LINKEDIN_TOKEN', '')
# URL to redirect the user after successful login when no next parameter is set
LOGIN_SUCCESS_URL = getattr(settings, 'ASTAKOS_LOGIN_SUCCESS_URL','/im/landing')
# Whether or not to display projects in astakos menu
......@@ -59,7 +59,7 @@ $(document).ready(function(){
var title = || el.verbose_name ||;
if (! { return }
if (el.cloudbar.icon) {
slink.append($('<img alt="'+title+'" src="'+cssloc+el.cloudbar.icon+'"/>'));
slink.append($('<img alt="'+title+'" src="'+el.cloudbar.icon+'"/>'));
} else {
......@@ -41,6 +41,7 @@ img.right { margin:0 0 1em 1em; float:right;}
/* container */
.container .wrapper { padding-bottom:100px;}
.mainlogo h1 { line-height:100%; font-size:1em; }
.mainlogo img { max-width:100%; max-height:300px; }
.container .navigation { margin:20px 0 0; font-size:1.154em; height:95px;}
.container .navigation ul { margin:5px 0; padding:0;}
.container .navigation ul + ul { font-size:0.933em; }
// retina.js, a high-resolution image swapper (, v0.0.2
(function(){function t(e){this.path=e;var t=this.path.split("."),n=t.slice(0,t.length-1).join("."),r=t[t.length-1];this.at_2x_path=n+"@2x."+r}function n(e){this.el=e,this.path=new t(this.el.getAttribute("src"));var n=this;this.path.check_2x_variant(function(e){e&&n.swap()})}var e=typeof exports=="undefined"?window:exports;e.RetinaImagePath=t,t.confirmed_paths=[],t.prototype.is_external=function(){return!!this.path.match(/^https?\:/i)&&!this.path.match("//"+document.domain)},t.prototype.check_2x_variant=function(e){var n,r=this;if(this.is_external())return e(!1);if(this.at_2x_path in t.confirmed_paths)return e(!0);n=new XMLHttpRequest,"HEAD",this.at_2x_path),n.onreadystatechange=function(){return n.readyState!=4?e(!1):n.status>=200&&n.status<=399?(t.confirmed_paths.push(r.at_2x_path),e(!0)):e(!1)},n.send()},e.RetinaImage=n,n.prototype.swap=function(e){function n(){t.el.complete?(t.el.setAttribute("width",t.el.offsetWidth),t.el.setAttribute("height",t.el.offsetHeight),t.el.setAttribute("src",e)):setTimeout(n,5)}typeof e=="undefined"&&(e=this.path.at_2x_path);var t=this;n()},e.devicePixelRatio>1&&(window.onload=function(){var e=document.getElementsByTagName("img"),t=[],r,i;for(r=0;r<e.length;r++)i=e[r],t.push(new n(i))})})();
\ No newline at end of file
--- A translation in English follows ---
Έχει ενεργοποιηθεί ο παρακάτω λογαριασμός:
Email: {{}}
First name: {{user.first_name}}
Last name: {{user.last_name}}
Is active: {{user.is_active}}
Level: {{user.level}}
Invitations: {{user.invitations}}
The following account has been activated:
Email: {{}}
......@@ -2,7 +2,7 @@
{% load filters %}
{% block page.title %}Profile{% endblock %}
{% block page.title %}Overview{% endblock %}
{% block page.nav.classes %}{% endblock %}
{% block page.quicknav.items %}
--- A translation in English follows ---
Έχει δημιουργηθεί ο παρακάτω λογαριασμός:
Email: {{}}
First name: {{user.first_name}}
Last name: {{user.last_name}}
Is active: {{user.is_active}}
Level: {{user.level}}
Invitations: {{user.invitations}}
Για την ενεργοποίησή του μπορείτε να χρησιμοποιήσετε το command line εργαλείο
snf-manage user-modify {{ }} --accept
The following account has been created:
Email: {{}}
......@@ -25,4 +9,4 @@ Invitations: {{user.invitations}}
For its activation you can use the command line tool
snf-manage user-modify {{ }} --accept
snf-manage user-modify {{ }} --accept
\ No newline at end of file
{% extends "im/email.txt" %}
{% block gr_content %}
Αγαπητέ/η {{ user.realname }},
Έπειτα από αίτημα σας έχει δημιουργηθεί ο λογαρισμός σας για την υπηρεσία {{ site_name }} της ΕΔΕΤ κατά την alpha2 (δεύτερη δοκιμαστική) φάση λειτουργίας της.
Για να τον ενεργοποιήσετε, χρησιμοποιήστε το σύνδεσμο: {{ url }}
{% endblock %}
{% block gr_note %} {% endblock%}
{% block en_content %}
{% block content %}
Dear {{ user.realname }},
After your request a new account for GRNET's {{ site_name }} service has been created for its alpha2 testing phase.
After your request a new account for {{ BRANDING_COMPANY_NAME|upper }}'s {{ BRANDING_SERVICE_NAME }} service has been created.
To activate the account, please use the link: {{ url }}
{% endblock %}
{% block en_note %} {% endblock%}
{% block note %} {% endblock%}
{% load astakos_tags %}<!doctype html>
{% block starthead %}{% endblock starthead %}
<meta charset="UTF-8">
......@@ -6,7 +7,7 @@
<meta http-equiv="X-UA-Compatible" content="{{ resource.meta.compatibility }}">
{% block page.title %}Accounts{% endblock %} / GRNET Cloud Accounts
{{ BRANDING_SERVICE_NAME }} Dashboard | {% block page.title %}Overview{% endblock %}
<meta name="description" content="">
......@@ -21,7 +22,7 @@
{% endblock %}
{% block page.favicons %}
<link rel="shortcut icon" href="">
<link rel="shortcut icon" href="{{ BRANDING_FAVICON_URL }}" />
<link rel="apple-touch-icon" href="">
{% endblock page.favicons %}
......@@ -53,7 +54,8 @@
<script src="{{ IM_STATIC_URL }}js/jquery-ui-1.8.21.custom.min.js"></script>
<script src="{{ IM_STATIC_URL }}js/jquery.tablesorter.js"></script>
<script src="{{ IM_STATIC_URL }}js/jquery.cookie.js"></script>
<script src="{{ IM_STATIC_URL }}js/mustache.js"></script>
<script src="{{ IM_STATIC_URL }}js/mustache.js"></script>
<script src="{{ IM_STATIC_URL }}js/retina.js"></script>
<script src="{{ IM_STATIC_URL }}js/common.js"></script>
{% endblock headjs %}
{% block endhead %}{% endblock endhead %}
......@@ -80,13 +82,12 @@
<div class="container">
<div class="wrapper">
<div class="wrapper">
{% display_messages %}
<div class="mainlogo">
<a href="{% url %}">
<img src="{{ IM_STATIC_URL }}images/accounts-logo.png" alt="accounts" />
<a href="{% url index %}" title="{{ BRANDING_SERVICE_NAME }} Dashboard">
<img src="{{ BRANDING_DASHBOARD_LOGO_URL }}" alt="{{ BRANDING_SERVICE_NAME }} Dashboard" />
{% block page.nav %}
--- A translation in English follows ---
{% block content %} {% endblock %}
{% block gr_content %} {% endblock %}
{% block gr_note %}
Ο {{ site_name }} είναι η υπηρεσία cloud του ΕΔΕΤ. Περιλαμβάνει τον pithos+, μέσω του οποίου έχετε 50GB αποθηκευτικό χώρο για τα αρχεία σας στο cloud, και τις cyclades, που παρέχουν εικονικές μηχανές (VMs) με διάφορα λειτουργικά συστήματα (OS images).
O {{ site_name }} θα είναι για τις επόμενες λίγες εβδομάδες στη φάση λειτουργίας alpha2. Για τους νέους χρήστες, αρχικά, μέσα από το ενοποιημένο web περιβάλλον θα προσφέρεται ο pithos+ και σε λίγες μέρες (10 περίπου), μόλις ολοκληρωθεί η διαδικασία μετάβασης των χρηστών, και οι cyclades (εικονικές μηχανές). Όσοι χρήστες συμμετείχατε ήδη στην alpha1, θα εξακολουθείτε να βλέπετε, για τις επόμενες 10 ημέρες, τις εικονικές σας μηχανές μέσα απο το και κατόπιν, με τον ίδιο σύνδεσμο, μέσα απο το ενοποιημένο περιβάλλον. Δε χρειάζεται να κάνετε τίποτε, εφόσον έχετε δηλώσει την ίδια διεύθυνση e-mail που είχατε και στην alpha1. H μετάβαση θα γίνει αυτόματα, μετά απο 10 ημέρες.
Αν και έχουμε κάνει ό,τι είναι δυνατό για να εξασφαλίσουμε την ποιότητα της υπηρεσίας, δεν αποκλείεται να εμφανιστούν προβλήματα στο λογισμικό διαχείρισης ή η υπηρεσία να μην είναι διαθέσιμη κατά διαστήματα. Για αυτό το λόγο, σας παρακαλούμε να μη μεταφέρετε ακόμη σημαντικά αρχεία στον pithos+ ή στις εικονικές μηχανές των cyclades.
Αναμένουμε τα σχόλια και τις παρατηρήσεις σας, για να βελτιώσουμε τη λειτουργικότητα και την αξιοπιστία της καινοτομικής αυτής υπηρεσίας.
{% endblock gr_note%}
Για όποιες παρατηρήσεις ή προβλήματα στη λειτουργία της υπηρεσίας μπορείτε να απευθύνεστε στο {{ support }}.
Σας ευχαριστούμε πολύ για τη συμμετοχή στην alpha2 λειτουργία του {{ site_name }}.
Εθνικό Δίκτυο Έρευνας και Τεχνολογίας (ΕΔΕΤ/GRNET)
{% block en_content %} {% endblock %}
{% block en_note %}
Please note:
{{ site_name }} is GRNET's cloud service. It includes pithos+, which offers 50GB of storage space for your files in the cloud and, cyclades, that offer virtual machines (VMs) with various operating systems (OS images).
{{ site_name }} will be, for the next few weeks, in its alpha2 testing phase. New users, will initially have access only to pithos+, and later, after the migration phase from alpha1 to alpha2 is completed (10 days), will have access to cyclades (VMs) as well. Old users, that participated during the alpha1 phase, will continue to have access to their existing VMs, through the, for the next 10 days, and then, through the same link, using the new web interface. There is no need to do anything, as long as your e-mail address is the same as in alpha1 phase. Migration will take place automatically, after 10 days.
This service will remain, for a few weeks, in alpha2. Although every care has been taken to ensure high quality, it is possible there may still be software bugs, or periods of limited service availability. For this reason we kindly request you not to transfer important files to pithos+ or to the virtual machines of cyclades.
We look forward to your feedback, to improve the functionality and reliability of this innovative service.
{% endblock en_note %}
{% block note %}{% endblock note %}
For any remarks or problems you may contact {{ support }}.
Thank you for participating in the alpha2 test of {{ site_name }}.
Thank you for participating in {{ BRANDING_SERVICE_NAME }}.
Greek Research and Technology Network - GRNET
{% extends "im/account_base.html" %}
{% load astakos_tags i18n %}
{% block page.title %}Contact{% endblock %}
{% block body %}
<h2>CONTACT US </h2>
<p>If you would like to contact us for any reason, please feel free to
<h2>{% trans "CONTACT US" %} </h2>
If you would like to contact us for any reason, please feel free to
write <br>your message in the following form. Feedback and suggestions are<br>
also highly appreciated. </p>
also highly appreciated.</p>
<form action="{% url feedback %}" method="post"
class="withlabels">{% csrf_token %}
{% block footer_content %}
<p class="termslink" style="float:right"><a href="{% url latest_terms %}">Terms of service</a></p>
<p>Copyright 2011-2012 <a href="" target="_blank" title="GRNET S.A.">GRNET S.A.</a> All rights reserved.</p>
<p><a href="{{ BRANDING_COMPANY_URL }}" target="_blank" title="{{ BRANDING_COMPANY_NAME }}">{{ BRANDING_COPYRIGHT_MESSAGE }}</a> All rights reserved.</p>
{% endif %}
<div class="software">Powered by <a href="">Synnefo</a> <span class="version">v{{ BRANDING_SYNNEFO_JS_LIB_VERSION }}</span></div>
{% endblock %}
......@@ -3,7 +3,6 @@
{% load filters %}
{% block page.body %}
<h2>HOW IT WORKS</h2>
<div class="centered how-it-works">
<div class="wrap">
{% extends "im/email.txt" %}
{% block gr_content %}
Αγαπητέ/η {{ invitation.realname }},
Ο/Η {{ invitation.inviter.realname }} ({{}}) σας έχει προσκαλέσει να χρησιμοποιήσετε την υπηρεσία {{ site_name }} της ΕΔΕΤ κατά την alpha2 (δεύτερη δοκιμαστική) φάση λειτουργίας της.
Για να αποκτήσετε πρόσβαση, χρησιμοποιήστε το σύνδεσμο: {{ url }}