1--Minetest
2--Copyright (C) 2013 sapier
3--
4--This program is free software; you can redistribute it and/or modify
5--it under the terms of the GNU Lesser General Public License as published by
6--the Free Software Foundation; either version 2.1 of the License, or
7--(at your option) any later version.
8--
9--This program is distributed in the hope that it will be useful,
10--but WITHOUT ANY WARRANTY; without even the implied warranty of
11--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12--GNU Lesser General Public License for more details.
13--
14--You should have received a copy of the GNU Lesser General Public License along
15--with this program; if not, write to the Free Software Foundation, Inc.,
16--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18--------------------------------------------------------------------------------
19
20--modstore implementation
21modstore = {}
22
23--------------------------------------------------------------------------------
24-- @function [parent=#modstore] init
25function modstore.init()
26	modstore.tabnames = {}
27
28	table.insert(modstore.tabnames,"dialog_modstore_unsorted")
29	table.insert(modstore.tabnames,"dialog_modstore_search")
30
31	modstore.modsperpage = 5
32
33	modstore.basetexturedir = core.get_texturepath() .. DIR_DELIM .. "base" ..
34						DIR_DELIM .. "pack" .. DIR_DELIM
35
36	modstore.lastmodtitle = ""
37	modstore.last_search = ""
38
39	modstore.searchlist = filterlist.create(
40		function()
41			if modstore.modlist_unsorted ~= nil and
42				modstore.modlist_unsorted.data ~= nil then
43				return modstore.modlist_unsorted.data
44			end
45			return {}
46		end,
47		function(element,modid)
48			if element.id == modid then
49				return true
50			end
51			return false
52		end, --compare fct
53		nil, --uid match fct
54		function(element,substring)
55			if substring == nil or
56				substring == "" then
57				return false
58			end
59			substring = substring:upper()
60
61			if element.title ~= nil and
62				element.title:upper():find(substring) ~= nil then
63				return true
64			end
65
66			if element.details ~= nil and
67				element.details.author ~= nil and
68				element.details.author:upper():find(substring) ~= nil then
69				return true
70			end
71
72			if element.details ~= nil and
73				element.details.description ~= nil and
74				element.details.description:upper():find(substring) ~= nil then
75				return true
76			end
77			return false
78		end --filter fct
79		)
80
81	modstore.current_list = nil
82end
83
84--------------------------------------------------------------------------------
85-- @function [parent=#modstore] nametoindex
86function modstore.nametoindex(name)
87
88	for i=1,#modstore.tabnames,1 do
89		if modstore.tabnames[i] == name then
90			return i
91		end
92	end
93
94	return 1
95end
96
97--------------------------------------------------------------------------------
98-- @function [parent=#modstore] getsuccessfuldialog
99function modstore.getsuccessfuldialog()
100	local retval = ""
101	retval = retval .. "size[6,2,true]"
102	if modstore.lastmodentry ~= nil then
103		retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
104		retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
105
106
107		retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
108		retval = retval .. "label[3,0.75;" .. core.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
109
110	end
111	retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]"
112
113
114	return retval
115end
116
117--------------------------------------------------------------------------------
118-- @function [parent=#modstore] gettab
119function modstore.gettab(tabname)
120	local retval = ""
121
122	local is_modstore_tab = false
123
124	if tabname == "dialog_modstore_unsorted" then
125		modstore.modsperpage = 5
126		retval = modstore.getmodlist(modstore.modlist_unsorted)
127		is_modstore_tab = true
128	end
129
130	if tabname == "dialog_modstore_search" then
131		retval = modstore.getsearchpage()
132		is_modstore_tab = true
133	end
134
135	if is_modstore_tab then
136		return modstore.tabheader(tabname) .. retval
137	end
138
139	if tabname == "modstore_mod_installed" then
140		return modstore.getsuccessfuldialog()
141	end
142
143	if tabname == "modstore_downloading" then
144		return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") ..
145				" " .. modstore.lastmodtitle .. " " ..
146				fgettext("please wait...") .. "]"
147	end
148
149	return ""
150end
151
152--------------------------------------------------------------------------------
153-- @function [parent=#modstore] tabheader
154function modstore.tabheader(tabname)
155	local retval  = "size[12,10.25,true]"
156	retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
157				"Unsorted,Search;" ..
158				modstore.nametoindex(tabname) .. ";true;false]" ..
159				"button[4,9.9;4,0.5;btn_modstore_close;" ..
160				fgettext("Close modstore") .. "]"
161
162	return retval
163end
164
165--------------------------------------------------------------------------------
166-- @function [parent=#modstore] handle_buttons
167function modstore.handle_buttons(current_tab,fields)
168
169	if fields["modstore_tab"] then
170		local index = tonumber(fields["modstore_tab"])
171
172		if index > 0 and
173			index <= #modstore.tabnames then
174			if modstore.tabnames[index] == "dialog_modstore_search" then
175				filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search)
176				filterlist.refresh(modstore.searchlist)
177				modstore.modsperpage = 4
178				modstore.currentlist = {
179					page = 0,
180					pagecount =
181						math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
182					data = filterlist.get_list(modstore.searchlist),
183				}
184			end
185
186			return {
187					current_tab = modstore.tabnames[index],
188					is_dialog = true,
189					show_buttons = false
190			}
191		end
192
193	end
194
195	if fields["btn_modstore_page_up"] then
196		if modstore.current_list ~= nil and modstore.current_list.page > 0 then
197			modstore.current_list.page = modstore.current_list.page - 1
198		end
199	end
200
201	if fields["btn_modstore_page_down"] then
202		if modstore.current_list ~= nil and
203			modstore.current_list.page <modstore.current_list.pagecount-1 then
204			modstore.current_list.page = modstore.current_list.page +1
205		end
206	end
207
208	if fields["btn_hidden_close_download"] ~= nil then
209		if fields["btn_hidden_close_download"].successfull then
210			modstore.lastmodentry = fields["btn_hidden_close_download"]
211			return {
212					current_tab = "modstore_mod_installed",
213					is_dialog = true,
214					show_buttons = false
215			}
216		else
217			modstore.lastmodtitle = ""
218			return {
219						current_tab = modstore.tabnames[1],
220						is_dialog = true,
221						show_buttons = false
222				}
223		end
224	end
225
226	if fields["btn_confirm_mod_successfull"] then
227		modstore.lastmodentry = nil
228		modstore.lastmodtitle = ""
229		return {
230					current_tab = modstore.tabnames[1],
231					is_dialog = true,
232					show_buttons = false
233			}
234	end
235
236	if fields["btn_modstore_search"] or
237		(fields["key_enter"] and fields["te_modstore_search"] ~= nil) then
238		modstore.last_search = fields["te_modstore_search"]
239		filterlist.set_filtercriteria(modstore.searchlist,fields["te_modstore_search"])
240		filterlist.refresh(modstore.searchlist)
241		modstore.currentlist = {
242			page = 0,
243			pagecount =  math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
244			data = filterlist.get_list(modstore.searchlist),
245		}
246	end
247
248
249	if fields["btn_modstore_close"] then
250		return {
251			is_dialog = false,
252			show_buttons = true,
253			current_tab = core.setting_get("main_menu_tab")
254		}
255	end
256
257	for key,value in pairs(fields) do
258		local foundat = key:find("btn_install_mod_")
259		if ( foundat == 1) then
260			local modid = tonumber(key:sub(17))
261			for i=1,#modstore.modlist_unsorted.data,1 do
262				if modstore.modlist_unsorted.data[i].id == modid then
263					local moddetails = modstore.modlist_unsorted.data[i].details
264
265					if modstore.lastmodtitle ~= "" then
266						modstore.lastmodtitle = modstore.lastmodtitle .. ", "
267					end
268
269					modstore.lastmodtitle = modstore.lastmodtitle .. moddetails.title
270
271					core.handle_async(
272						function(param)
273
274							local fullurl = core.setting_get("modstore_download_url") ..
275											param.moddetails.download_url
276
277							if param.version ~= nil then
278								local found = false
279								for i=1,#param.moddetails.versions, 1 do
280									if param.moddetails.versions[i].date:sub(1,10) == param.version then
281										fullurl = core.setting_get("modstore_download_url") ..
282														param.moddetails.versions[i].download_url
283										found = true
284									end
285								end
286
287								if not found then
288									return {
289										moddetails = param.moddetails,
290										successfull = false
291									}
292								end
293							end
294
295							if core.download_file(fullurl,param.filename) then
296								return {
297									texturename = param.texturename,
298									moddetails = param.moddetails,
299									filename = param.filename,
300									successfull = true
301								}
302							else
303								return {
304									moddetails = param.moddetails,
305									successfull = false
306								}
307							end
308						end,
309						{
310							moddetails = moddetails,
311							version = fields["dd_version" .. modid],
312							filename = os.tempfolder() .. "_MODNAME_" .. moddetails.basename .. ".zip",
313							texturename = modstore.modlist_unsorted.data[i].texturename
314						},
315						function(result)
316							if result.successfull then
317								modmgr.installmod(result.filename,result.moddetails.basename)
318								os.remove(result.filename)
319							else
320								gamedata.errormessage = "Failed to download " .. result.moddetails.title
321							end
322
323							if gamedata.errormessage == nil then
324								core.button_handler({btn_hidden_close_download=result})
325							else
326								core.button_handler({btn_hidden_close_download={successfull=false}})
327							end
328						end
329					)
330
331					return {
332						current_tab = "modstore_downloading",
333						is_dialog = true,
334						show_buttons = false,
335						ignore_menu_quit = true
336					}
337				end
338			end
339			break
340		end
341	end
342end
343
344--------------------------------------------------------------------------------
345-- @function [parent=#modstore] update_modlist
346function modstore.update_modlist()
347	modstore.modlist_unsorted = {}
348	modstore.modlist_unsorted.data = {}
349	modstore.modlist_unsorted.pagecount = 1
350	modstore.modlist_unsorted.page = 0
351
352	core.handle_async(
353		function(param)
354			return core.get_modstore_list()
355		end,
356		nil,
357		function(result)
358			if result ~= nil then
359				modstore.modlist_unsorted = {}
360				modstore.modlist_unsorted.data = result
361
362				if modstore.modlist_unsorted.data ~= nil then
363					modstore.modlist_unsorted.pagecount =
364						math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
365				else
366					modstore.modlist_unsorted.data = {}
367					modstore.modlist_unsorted.pagecount = 1
368				end
369				modstore.modlist_unsorted.page = 0
370				modstore.fetchdetails()
371				core.event_handler("Refresh")
372			end
373		end
374	)
375end
376
377--------------------------------------------------------------------------------
378-- @function [parent=#modstore] fetchdetails
379function modstore.fetchdetails()
380
381	for i=1,#modstore.modlist_unsorted.data,1 do
382		core.handle_async(
383		function(param)
384			param.details = core.get_modstore_details(tostring(param.modid))
385			return param
386		end,
387		{
388			modid=modstore.modlist_unsorted.data[i].id,
389			listindex=i
390		},
391		function(result)
392			if result ~= nil and
393				modstore.modlist_unsorted ~= nil
394				and modstore.modlist_unsorted.data ~= nil and
395				modstore.modlist_unsorted.data[result.listindex] ~= nil and
396				modstore.modlist_unsorted.data[result.listindex].id ~= nil then
397
398				modstore.modlist_unsorted.data[result.listindex].details = result.details
399				core.event_handler("Refresh")
400			end
401		end
402		)
403	end
404end
405
406--------------------------------------------------------------------------------
407-- @function [parent=#modstore] getscreenshot
408function modstore.getscreenshot(ypos,listentry)
409
410	if	listentry.details ~= nil and
411		(listentry.details.screenshot_url == nil or
412		listentry.details.screenshot_url == "") then
413
414		if listentry.texturename == nil then
415			listentry.texturename = modstore.basetexturedir .. "no_screenshot.png"
416		end
417
418		return "image[0,".. ypos .. ";3,2;" ..
419			core.formspec_escape(listentry.texturename) .. "]"
420	end
421
422	if listentry.details ~= nil and
423		listentry.texturename == nil then
424		--make sure we don't download multiple times
425		listentry.texturename = "in progress"
426
427		--prepare url and filename
428		local fullurl = core.setting_get("modstore_download_url") ..
429					listentry.details.screenshot_url
430		local filename = os.tempfolder() .. "_MID_" .. listentry.id
431
432		--trigger download
433		core.handle_async(
434			--first param is downloadfct
435			function(param)
436				param.successfull = core.download_file(param.fullurl,param.filename)
437				return param
438			end,
439			--second parameter is data passed to async job
440			{
441				fullurl = fullurl,
442				filename = filename,
443				modid = listentry.id
444			},
445			--integrate result to raw list
446			function(result)
447				if result.successfull then
448					local found = false
449					for i=1,#modstore.modlist_unsorted.data,1 do
450						if modstore.modlist_unsorted.data[i].id == result.modid then
451							found = true
452							modstore.modlist_unsorted.data[i].texturename = result.filename
453							break
454						end
455					end
456					if found then
457						core.event_handler("Refresh")
458					else
459						core.log("error","got screenshot but didn't find matching mod: " .. result.modid)
460					end
461				end
462			end
463		)
464	end
465
466	if listentry.texturename ~= nil and
467		listentry.texturename ~= "in progress" then
468		return "image[0,".. ypos .. ";3,2;" ..
469			core.formspec_escape(listentry.texturename) .. "]"
470	end
471
472	return ""
473end
474
475--------------------------------------------------------------------------------
476--@function [parent=#modstore] getshortmodinfo
477function modstore.getshortmodinfo(ypos,listentry,details)
478	local retval = ""
479
480	retval = retval .. "box[0," .. ypos .. ";11.4,1.75;#FFFFFF]"
481
482	--screenshot
483	retval = retval .. modstore.getscreenshot(ypos,listentry)
484
485	--title + author
486	retval = retval .."label[2.75," .. ypos .. ";" ..
487		core.formspec_escape(details.title) .. " (" .. details.author .. ")]"
488
489	--description
490	local descriptiony = ypos + 0.5
491	retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
492		core.formspec_escape(details.description) .. ";]"
493
494	--rating
495	local ratingy = ypos
496	retval = retval .."label[7," .. ratingy .. ";" ..
497					fgettext("Rating") .. ":]"
498	retval = retval .. "label[8.7," .. ratingy .. ";" .. details.rating .."]"
499
500	--versions (IMPORTANT has to be defined AFTER rating)
501	if details.versions ~= nil and
502		#details.versions > 1 then
503		local versiony = ypos + 0.05
504		retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";"
505		local versions = ""
506		for i=1,#details.versions , 1 do
507			if versions ~= "" then
508				versions = versions .. ","
509			end
510
511			versions = versions .. details.versions[i].date:sub(1,10)
512		end
513		retval = retval .. versions .. ";1]"
514	end
515
516	if details.basename then
517		--install button
518		local buttony = ypos + 1.2
519		retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";"
520
521		if modmgr.mod_exists(details.basename) then
522			retval = retval .. fgettext("re-Install") .."]"
523		else
524			retval = retval .. fgettext("Install") .."]"
525		end
526	end
527
528	return retval
529end
530
531--------------------------------------------------------------------------------
532--@function [parent=#modstore] getmodlist
533function modstore.getmodlist(list,yoffset)
534
535	modstore.current_list = list
536
537	if #list.data == 0 then
538		return ""
539	end
540
541	if yoffset == nil then
542		yoffset = 0
543	end
544
545	local scrollbar = ""
546	scrollbar = scrollbar .. "label[0.1,9.5;"
547				.. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
548	scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28,"
549				.. (8.6 - yoffset) .. ";#000000]"
550	local scrollbarpos = (yoffset + 0.75) +
551				((7.7 -yoffset)/(list.pagecount-1)) * list.page
552	scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
553	scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3))
554				.. ";0.5,0.5;btn_modstore_page_up;^]"
555	scrollbar = scrollbar .. "button[11.6," .. 9.0
556				.. ";0.5,0.5;btn_modstore_page_down;v]"
557
558	local retval = ""
559
560	local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
561
562	if (endmod > #list.data) then
563		endmod = #list.data
564	end
565
566	for i=(list.page * modstore.modsperpage) +1, endmod, 1 do
567		--getmoddetails
568		local details = list.data[i].details
569
570		if details == nil then
571			details = {}
572			details.title = list.data[i].title
573			details.author = ""
574			details.rating = -1
575			details.description = ""
576		end
577
578		if details ~= nil then
579			local screenshot_ypos =
580				yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
581
582			retval = retval .. modstore.getshortmodinfo(screenshot_ypos,
583														list.data[i],
584														details)
585		end
586	end
587
588	return retval .. scrollbar
589end
590
591--------------------------------------------------------------------------------
592--@function [parent=#modstore] getsearchpage
593function modstore.getsearchpage()
594	local retval = ""
595	local search = ""
596
597	if modstore.last_search ~= nil then
598		search = modstore.last_search
599	end
600
601	retval = retval ..
602		"button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" ..
603		"field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]"
604
605
606	--show 4 mods only
607	modstore.modsperpage = 4
608	retval = retval ..
609		modstore.getmodlist(
610			modstore.currentlist,
611			1.75)
612
613	return retval;
614end
615
616