Commit 39d8d0d95fe1e71212393d1d6900cdf912f2d041
Exists in
master
and in
39 other branches
Merge branch 'master' of https://github.com/interlegis/colab
Showing
10 changed files
with
509 additions
and
2 deletions
Show diff stats
requirements.txt
src/colab/custom_settings.py
... | ... | @@ -107,6 +107,8 @@ INSTALLED_APPS = INSTALLED_APPS + ( |
107 | 107 | 'haystack', |
108 | 108 | 'hitcounter', |
109 | 109 | 'i18n_model', |
110 | + 'mptt', | |
111 | + 'dpaste', | |
110 | 112 | |
111 | 113 | # Own apps |
112 | 114 | 'super_archives', |
... | ... | @@ -207,7 +209,6 @@ TEMPLATE_CONTEXT_PROCESSORS = ( |
207 | 209 | 'django.core.context_processors.tz', |
208 | 210 | 'django.contrib.messages.context_processors.messages', |
209 | 211 | 'django.core.context_processors.request', |
210 | - 'django_browserid.context_processors.browserid', | |
211 | 212 | 'django_mobile.context_processors.is_mobile', |
212 | 213 | 'super_archives.context_processors.mailarchive', |
213 | 214 | 'home.context_processors.robots', |
... | ... | @@ -303,6 +304,18 @@ CONVERSEJS_SHOW_ONLY_ONLINE_USERS = True |
303 | 304 | TASTYPIE_DEFAULT_FORMATS = ['json', ] |
304 | 305 | |
305 | 306 | |
307 | +# Dpaste settings | |
308 | +DPASTE_EXPIRE_CHOICES = ( | |
309 | + ('onetime', _(u'One Time Snippet')), | |
310 | + (3600, _(u'In one hour')), | |
311 | + (3600 * 24 * 7, _(u'In one week')), | |
312 | + (3600 * 24 * 30, _(u'In one month')), | |
313 | + ('never', _(u'Never')), | |
314 | +) | |
315 | +DPASTE_EXPIRE_DEFAULT = DPASTE_EXPIRE_CHOICES[4][0] | |
316 | +DPASTE_DEFAULT_GIST_DESCRIPTION = 'Gist created on Colab' | |
317 | +DPASTE_DEFAULT_GIST_NAME = 'colab_paste' | |
318 | + | |
306 | 319 | try: |
307 | 320 | from local_settings import * |
308 | 321 | except ImportError: | ... | ... |
src/colab/urls.py
... | ... | @@ -28,7 +28,7 @@ urlpatterns = patterns('', |
28 | 28 | |
29 | 29 | url(r'^planet/', include('feedzilla.urls')), |
30 | 30 | |
31 | - url(r'', include('django_browserid.urls')), | |
31 | + url(r'paste/', include('dpaste.urls.dpaste')), | |
32 | 32 | |
33 | 33 | # Uncomment the next line to enable the admin: |
34 | 34 | url(r'^colab/admin/', include(admin.site.urls)), | ... | ... |
... | ... | @@ -0,0 +1,212 @@ |
1 | + | |
2 | +.shortcut { | |
3 | + color: #AAA; | |
4 | + font-size: 13px; | |
5 | + font-weight: 300; | |
6 | + margin-left: 15px; | |
7 | +} | |
8 | + | |
9 | +.form-horizontal .form-group { | |
10 | + margin: 0 0 20px 0; | |
11 | +} | |
12 | + | |
13 | +#id_content{ | |
14 | + width: 100%; | |
15 | + font-family: monospace; | |
16 | + font-size: 14px; | |
17 | + line-height: 16px; | |
18 | +} | |
19 | + | |
20 | +/* ---------------------------------------------------------------------------- | |
21 | + Snippet Details | |
22 | +---------------------------------------------------------------------------- */ | |
23 | +#snippet-diff { | |
24 | + display: none; | |
25 | +} | |
26 | +.snippet-options{ | |
27 | + margin-bottom: 20px; | |
28 | +} | |
29 | + | |
30 | +.snippet-reply { | |
31 | + margin-top: 30px; | |
32 | +} | |
33 | + | |
34 | +.snippet-reply-hidden { | |
35 | + opacity: 0.3; | |
36 | +} | |
37 | + | |
38 | +.snippet-reply-hidden, | |
39 | +.snippet-reply-hidden *{ | |
40 | + cursor: pointer; | |
41 | +} | |
42 | + | |
43 | +.snippet-diff-form { | |
44 | +} | |
45 | + | |
46 | +.snippet-rendered { | |
47 | + color: #666; | |
48 | + font-size: 16px; | |
49 | + line-height: 24px; | |
50 | + max-width: 620px; | |
51 | + font-family: Helvetica, FreeSerif, serif; | |
52 | + font-weight: 300; | |
53 | +} | |
54 | +.diff-form { | |
55 | +/* margin-bottom: 10px;*/ | |
56 | +} | |
57 | + | |
58 | +#diff { | |
59 | + margin-bottom: 10px; | |
60 | +/* display: none;*/ | |
61 | +} | |
62 | + | |
63 | +.tree{ | |
64 | + width: 100%; | |
65 | + line-height: 1.8em; | |
66 | +} | |
67 | + | |
68 | +.tree ul, | |
69 | +.tree ul li{ | |
70 | + margin: 0; | |
71 | + padding: 0; | |
72 | + list-style: none; | |
73 | +} | |
74 | + | |
75 | +.tree ul li{ | |
76 | + color: #ccc; | |
77 | + clear: both; | |
78 | +} | |
79 | + | |
80 | +.tree ul li div{ | |
81 | + border-bottom: 1px solid #EEE; | |
82 | +} | |
83 | + | |
84 | +.tree strong{ | |
85 | + color: #111; | |
86 | + font-weight: normal; | |
87 | +} | |
88 | + | |
89 | +.tree ul li li{ | |
90 | + padding-left: 0; | |
91 | + margin-left: 15px; | |
92 | + color: #ccc; | |
93 | + list-style: circle; | |
94 | +} | |
95 | + | |
96 | +/* ---------------------------------------------------------------------------- | |
97 | + .code | |
98 | +---------------------------------------------------------------------------- */ | |
99 | + | |
100 | +.code { | |
101 | + width: 100%; | |
102 | + background: #232829; | |
103 | + color: #f8f8f2; | |
104 | + padding: 20px 30px !important; | |
105 | + border-radius: 0; | |
106 | + padding: 20px 30px; | |
107 | + font-family: Monaco,Menlo,Consolas,"Courier New",monospace; | |
108 | +} | |
109 | + | |
110 | +.code.wordwrap { | |
111 | + overflow: auto; | |
112 | + white-space: nowrap; | |
113 | +} | |
114 | + | |
115 | +.code ol { | |
116 | + margin: 0 0 0 45px; | |
117 | +} | |
118 | + | |
119 | +.code ol li { | |
120 | + color: #aaa; | |
121 | + font-size: 12px; | |
122 | + line-height: 21px; | |
123 | + cursor: pointer; | |
124 | + padding-left: 5px; | |
125 | +} | |
126 | + | |
127 | +.code ol li.marked { | |
128 | + color: #f4e009; | |
129 | + background-color: #4f4800; | |
130 | + margin-right: -30px; | |
131 | + padding-right: 30px; | |
132 | +} | |
133 | + | |
134 | +/* ---------------------------------------------------------------------------- | |
135 | + Pygments | |
136 | +---------------------------------------------------------------------------- */ | |
137 | + | |
138 | +.code .gd { color: #FF494F; display: block; } | |
139 | +.code .gi { color: #53C64A; display: block; } | |
140 | + | |
141 | +.code .hll { background-color: #49483e } | |
142 | +.code .c { color: #75715e } /* Comment */ | |
143 | +.code .err { color: #960050; background-color: #1e0010 } /* Error */ | |
144 | +.code .k { color: #66d9ef } /* Keyword */ | |
145 | +.code .l { color: #ae81ff } /* Literal */ | |
146 | +.code .n { color: #f8f8f2 } /* Name */ | |
147 | +.code .o { color: #f92672 } /* Operator */ | |
148 | +.code .p { color: #f8f8f2 } /* Punctuation */ | |
149 | +.code .cm { color: #75715e } /* Comment.Multiline */ | |
150 | +.code .cp { color: #75715e } /* Comment..codeproc */ | |
151 | +.code .c1 { color: #75715e } /* Comment.Single */ | |
152 | +.code .cs { color: #75715e } /* Comment.Special */ | |
153 | +.code .ge { font-style: italic } /* Generic.Emph */ | |
154 | +.code .gs { font-weight: bold } /* Generic.Strong */ | |
155 | +.code .kc { color: #66d9ef } /* Keyword.Constant */ | |
156 | +.code .kd { color: #66d9ef } /* Keyword.Declaration */ | |
157 | +.code .kn { color: #f92672 } /* Keyword.Namespace */ | |
158 | +.code .kp { color: #66d9ef } /* Keyword.Pseudo */ | |
159 | +.code .kr { color: #66d9ef } /* Keyword.Reserved */ | |
160 | +.code .kt { color: #66d9ef } /* Keyword.Type */ | |
161 | +.code .ld { color: #e6db74 } /* Literal.Date */ | |
162 | +.code .m { color: #ae81ff } /* Literal.Number */ | |
163 | +.code .s { color: #e6db74 } /* Literal.String */ | |
164 | +.code .na { color: #a6e22e } /* Name.Attribute */ | |
165 | +.code .nb { color: #f8f8f2 } /* Name.Builtin */ | |
166 | +.code .nc { color: #a6e22e } /* Name.Class */ | |
167 | +.code .no { color: #66d9ef } /* Name.Constant */ | |
168 | +.code .nd { color: #a6e22e } /* Name.Decorator */ | |
169 | +.code .ni { color: #f8f8f2 } /* Name.Entity */ | |
170 | +.code .ne { color: #a6e22e } /* Name.Exception */ | |
171 | +.code .nf { color: #a6e22e } /* Name.Function */ | |
172 | +.code .nl { color: #f8f8f2 } /* Name.Label */ | |
173 | +.code .nn { color: #f8f8f2 } /* Name.Namespace */ | |
174 | +.code .nx { color: #a6e22e } /* Name.Other */ | |
175 | +.code .py { color: #f8f8f2 } /* Name.Property */ | |
176 | +.code .nt { color: #f92672 } /* Name.Tag */ | |
177 | +.code .nv { color: #f8f8f2 } /* Name.Variable */ | |
178 | +.code .ow { color: #f92672 } /* Operator.Word */ | |
179 | +.code .w { color: #f8f8f2 } /* Text.Whitespace */ | |
180 | +.code .mf { color: #ae81ff } /* Literal.Number.Float */ | |
181 | +.code .mh { color: #ae81ff } /* Literal.Number.Hex */ | |
182 | +.code .mi { color: #ae81ff } /* Literal.Number.Integer */ | |
183 | +.code .mo { color: #ae81ff } /* Literal.Number.Oct */ | |
184 | +.code .sb { color: #e6db74 } /* Literal.String.Backtick */ | |
185 | +.code .sc { color: #e6db74 } /* Literal.String.Char */ | |
186 | +.code .sd { color: #e6db74 } /* Literal.String.Doc */ | |
187 | +.code .s2 { color: #e6db74 } /* Literal.String.Double */ | |
188 | +.code .se { color: #ae81ff } /* Literal.String.Escape */ | |
189 | +.code .sh { color: #e6db74 } /* Literal.String.Heredoc */ | |
190 | +.code .si { color: #e6db74 } /* Literal.String.Interpol */ | |
191 | +.code .sx { color: #e6db74 } /* Literal.String.Other */ | |
192 | +.code .sr { color: #e6db74 } /* Literal.String.Regex */ | |
193 | +.code .s1 { color: #e6db74 } /* Literal.String.Single */ | |
194 | +.code .ss { color: #e6db74 } /* Literal.String.Symbol */ | |
195 | +.code .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ | |
196 | +.code .vc { color: #f8f8f2 } /* Name.Variable.Class */ | |
197 | +.code .vg { color: #f8f8f2 } /* Name.Variable.Global */ | |
198 | +.code .vi { color: #f8f8f2 } /* Name.Variable.Instance */ | |
199 | +.code .il { color: #ae81ff } /* Literal.Number.Integer.Long */ | |
200 | + | |
201 | + | |
202 | +/* ---------------------------------------------------------------------------- | |
203 | + Mobile | |
204 | +---------------------------------------------------------------------------- */ | |
205 | + | |
206 | +@media (max-width: 580px) { | |
207 | + .form-options-expire { | |
208 | + float: left; | |
209 | + clear: left; | |
210 | + margin-top: 10px; | |
211 | + } | |
212 | +} | ... | ... |
src/templates/base.html
... | ... | @@ -0,0 +1,16 @@ |
1 | +{% extends 'base.html' %} | |
2 | + | |
3 | +{% block head_css %} | |
4 | +<link rel="stylesheet" href="{{ STATIC_URL }}dpaste/css/theme.css" | |
5 | + type="text/css" media="screen" /> | |
6 | +{% endblock %} | |
7 | + | |
8 | +{% block header %} | |
9 | +<h2>Paste</h2> | |
10 | +{% endblock %} | |
11 | + | |
12 | +{% block main-content %} | |
13 | +{% block page %}{% endblock %} | |
14 | +{% endblock %} | |
15 | + | |
16 | +{% block footer_js %}{% endblock %} | ... | ... |
... | ... | @@ -0,0 +1,207 @@ |
1 | +{% extends "dpaste/base.html" %} | |
2 | + | |
3 | +{% load mptt_tags %} | |
4 | +{% load i18n %} | |
5 | +{% load dpaste_tags %} | |
6 | + | |
7 | +{% block page %} | |
8 | + | |
9 | +{# Snippet options #} | |
10 | + | |
11 | + | |
12 | +<div id="snippet-diff" class="row"> | |
13 | + <div class="col-md-10"> | |
14 | + <div id="diff" style="display:none;"></div> | |
15 | + </div> | |
16 | + <div id="snippet-diff-form" class="col-md-2"> | |
17 | + <form method="get" id="diffform" action="{% url "snippet_diff" %}"> | |
18 | + {% csrf_token %} | |
19 | + <div class="form-group tree"> | |
20 | + {% for tree_item,structure in tree|tree_info %} | |
21 | + {% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %} | |
22 | + <div> | |
23 | + <span class="pull-right"> | |
24 | + <input type="radio" name="a" value="{{ tree_item.id }}" {% ifequal tree_item.id snippet.parent_id %}checked="checked"{% endifequal %}> | |
25 | + <input type="radio" name="b" value="{{ tree_item.id }}" {% ifequal snippet tree_item %}checked="checked"{% endifequal %}> | |
26 | + </span> | |
27 | + {% ifequal snippet tree_item %} | |
28 | + <strong>#{{ tree_item.id }}</strong> | |
29 | + {% else %} | |
30 | + <a href="{{ tree_item.get_absolute_url }}">#{{ tree_item.id }}</a> | |
31 | + {% endifequal %} | |
32 | + </div> | |
33 | + {% for level in structure.closed_levels %}</li></ul>{% endfor %} | |
34 | + {% endfor %} | |
35 | + </div> | |
36 | + <div class="form-group pull-right"> | |
37 | + <button type="submit" class="btn btn-default">{% trans "Compare" %}</button> | |
38 | + </div> | |
39 | + </form> | |
40 | + </div> | |
41 | +</div> | |
42 | + | |
43 | + | |
44 | +<div class="btn-group btn-group-sm snippet-options"> | |
45 | + <a href="#" class="btn btn-default disabled"> | |
46 | + {% if snippet.expire_type == 1 %} | |
47 | + {% blocktrans with date=snippet.expires|timeuntil %}Expires in: {{ date }}{% endblocktrans %} | |
48 | + {% elif snippet.expire_type == 2 %} | |
49 | + {% trans "Snippet never expires" %} | |
50 | + {% elif snippet.expire_type == 3 %} | |
51 | + {% trans "One-time snippet" %} | |
52 | + {% endif %} | |
53 | + </a> | |
54 | + | |
55 | + {% if snippet.pk|in_list:request.session.snippet_list %} | |
56 | + <a href="{% url "snippet_delete" snippet.secret_id %}" class="btn btn-default" onclick="return confirm('{% trans "Really delete this snippet?" %}');"> | |
57 | + <span class="glyphicon glyphicon-trash"></span> | |
58 | + {% trans "Delete Now" %} | |
59 | + </a> | |
60 | + {% endif %} | |
61 | + | |
62 | + {% if not snippet.is_single %} | |
63 | + <a href="#snippet-diff" class="btn btn-default snippet-diff-trigger" title="{% trans "Compare Snippets" %}"> | |
64 | + <span class="glyphicon glyphicon-search"></span> | |
65 | + {% trans "Compare Snippets" %} | |
66 | + </a> | |
67 | + {% endif %} | |
68 | + | |
69 | + <a href="{% url "snippet_details_raw" snippet.secret_id %}" class="btn btn-default" title="{% trans "View Raw" %}"> | |
70 | + <span class="glyphicon glyphicon-align-left"></span> | |
71 | + {% trans "View Raw" %} | |
72 | + </a> | |
73 | + <a href="{% url "snippet_gist" snippet.secret_id %}" class="btn btn-default" rel="nofollow" title="Create a secret Gist"> | |
74 | + <span class="glyphicon glyphicon-share"></span> | |
75 | + </i> {% trans "Gist" %} | |
76 | + </a> | |
77 | + <a href="/pastebin" class="btn btn-primary"> | |
78 | + New snippet | |
79 | + <span class="glyphicon glyphicon-arrow-right"></span> | |
80 | + </a> | |
81 | +</div> | |
82 | + | |
83 | + | |
84 | + {% if snippet.expire_type == 3 %} | |
85 | + <div class="alert alert-warning alert-dismissable"> | |
86 | + <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> | |
87 | + | |
88 | + {% trans "This is a one-time snippet." %} | |
89 | + {% if snippet.remaining_views > 1 %} | |
90 | + {% trans "It will automatically get deleted after {{ remaining }} further views." %} | |
91 | + {% elif snippet.remaining_views == 1 %} | |
92 | + {% trans "It will automatically get deleted after the next view." %} | |
93 | + {% else %} | |
94 | + {% trans "It cannot be viewed again." %} | |
95 | + {% endif %} | |
96 | + </div> | |
97 | + {% endif %} | |
98 | + | |
99 | + {% if snippet.lexer == 'text' %} | |
100 | + <div class="snippet-rendered"> | |
101 | + {{ snippet.content|linebreaksbr }} | |
102 | + </div> | |
103 | + {% else %} | |
104 | + {% include "dpaste/snippet_pre.html" %} | |
105 | + {% endif %} | |
106 | + | |
107 | + {% if not snippet.expire_type == 3 %} | |
108 | + <div class="snippet-reply snippet-reply-hidden"> | |
109 | + <h3>{% trans "Reply to this snippet" %} →</h3> | |
110 | + {% include "dpaste/snippet_form.html" %} | |
111 | + </div> | |
112 | + {% endif %} | |
113 | + | |
114 | + | |
115 | + | |
116 | +{% block footer_js %} | |
117 | +<script> | |
118 | + | |
119 | +$(document).ready(function() { | |
120 | + $('.snippet-reply-hidden').click(function(e) { | |
121 | + $(this).removeClass('snippet-reply-hidden'); | |
122 | + }); | |
123 | + | |
124 | + var diffReq; | |
125 | + | |
126 | + $('.snippet-diff-trigger').click(function(event) { | |
127 | + $('#snippet-diff form').submit(); | |
128 | + $('#snippet-diff').show(); | |
129 | + event.preventDefault(); | |
130 | + }); | |
131 | + | |
132 | + | |
133 | + $('#diffform').submit(function() { | |
134 | + var a = $('input[name="a"]:checked').val(), | |
135 | + b = $('input[name="b"]:checked').val(); | |
136 | + | |
137 | + window.location.hash = 'D' + a + ',' + b; | |
138 | + | |
139 | + // Cancel previous request if it is still pending | |
140 | + if (diffReq) { | |
141 | + diffReq.abort(); | |
142 | + } | |
143 | + | |
144 | + diffReq = $.get("{% url "snippet_diff" %}", { | |
145 | + a: a, | |
146 | + b: b | |
147 | + }).done(function(data) { | |
148 | + $('#diff').html(data).slideDown('fast'); | |
149 | + }).complete(function() { | |
150 | + diffReq = null; | |
151 | + }); | |
152 | + | |
153 | + return false; | |
154 | + }); | |
155 | + | |
156 | + var curLine = document.location.hash, | |
157 | + hashlist; | |
158 | + | |
159 | + if (curLine.substring(0, 2) === '#D') { | |
160 | + hashlist = curLine.substring(2).split(','); | |
161 | + if (hashlist.length === 2) { | |
162 | + console.log(hashlist); | |
163 | + $('#diffform input[name="a"][value="' + hashlist[0] + '"]').prop('checked', true); | |
164 | + $('#diffform input[name="b"][value="' + hashlist[1] + '"]').prop('checked', true); | |
165 | + $('#snippet-diff form').submit(); | |
166 | + $('#snippet-diff').show(); | |
167 | + } | |
168 | + } | |
169 | + | |
170 | + /* ------------------------------------------------------------------------ | |
171 | +Line Highlighting | |
172 | +------------------------------------------------------------------------ */ | |
173 | + if (curLine.substring(0, 2) === '#L') { | |
174 | + hashlist = curLine.substring(2).split(','); | |
175 | + if (hashlist.length > 0 && hashlist[0] !== '') { | |
176 | + $.each(hashlist, function(index, elem){ | |
177 | + $('.code li#' + elem).addClass('marked'); | |
178 | + }); | |
179 | + } | |
180 | + } | |
181 | + | |
182 | + $('.code li').click(function(event) { | |
183 | + var hash = 'L'; | |
184 | + | |
185 | + $(this).toggleClass("marked"); | |
186 | + | |
187 | + $('.code li.marked').each(function (index, elem) { | |
188 | + if (hash !== 'L') hash += ','; | |
189 | + hash += $(elem).attr('id'); | |
190 | + }); | |
191 | + | |
192 | + window.location.hash = hash; | |
193 | + | |
194 | + e.preventDefault(); | |
195 | + }); | |
196 | + | |
197 | + /* ------------------------------------------------------------------------ | |
198 | +Line Highlighting | |
199 | +------------------------------------------------------------------------ */ | |
200 | + $('#toggleWordwrap').click(function(e){ | |
201 | + e.preventDefault(); | |
202 | + $('.code').toggleClass('wordwrap'); | |
203 | + }); | |
204 | +}); | |
205 | +</script> | |
206 | +{% endblock %} | |
207 | +{% endblock %} | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +{% load i18n %} | |
2 | +{% load url from future %} | |
3 | + | |
4 | +<h4> | |
5 | + {% blocktrans with fileA.get_absolute_url as filea_url and fileB.get_absolute_url as fileb_url and fileA.id as filea_id and fileB.id as fileb_id %} | |
6 | + Diff between <a href="{{ filea_url }}">#{{ filea_id }}</a> and <a href="{{ fileb_url }}">#{{ fileb_id }}</a> | |
7 | + {% endblocktrans %} | |
8 | +</h4> | |
9 | + | |
10 | +{% if snippet.lexer == 'diff' %} | |
11 | + {% include "dpaste/snippet_pre.html" %} | |
12 | +{% else %} | |
13 | + <p>{{ snippet.content }}</p> | |
14 | +{% endif %} | ... | ... |
... | ... | @@ -0,0 +1,33 @@ |
1 | +{% load i18n %} | |
2 | + | |
3 | +<form action="" method="post" class="form-horizontal"> | |
4 | + {% csrf_token %} | |
5 | + {{ snippet_form.non_field_errors }} | |
6 | + | |
7 | + <div class="form-group superenter {% if is_new %}autofocus{% endif %} {% if snippet_form.content.errors %}has-error{% endif %}"> | |
8 | + {{ snippet_form.content }} | |
9 | + </div> | |
10 | + | |
11 | + <div class="form-group"> | |
12 | + | |
13 | + <div class="pull-left {% if snippet_form.lexer.errors %}has-error{% endif %}"> | |
14 | + {{ snippet_form.lexer }} | |
15 | + </div> | |
16 | + | |
17 | + <div class="pull-right has-feedback"> | |
18 | + <div class="input-group"> | |
19 | + <span class="glyphicon glyphicon-trash input-group-addon"></span> | |
20 | + {{ snippet_form.expires }} | |
21 | + </div> | |
22 | + | |
23 | + </div> | |
24 | + </div> | |
25 | + | |
26 | + <div class="form-group"> | |
27 | + <div class="well"> | |
28 | + <input tabindex="0" type="submit" class="btn btn-primary" value="{% trans "Paste it" %}"> | |
29 | + <span class="shortcut">Ctrl+⏎ ou ⌘+⏎</span> | |
30 | + </div> | |
31 | + </div> | |
32 | + | |
33 | +</form> | ... | ... |