|
0
|
1 local Luan = require "luan:Luan.luan"
|
|
|
2 local error = Luan.error
|
|
|
3 local ipairs = Luan.ipairs or error()
|
|
|
4 local pairs = Luan.pairs or error()
|
|
|
5 local range = Luan.range or error()
|
|
|
6 local Table = require "luan:Table.luan"
|
|
|
7 local is_list = Table.is_list or error()
|
|
|
8 local is_empty = Table.is_empty or error()
|
|
|
9 local concat = Table.concat or error()
|
|
|
10 local Io = require "luan:Io.luan"
|
|
|
11 local uri = Io.uri or error()
|
|
|
12 local Html = require "luan:Html.luan"
|
|
|
13 local html_encode = Html.encode or error()
|
|
|
14 local url_encode = Html.url_encode or error()
|
|
|
15 local Parsers = require "luan:Parsers.luan"
|
|
|
16 local json_parse = Parsers.json_parse or error()
|
|
|
17 local json_string = Parsers.json_string or error()
|
|
|
18 local Thread = require "luan:Thread.luan"
|
|
|
19 local thread_run = Thread.run or error()
|
|
|
20 local sleep = Thread.sleep or error()
|
|
|
21 local Time = require "luan:Time.luan"
|
|
|
22 local Http = require "luan:http/Http.luan"
|
|
|
23 local Mail = require "luan:mail/Mail.luan"
|
|
|
24 local Config = require "site:/private/Config.luan"
|
|
|
25 local uploadcare_public_key = Config.uploadcare.public_key or error()
|
|
|
26 local Ab_test = require "site:/lib/Ab_test.luan"
|
|
|
27 local ab_test_head = Ab_test.head or error()
|
|
|
28 local User = require "site:/lib/User.luan"
|
|
|
29 local current_user = User.current or error()
|
|
|
30 local Icon = require "site:/lib/Icon.luan"
|
|
|
31 local get_user_icons = Icon.get_user_icons or error()
|
|
|
32 local Utils = require "site:/lib/Utils.luan"
|
|
|
33 local is_production = not not Utils.is_production
|
|
|
34 local base_url = Utils.base_url or error()
|
|
|
35 local to_list = Utils.to_list or error()
|
|
|
36 local Logging = require "luan:logging/Logging.luan"
|
|
|
37 local logger = Logging.logger "Shared"
|
|
|
38
|
|
|
39
|
|
|
40 local Shared = {}
|
|
|
41
|
|
|
42 local started = Time.now()
|
|
|
43
|
|
|
44 Shared.is_production = is_production
|
|
|
45 local is_local = Http.domain == nil
|
|
|
46 local has_reporting = true
|
|
|
47
|
|
|
48 Shared.compressed = Utils.compressed
|
|
|
49
|
|
|
50 local mp_id = is_production and "404d4c479de9c3070252e692375e82ca" or "bd2099a22e4118350a46b5b360d8c4da"
|
|
|
51 Shared.mp_id = mp_id
|
|
|
52 local mp_url = "https://api.mixpanel.com/track"
|
|
|
53
|
|
|
54 local function call_mixpanel_raw(url,data)
|
|
|
55 local options = {
|
|
|
56 method = "POST"
|
|
|
57 parameters = {
|
|
|
58 data = json_string(data)
|
|
|
59 verbose = "1"
|
|
|
60 }
|
|
|
61 }
|
|
|
62 -- logger.info(options.parameters.data)
|
|
|
63 local result = uri(url,options).read_text()
|
|
|
64 result = json_parse(result)
|
|
|
65 result.error and logger.error("mixpanel: "..result.error)
|
|
|
66 end
|
|
|
67 Shared.call_mixpanel_raw = call_mixpanel_raw
|
|
|
68
|
|
|
69 function Shared.call_mixpanel(data)
|
|
|
70 if is_list(data) then
|
|
|
71 for _, event in ipairs(data) do
|
|
|
72 event.properties.token = mp_id
|
|
|
73 end
|
|
|
74 else
|
|
|
75 data.properties.token = mp_id
|
|
|
76 end
|
|
|
77 call_mixpanel_raw(mp_url,data)
|
|
|
78 end
|
|
|
79
|
|
|
80
|
|
|
81 local function remove_from_path(name)
|
|
|
82 local path = Http.request.path
|
|
|
83 if path == "/index.html" then
|
|
|
84 path = "/"
|
|
|
85 end
|
|
|
86 local params = Http.request.parameters
|
|
|
87 params[name] = nil
|
|
|
88 if not is_empty(params) then
|
|
|
89 local t = {}
|
|
|
90 for name, value in pairs(params) do
|
|
|
91 t[#t+1] = url_encode(name).."="..url_encode(value)
|
|
|
92 end
|
|
|
93 path = path.."?"..concat(t,"&")
|
|
|
94 end
|
|
|
95 return path
|
|
|
96 end
|
|
|
97
|
|
|
98 local function min_head(user)
|
|
|
99 %>
|
|
|
100 <meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
101 <link rel="icon" href="/images/favicon.png" type="image/png">
|
|
|
102 <style>
|
|
|
103 @import "https://fonts.googleapis.com/css?family=Bitter";
|
|
|
104 @import "/site.css?s=<%=started%>";
|
|
|
105 </style>
|
|
|
106 <script src="/site.js?s=<%=started%>"></script>
|
|
|
107 <%
|
|
|
108 local theme_date = user and user.theme_date
|
|
|
109 if theme_date ~= nil then
|
|
|
110 %>
|
|
|
111 <style> @import "/theme.css?user=<%=user.id%>&date=<%=theme_date%>&s=<%=started%>"; </style>
|
|
|
112 <%
|
|
|
113 end
|
|
|
114 end
|
|
|
115
|
|
|
116 function Shared.head()
|
|
|
117 local user = current_user()
|
|
|
118 min_head(user)
|
|
|
119 local source = Http.request.parameters.source
|
|
|
120 if source ~= nil then
|
|
|
121 local path = remove_from_path('source')
|
|
|
122 %>
|
|
|
123 <script>
|
|
|
124 history.replaceState(null,null,'<%=path%>');
|
|
|
125 </script>
|
|
|
126 <%
|
|
|
127 end
|
|
|
128 if user == nil and Http.request.cookies.source == nil then
|
|
|
129 source = source or ""
|
|
|
130 %>
|
|
|
131 <script>
|
|
|
132 mixpanel.ours.identify();
|
|
|
133 mixpanel.ours.people.set({'source':'<%=source%>'});
|
|
|
134 </script>
|
|
|
135 <%
|
|
|
136 Http.response.set_persistent_cookie( "source", source )
|
|
|
137 end
|
|
|
138 %>
|
|
|
139 <style>
|
|
|
140 @import "/uploadcare/croppr.css";
|
|
|
141 @import "/uploadcare/uploadcare.css?s=<%=started%>";
|
|
|
142 @import "/dad.css";
|
|
|
143 @import "/admin.css?s=<%=started%>";
|
|
|
144 </style>
|
|
|
145 <script src="/uploadcare/croppr.js"></script>
|
|
|
146 <script src="/uploadcare/uploadcare.js?s=<%=started%>"></script>
|
|
|
147 <script> window.uploadcarePubKey = '<%=uploadcare_public_key%>'; </script>
|
|
|
148 <script src="/dad.js?v1"></script>
|
|
|
149 <% if user ~= nil then %>
|
|
|
150 <script>
|
|
|
151 window.UserEmail = '<%= user.email or error() %>';
|
|
|
152 window.UserName = '<%= user.name or error() %>';
|
|
|
153 </script>
|
|
|
154 <% end %>
|
|
|
155 <script src="/admin.js?s=<%=started%>"></script>
|
|
|
156 <%
|
|
|
157 ab_test_head()
|
|
|
158 end
|
|
|
159
|
|
|
160 function Shared.pub_head(user)
|
|
|
161 %>
|
|
|
162 <script>
|
|
|
163 window.Owner = '<%= user.name %>';
|
|
|
164 <% if user.mp_id ~= nil then %>
|
|
|
165 window.OwnerMpId = <%= json_string(user.mp_id) %>;
|
|
|
166 <% end %>
|
|
|
167 </script>
|
|
|
168 <%
|
|
|
169 min_head(user)
|
|
|
170 if has_reporting then
|
|
|
171 %>
|
|
|
172 <script async src="/reporting.js?s=<%=started%>"></script>
|
|
|
173 <%
|
|
|
174 end
|
|
|
175 end
|
|
|
176
|
|
|
177 function Shared.page_header()
|
|
|
178 %>
|
|
|
179 <a header href="/">
|
|
|
180 <img logo=big src="/images/logo.png">
|
|
|
181 <img logo=small src="/images/small_logo.png">
|
|
|
182 </a>
|
|
|
183 <%
|
|
|
184 end
|
|
|
185
|
|
|
186 function Shared.body_header()
|
|
|
187 local user = current_user()
|
|
|
188 %>
|
|
|
189 <div header>
|
|
|
190 <a left href="/">
|
|
|
191 <img logo=big src="/images/logo.png">
|
|
|
192 <img logo=small src="/images/small_logo.png">
|
|
|
193 </a>
|
|
|
194 <% if user == nil then %>
|
|
|
195 <span right login>
|
|
|
196 <a button login href="/login.html">Log in</a>
|
|
|
197 <a button register href="/register.html">Sign up</a>
|
|
|
198 </span>
|
|
|
199 <% else %>
|
|
|
200 <span right pulldown>
|
|
|
201 <img src="<%= user.get_pic_url() or "/images/user.png" %>" onclick="clickMenu(this)">
|
|
|
202 <div pulldown_menu>
|
|
|
203 <a href="javascript:copyLink()">
|
|
|
204 <span>linkmy.style/<%=user.name%></span>
|
|
|
205 <span copy>Copy</span>
|
|
|
206 </a>
|
|
|
207 <a href="/<%=user.name%>">My page</a>
|
|
|
208 <a href="/account.html">My account</a>
|
|
|
209 <a href="/links.html">Links</a>
|
|
|
210 <a href="/pics.html">Photos</a>
|
|
|
211 <a href="/theme.html">My theme</a>
|
|
|
212 <a href="/qr_code.html">QR code</a>
|
|
|
213 <a href="/analytics.html">Analytics</a>
|
|
|
214 <a href="javascript:logout()">Log out</a>
|
|
|
215 </div>
|
|
|
216 </span>
|
|
|
217 <input clipboard value="<%=base_url()%>/<%=user.name%>">
|
|
|
218 <% end %>
|
|
|
219 </div>
|
|
|
220 <%
|
|
|
221 end
|
|
|
222
|
|
|
223 function Shared.footer()
|
|
|
224 %>
|
|
|
225 <div footer>
|
|
|
226 <span>
|
|
|
227 <a href="/help.html">Help</a>
|
|
|
228 <br>support@linkmy.style
|
|
|
229 </span>
|
|
|
230 <span>
|
|
|
231 <a href="https://apps.apple.com/us/app/linkmystyle/id6475133762"><img ios src="/images/ios.svg"></a>
|
|
|
232 <a href="https://www.instagram.com/linkmy.style/"><img src="/images/icons/instagram.svg"></a>
|
|
|
233 </span>
|
|
|
234 </div>
|
|
|
235 <%
|
|
|
236 end
|
|
|
237
|
|
|
238 function Shared.show_editable_link(link)
|
|
|
239 %>
|
|
|
240 <div link="<%=link.id%>">
|
|
|
241 <div>
|
|
|
242 <a link pub_content href="<%=html_encode(link.url)%>" draggable=false><%=html_encode(link.title)%></a>
|
|
|
243 <img src="/images/drag_indicator.svg">
|
|
|
244 </div>
|
|
|
245 <button type=button small onclick="editLink('<%=link.id%>')">Edit</button>
|
|
|
246 <button type=button small onclick="deleteLink1(this,'<%=link.id%>')">Delete</button>
|
|
|
247 </div>
|
|
|
248 <%
|
|
|
249 end
|
|
|
250
|
|
|
251 function Shared.show_saved(close_url)
|
|
|
252 %>
|
|
|
253 <div saved>
|
|
|
254 <p>Your changes have been saved.</p>
|
|
|
255 <p><a href="/theme.html">Edit Theme</a></p>
|
|
|
256 <a close href="<%=close_url%>"><img src="/images/close.svg"></a>
|
|
|
257 </div>
|
|
|
258 <%
|
|
|
259 end
|
|
|
260
|
|
|
261 function Shared.show_user_icons(user)
|
|
|
262 local icons = get_user_icons(user.id)
|
|
|
263 %>
|
|
|
264 <div list>
|
|
|
265 <%
|
|
|
266 for _, icon in ipairs(icons) do
|
|
|
267 %>
|
|
|
268 <span icon="<%=icon.id%>">
|
|
|
269 <img src="/images/drag_indicator.svg">
|
|
|
270 <a <%=icon.title_attr()%> href="<%=html_encode(icon.url)%>" draggable=false>
|
|
|
271 <img src="/images/icons/<%=icon.name%>.svg" draggable=false>
|
|
|
272 </a>
|
|
|
273 </span>
|
|
|
274 <%
|
|
|
275 end
|
|
|
276 %>
|
|
|
277 </div>
|
|
|
278 <button big onclick="ajax('/edit_icons.js')"><%= #icons==0 and "Add" or "Edit" %></button>
|
|
|
279 <%
|
|
|
280 end
|
|
|
281
|
|
|
282 function Shared.js_error(field,message)
|
|
|
283 %>
|
|
|
284 showError( context.form, '<%=field%>', <%=json_string(message)%> );
|
|
|
285 <%
|
|
|
286 end
|
|
|
287
|
|
|
288 local send_mail = Mail.sender(Config.mail_server).send
|
|
|
289
|
|
|
290 function Shared.send_mail_async(mail)
|
|
|
291 thread_run( function()
|
|
|
292 for i in range(1,5) do
|
|
|
293 try
|
|
|
294 send_mail(mail)
|
|
|
295 return
|
|
|
296 catch e
|
|
|
297 logger.error("send_mail_async fail "..i..":\n"..e)
|
|
|
298 sleep(10000)
|
|
|
299 end
|
|
|
300 end
|
|
|
301 error "send_mail_async failed"
|
|
|
302 end )
|
|
|
303 end
|
|
|
304
|
|
|
305 Shared.theme_fields = {
|
|
|
306 background_color = "#c0d1d6"
|
|
|
307 link_background_color = "#325762"
|
|
|
308 link_hover_background_color = "#243f47"
|
|
|
309 link_text_color = "#ffffff"
|
|
|
310 title_color = "#000000"
|
|
|
311 bio_color = "#000000"
|
|
|
312 icon_color = ""
|
|
|
313 link_border_radius = "12px / 50%"
|
|
|
314 link_border_color = ""
|
|
|
315 link_shadow = ""
|
|
|
316 link_shadow_color = ""
|
|
|
317 font = "Bitter"
|
|
|
318 font_url = ""
|
|
|
319 background_img_uuid = ""
|
|
|
320 background_img_filename = ""
|
|
|
321 }
|
|
|
322
|
|
|
323 function Shared.password_input(value)
|
|
|
324 value = value or ""
|
|
|
325 %>
|
|
|
326 <div password>
|
|
|
327 <input type=password required name=password value="<%=html_encode(value)%>" placeholder="Password">
|
|
|
328 <img show src="/images/visibility.svg" onclick="showPassword(parentNode)">
|
|
|
329 <img hide src="/images/visibility_off.svg" onclick="hidePassword(parentNode)">
|
|
|
330 </div>
|
|
|
331 <%
|
|
|
332 end
|
|
|
333
|
|
|
334 function Shared.get_hashtags_list(pics)
|
|
|
335 local hashtags_set = {}
|
|
|
336 for _, pic in ipairs(pics) do
|
|
|
337 local hashtags = pic.hashtags
|
|
|
338 if hashtags ~= nil then
|
|
|
339 hashtags = to_list(hashtags)
|
|
|
340 for _, hashtag in ipairs(hashtags) do
|
|
|
341 hashtags_set[hashtag] = true
|
|
|
342 end
|
|
|
343 local classes = concat( hashtags, " " )
|
|
|
344 pic.class = [[class="]]..classes..[["]]
|
|
|
345 end
|
|
|
346 end
|
|
|
347 local list = {}
|
|
|
348 for hashtag, v in pairs(hashtags_set) do
|
|
|
349 if v == true then
|
|
|
350 list[#list+1] = hashtag
|
|
|
351 end
|
|
|
352 end
|
|
|
353 return list, hashtags_set
|
|
|
354 end
|
|
|
355
|
|
|
356 return Shared
|