1 | #! /usr/bin/python3 |
---|
2 | # -*- coding: utf-8 -*- |
---|
3 | |
---|
4 | import os |
---|
5 | import gi |
---|
6 | gi.require_version('Gtk', '3.0') |
---|
7 | from gi.repository import Gtk,Gdk,GdkPixbuf,GObject,GLib |
---|
8 | gi.require_version('PangoCairo', '1.0') |
---|
9 | import json |
---|
10 | from edupals.ui.n4dgtklogin import * |
---|
11 | #import repomanager.RepoManager as RepoManager |
---|
12 | import xmlrpc.client as n4d |
---|
13 | import ssl |
---|
14 | import threading |
---|
15 | import time |
---|
16 | import subprocess |
---|
17 | from collections import OrderedDict |
---|
18 | |
---|
19 | import gettext |
---|
20 | gettext.textdomain('repoman') |
---|
21 | _ = gettext.gettext |
---|
22 | |
---|
23 | RSRC_DIR='/usr/share/repoman/rsrc' |
---|
24 | #RSRC_DIR='/home/lliurex/trabajo/repoman/rsrc' |
---|
25 | JSON_SRC_DIR='/usr/share/repoman/sources.d' |
---|
26 | APT_SRC_DIR='/etc/apt/sources.list.d' |
---|
27 | LOGIN_IMG=RSRC_DIR+'/repoman_login.png' |
---|
28 | LOGIN_BACKGROUND=RSRC_DIR+'/repoman_background.png' |
---|
29 | |
---|
30 | SPACING=6 |
---|
31 | MARGIN=6 |
---|
32 | |
---|
33 | class main: |
---|
34 | |
---|
35 | def __init__(self): |
---|
36 | self.dbg=True |
---|
37 | self.default_editor='' |
---|
38 | self.err_msg={1:_("Invalid Url"), |
---|
39 | 2:_("Can't add repository information.\nCheck your permissions"), |
---|
40 | 3:_("Can't write sources file.\nCheck your permissions"), |
---|
41 | 4:_("Repository not found at given Url"), |
---|
42 | 5:_("Repositories failed to update"), |
---|
43 | 6:_("Mirror not availabe"), |
---|
44 | 7:("This repository could'nt be overwrited") |
---|
45 | } |
---|
46 | self.result={} |
---|
47 | self._set_css_info() |
---|
48 | self.stack_dir=Gtk.StackTransitionType.SLIDE_LEFT |
---|
49 | self.n4d=None |
---|
50 | self.credentials=[] |
---|
51 | self.server=None |
---|
52 | self.repos={} |
---|
53 | self._render_gui() |
---|
54 | #def __init__ |
---|
55 | |
---|
56 | def _debug(self,msg): |
---|
57 | if self.dbg: |
---|
58 | print("repoman: %s"%msg) |
---|
59 | |
---|
60 | def _render_gui(self): |
---|
61 | self.mw=Gtk.Window() |
---|
62 | self.mw.set_title("RepoMan") |
---|
63 | self.mw.set_hexpand(True) |
---|
64 | self.mw.connect("destroy",self._on_destroy) |
---|
65 | self.mw.set_resizable(False) |
---|
66 | self.overlay=Gtk.Stack() |
---|
67 | self.mw.add(self.overlay) |
---|
68 | vbox=Gtk.VBox(False,True) |
---|
69 | self.overlay.set_name("WHITE_BACKGROUND") |
---|
70 | self.overlay.add_titled(vbox,"vbox","vbox") |
---|
71 | self.overlay.add_titled(self._render_login(),"login","login") |
---|
72 | self.overlay.set_visible_child_name("login") |
---|
73 | pb=GdkPixbuf.Pixbuf.new_from_file("%s/repoman.png"%RSRC_DIR) |
---|
74 | img_banner=Gtk.Image.new_from_pixbuf(pb) |
---|
75 | img_banner.props.halign=Gtk.Align.CENTER |
---|
76 | img_banner.set_margin_left(MARGIN*2) |
---|
77 | vbox.add(img_banner) |
---|
78 | toolbarbox=self._render_toolbar() |
---|
79 | vbox.add(toolbarbox) |
---|
80 | self.rev_question=Gtk.InfoBar() |
---|
81 | lbl_question=Gtk.Label() |
---|
82 | lbl_question.set_name("NOTIF_LABEL") |
---|
83 | lbl_question.set_halign(Gtk.Align.START) |
---|
84 | self.rev_question.props.no_show_all=True |
---|
85 | self.rev_question.get_content_area().add(lbl_question) |
---|
86 | img_cancel=Gtk.Image() |
---|
87 | img_cancel.set_from_icon_name(Gtk.STOCK_CANCEL,Gtk.IconSize.BUTTON) |
---|
88 | btn_cancel=Gtk.Button() |
---|
89 | btn_cancel.add(img_cancel) |
---|
90 | btn_cancel.set_halign(Gtk.Align.END) |
---|
91 | btn_cancel.props.no_show_all=False |
---|
92 | self.rev_question.add_action_widget(btn_cancel,Gtk.ResponseType.CANCEL) |
---|
93 | img_ok=Gtk.Image() |
---|
94 | img_ok.set_from_icon_name(Gtk.STOCK_OK,Gtk.IconSize.BUTTON) |
---|
95 | btn_ok=Gtk.Button() |
---|
96 | btn_ok.props.no_show_all=False |
---|
97 | btn_ok.set_halign(Gtk.Align.START) |
---|
98 | btn_ok.add(img_ok) |
---|
99 | self.rev_question.add_action_widget(btn_ok,Gtk.ResponseType.OK) |
---|
100 | self.rev_question.connect('response',self._manage_response) |
---|
101 | vbox.add(self.rev_question) |
---|
102 | self.stack = Gtk.Stack() |
---|
103 | self.stack.set_hexpand(True) |
---|
104 | self.stack.set_transition_duration(1000) |
---|
105 | self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT) |
---|
106 | # self.stack.set_visible_child_name("login") |
---|
107 | vbox.add(self.stack) |
---|
108 | self.rev_info=Gtk.Revealer() |
---|
109 | vbox.add(self.rev_info) |
---|
110 | lbl_update=Gtk.Label() |
---|
111 | lbl_update.set_name("NOTIF_LABEL") |
---|
112 | self.rev_info.add(lbl_update) |
---|
113 | self.rev_info.set_transition_duration(1000) |
---|
114 | self.mw.show_all() |
---|
115 | self.rev_info.set_reveal_child(False) |
---|
116 | # toolbarbox.hide() |
---|
117 | # img_banner.hide() |
---|
118 | self.box_info=Gtk.Grid() |
---|
119 | self.box_info.set_margin_bottom(MARGIN) |
---|
120 | self.box_info.set_margin_left(MARGIN) |
---|
121 | self.box_info.set_column_spacing(MARGIN) |
---|
122 | lbl_info=Gtk.Label() |
---|
123 | info=_("Repositories must be updated. Update now?") |
---|
124 | lbl_info.set_markup('<span color="grey">%s</span>'%info) |
---|
125 | # img_info=Gtk.Image().new_from_icon_name(Gtk.STOCK_REFRESH,Gtk.IconSize.BUTTON) |
---|
126 | pb_info=GdkPixbuf.Pixbuf.new_from_file_at_scale("%s/stock_refresh.png"%RSRC_DIR,16,16,True) |
---|
127 | img_info=Gtk.Image().new_from_pixbuf(pb_info) |
---|
128 | btn_info=Gtk.Button() |
---|
129 | btn_info.set_name("BLUEBUTTON") |
---|
130 | btn_info.set_tooltip_text(_("Update repositories")) |
---|
131 | btn_info.add(img_info) |
---|
132 | spn_info=Gtk.Spinner() |
---|
133 | self.box_info.attach(lbl_info,0,0,1,1) |
---|
134 | self.box_info.attach(btn_info,1,0,1,1) |
---|
135 | self.box_info.attach(spn_info,0,0,2,1) |
---|
136 | self.box_info.set_no_show_all(True) |
---|
137 | btn_info.connect("clicked",self._begin_update_repos,spn_info) |
---|
138 | vbox.add(self.box_info) |
---|
139 | Gtk.main() |
---|
140 | #def _render_gui |
---|
141 | |
---|
142 | def _render_toolbar(self): |
---|
143 | self.toolbar=Gtk.Box() |
---|
144 | self.toolbar=Gtk.Box(spacing=SPACING) |
---|
145 | self.toolbar.set_margin_top(MARGIN) |
---|
146 | self.toolbar.set_margin_bottom(MARGIN) |
---|
147 | self.toolbar.set_margin_left(MARGIN) |
---|
148 | |
---|
149 | btn_return=Gtk.Button()#.new_from_stock(Gtk.STOCK_GO_BACK) |
---|
150 | btn_return.add(Gtk.Image().new_from_icon_name(Gtk.STOCK_HOME,Gtk.IconSize.BUTTON)) |
---|
151 | btn_return.connect("clicked",self._load_screen,"sources") |
---|
152 | btn_return.props.halign=Gtk.Align.START |
---|
153 | btn_return.set_tooltip_text(_("Default repositories")) |
---|
154 | self.toolbar.add(btn_return) |
---|
155 | |
---|
156 | btn_manage=Gtk.Button() |
---|
157 | btn_manage.props.halign=Gtk.Align.START |
---|
158 | btn_manage.add(Gtk.Image().new_from_icon_name(Gtk.STOCK_PROPERTIES,Gtk.IconSize.BUTTON)) |
---|
159 | btn_manage.connect("clicked",self._load_screen,"repolist") |
---|
160 | btn_manage.set_tooltip_text(_("External repositories")) |
---|
161 | self.toolbar.add(btn_manage) |
---|
162 | |
---|
163 | btn_add=Gtk.Button()#.new_from_stock(Gtk.STOCK_ADD) |
---|
164 | btn_add.props.halign=Gtk.Align.START |
---|
165 | btn_add.add(Gtk.Image().new_from_icon_name(Gtk.STOCK_ADD,Gtk.IconSize.BUTTON)) |
---|
166 | btn_add.connect("clicked",self._load_screen,"newrepo") |
---|
167 | btn_add.set_tooltip_text(_("Add external repository")) |
---|
168 | self.toolbar.add(btn_add) |
---|
169 | |
---|
170 | return(self.toolbar) |
---|
171 | #def _render_toolbar |
---|
172 | |
---|
173 | def _render_login(self): |
---|
174 | login=N4dGtkLogin(orientation=Gtk.Orientation.VERTICAL) |
---|
175 | # login=N4dGtkLogin() |
---|
176 | login.set_mw_proportion_ratio(1,1) |
---|
177 | login.set_allowed_groups(['adm','teachers']) |
---|
178 | login.set_login_banner(image=LOGIN_IMG) |
---|
179 | login.set_label_background(255,255,255,0.3) |
---|
180 | login.set_mw_background(image=LOGIN_BACKGROUND,cover=True) |
---|
181 | desc=_("From here you can invoke RepoMan's mighty powers to manage your repositories.") |
---|
182 | login.set_info_text("<span foreground='black'>RepoMan</span>",_("Repositories Manager"),"<span foreground='black'>"+desc+"</span>\n") |
---|
183 | login.after_validation_goto(self._signin) |
---|
184 | login.hide_server_entry() |
---|
185 | login.show_all() |
---|
186 | return (login) |
---|
187 | |
---|
188 | def _signin(self,user=None,pwd=None,server=None,data=None): |
---|
189 | # self.scheduler.set_credentials(user,pwd,server) |
---|
190 | # self.stack.set_visible_child_name("sources") |
---|
191 | self.credentials=[user,pwd] |
---|
192 | self.server=server |
---|
193 | context=ssl._create_unverified_context() |
---|
194 | self.n4d=n4d.ServerProxy("https://%s:9779"%server,context=context,allow_none=True) |
---|
195 | self.stack.add_titled(self._render_sources(), "sources", "Sources") |
---|
196 | self.stack.add_titled(self._render_newrepo(), "newrepo", "Newrepo") |
---|
197 | self.stack.add_titled(self._render_repolist(), "repolist", "Repolist") |
---|
198 | self.overlay.set_transition_duration(1000) |
---|
199 | self.overlay.set_transition_type(Gtk.StackTransitionType.CROSSFADE) |
---|
200 | self.overlay.set_visible_child_name("vbox") |
---|
201 | self.mw.show_all() |
---|
202 | #def _signin |
---|
203 | |
---|
204 | def _render_sources(self): |
---|
205 | gridbox=Gtk.Grid() |
---|
206 | gridbox.set_column_homogeneous(False) |
---|
207 | gridbox.set_column_spacing(MARGIN) |
---|
208 | gridbox.set_name("WHITE_BACKGROUND") |
---|
209 | gridbox.set_margin_top(MARGIN) |
---|
210 | gridbox.set_margin_left(MARGIN) |
---|
211 | gridbox.set_margin_right(MARGIN) |
---|
212 | gridbox.set_margin_bottom(MARGIN) |
---|
213 | self.repos=self.n4d.list_default_repos(self.credentials,"RepoManager")['data'] |
---|
214 | #Sort by relevancy (Lliurex, Local, Ubuntu-*) |
---|
215 | sort_repos=OrderedDict() |
---|
216 | for repo in sorted(self.repos.keys()): |
---|
217 | sort_repos.update({repo:self.repos[repo]}) |
---|
218 | self.repos=sort_repos.copy() |
---|
219 | row=0 |
---|
220 | for source,sourcedata in self.repos.items(): |
---|
221 | desc='' |
---|
222 | if sourcedata['desc']: |
---|
223 | desc=_(sourcedata['desc']) |
---|
224 | self._insert_sourceslist_item(gridbox,source,desc,'',sourcedata['enabled'],row) |
---|
225 | row+=1 |
---|
226 | gridbox.set_margin_top(MARGIN) |
---|
227 | gridbox.set_margin_bottom(MARGIN*2) |
---|
228 | return(gridbox) |
---|
229 | #def _render_sources |
---|
230 | |
---|
231 | def _repo_state_changed(self,*args): |
---|
232 | self.stack.set_sensitive(False) |
---|
233 | self.box_info.set_no_show_all(False) |
---|
234 | reponame=args[-1] |
---|
235 | state=args[-2] |
---|
236 | widget=args[0] |
---|
237 | err=0 |
---|
238 | if reponame.lower()=="lliurex mirror": |
---|
239 | ret=subprocess.run(["lliurex-version","-m"],universal_newlines=True,stdout=subprocess.PIPE) |
---|
240 | if ret.stdout.strip()=="False": |
---|
241 | err=6 |
---|
242 | if err==0: |
---|
243 | self.repos[reponame].update({'enabled':str(state)}) |
---|
244 | repo={} |
---|
245 | repo={reponame:self.repos[reponame]} |
---|
246 | self._debug("New state: %s"%repo) |
---|
247 | self._debug("Saving repo json: %s"%repo) |
---|
248 | if self.n4d.write_repo_json(self.credentials,"RepoManager",repo)['status']: |
---|
249 | if self.n4d.write_repo(self.credentials,"RepoManager",repo)['status']!=True: |
---|
250 | err=3 |
---|
251 | else: |
---|
252 | err=2 |
---|
253 | if err: |
---|
254 | self.toolbar.set_sensitive(False) |
---|
255 | GLib.timeout_add(3000,self.show_info,(self.err_msg[err]),"ERROR_LABEL") |
---|
256 | widget.set_state(not(state)) |
---|
257 | return True |
---|
258 | else: |
---|
259 | self.stack.set_sensitive(True) |
---|
260 | self.box_info.show_all() |
---|
261 | #def _repo_state_changed |
---|
262 | |
---|
263 | def _render_repolist(self): |
---|
264 | scrollbox=Gtk.ScrolledWindow() |
---|
265 | scrollbox.set_min_content_height(280) |
---|
266 | scrollbox.set_min_content_width(280) |
---|
267 | self.repobox=Gtk.Grid() |
---|
268 | self.repobox.set_hexpand(True) |
---|
269 | self.repobox.set_row_spacing(MARGIN) |
---|
270 | self.repobox.set_margin_left(MARGIN) |
---|
271 | self.repobox.set_margin_right(MARGIN) |
---|
272 | self.repobox.set_margin_top(MARGIN) |
---|
273 | self.repobox.set_row_spacing(0) |
---|
274 | self.repobox.set_name("WHITE_BACKGROUND") |
---|
275 | sourcefiles=self.n4d.list_sources(self.credentials,"RepoManager")['data'] |
---|
276 | sort_repos=OrderedDict() |
---|
277 | for repo in sorted(sourcefiles.keys()): |
---|
278 | sort_repos.update({repo:sourcefiles[repo]}) |
---|
279 | sourcefiles=sort_repos.copy() |
---|
280 | self.repos.update(sourcefiles) |
---|
281 | row=0 |
---|
282 | for sourcefile,sourcedata in sourcefiles.items(): |
---|
283 | desc='' |
---|
284 | if sourcedata['desc']: |
---|
285 | desc=_(sourcedata['desc']) |
---|
286 | edit=True |
---|
287 | if 'protected' in sourcedata.keys(): |
---|
288 | if sourcedata['protected'].lower()=='true': |
---|
289 | edit=False |
---|
290 | self._insert_sourceslist_item(self.repobox,sourcefile,desc,'',sourcedata['enabled'],row,edit) |
---|
291 | row+=1 |
---|
292 | scrollbox.add(self.repobox) |
---|
293 | return scrollbox |
---|
294 | #def _render_repolist |
---|
295 | |
---|
296 | def _render_newrepo(self): |
---|
297 | def del_icon(*args): |
---|
298 | args[-1].set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,None) |
---|
299 | args[-1].set_placeholder_text("") |
---|
300 | |
---|
301 | gridbox=Gtk.Grid() |
---|
302 | gridbox.set_hexpand(True) |
---|
303 | gridbox.set_row_spacing(MARGIN) |
---|
304 | gridbox.set_margin_left(MARGIN) |
---|
305 | gridbox.set_margin_right(MARGIN) |
---|
306 | gridbox.set_margin_top(MARGIN) |
---|
307 | boxname=Gtk.VBox(True,True) |
---|
308 | boxname.set_name("WHITE_BACKGROUND") |
---|
309 | boxname.set_hexpand(True) |
---|
310 | lbl_name=Gtk.Label() |
---|
311 | lbl_name.set_name("ENTRY_LABEL") |
---|
312 | lbl_name.set_halign(Gtk.Align.START) |
---|
313 | lbl_name.set_markup("<sup>%s</sup>"%_("Name for the repo")) |
---|
314 | boxname.add(lbl_name) |
---|
315 | inp_name=Gtk.Entry() |
---|
316 | inp_name.connect("focus-in-event",del_icon,inp_name) |
---|
317 | boxname.add(inp_name) |
---|
318 | gridbox.add(boxname) |
---|
319 | boxdesc=Gtk.VBox(True,True) |
---|
320 | boxdesc.set_name("WHITE_BACKGROUND") |
---|
321 | lbl_desc=Gtk.Label() |
---|
322 | lbl_desc.set_name("ENTRY_LABEL") |
---|
323 | lbl_desc.set_halign(Gtk.Align.START) |
---|
324 | lbl_desc.set_markup("<sup>%s</sup>"%_("Description")) |
---|
325 | boxdesc.add(lbl_desc) |
---|
326 | inp_desc=Gtk.Entry() |
---|
327 | boxdesc.add(inp_desc) |
---|
328 | gridbox.attach_next_to(boxdesc,boxname,Gtk.PositionType.BOTTOM,1,1) |
---|
329 | boxurl=Gtk.VBox(True,True) |
---|
330 | boxurl.set_name("WHITE_BACKGROUND") |
---|
331 | lbl_url=Gtk.Label() |
---|
332 | lbl_url.set_name("ENTRY_LABEL") |
---|
333 | lbl_url.set_halign(Gtk.Align.START) |
---|
334 | lbl_url.set_markup("<sup>%s</sup>"%_("Url")) |
---|
335 | boxurl.add(lbl_url) |
---|
336 | inp_url=Gtk.Entry() |
---|
337 | inp_url.connect("focus-in-event",del_icon,inp_url) |
---|
338 | inp_url.connect("activate",self._begin_add_repo,inp_name,inp_desc,inp_url) |
---|
339 | boxurl.add(inp_url) |
---|
340 | gridbox.attach_next_to(boxurl,boxdesc,Gtk.PositionType.BOTTOM,1,1) |
---|
341 | boxbtn=Gtk.Box() |
---|
342 | btn_add=Gtk.Button.new_from_stock(Gtk.STOCK_APPLY) |
---|
343 | btn_add.connect("clicked",self._begin_add_repo,inp_name,inp_desc,inp_url) |
---|
344 | # btn_add.connect("clicked",self._add_repo,inp_name,inp_desc,inp_url) |
---|
345 | boxbtn.set_halign(Gtk.Align.END) |
---|
346 | boxbtn.add(btn_add) |
---|
347 | |
---|
348 | gridbox.attach_next_to(boxbtn,boxurl,Gtk.PositionType.BOTTOM,1,1) |
---|
349 | return(gridbox) |
---|
350 | #def _render_newrepo |
---|
351 | |
---|
352 | def _begin_add_repo(self,*args): |
---|
353 | name=args[-3].get_text() |
---|
354 | desc=args[-2].get_text() |
---|
355 | url=args[-1].get_text() |
---|
356 | sw_err=False |
---|
357 | if not name: |
---|
358 | args[-3].set_placeholder_text(_("Name is mandatory")) |
---|
359 | args[-3].set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,Gtk.STOCK_DIALOG_ERROR) |
---|
360 | sw_err=True |
---|
361 | elif len(name)>40: |
---|
362 | args[-3].set_placeholder_text(_("Name is too long")) |
---|
363 | args[-3].set_text("") |
---|
364 | args[-3].set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,Gtk.STOCK_DIALOG_ERROR) |
---|
365 | sw_err=True |
---|
366 | |
---|
367 | desc_array=desc.split(' ') |
---|
368 | for element in desc_array: |
---|
369 | if len(element)>40: |
---|
370 | args[-2].set_placeholder_text(_("Description is too long")) |
---|
371 | args[-2].set_text("") |
---|
372 | args[-2].set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,Gtk.STOCK_DIALOG_ERROR) |
---|
373 | sw_err=True |
---|
374 | |
---|
375 | if not url: |
---|
376 | args[-1].set_placeholder_text(_("Url is mandatory")) |
---|
377 | args[-1].set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,Gtk.STOCK_DIALOG_ERROR) |
---|
378 | sw_err=True |
---|
379 | if not sw_err: |
---|
380 | listfiles=os.listdir(JSON_SRC_DIR) |
---|
381 | lowfiles=[] |
---|
382 | for jsonfile in listfiles: |
---|
383 | lowfile=jsonfile.lower() |
---|
384 | lowfiles.append(lowfile) |
---|
385 | if name.replace(' ','_').lower()+'.json' in lowfiles: |
---|
386 | #see if the repo is protected |
---|
387 | for repo in self.repos.keys(): |
---|
388 | if name.lower().replace('_',' ')==repo.lower(): |
---|
389 | if 'protected' in self.repos[repo]: |
---|
390 | if self.repos[repo]['protected'].lower()=='true': |
---|
391 | self.stack.set_sensitive(False) |
---|
392 | self.toolbar.set_sensitive(False) |
---|
393 | err=7 |
---|
394 | GLib.timeout_add(3000,self.show_info,(self.err_msg[err]),"ERROR_LABEL") |
---|
395 | return |
---|
396 | |
---|
397 | self.result.pop('response',None) |
---|
398 | try: |
---|
399 | self.rev_question.disconnect_by_func(self._manage_response) |
---|
400 | except: |
---|
401 | pass |
---|
402 | self.rev_question.grab_add() |
---|
403 | self.rev_question.connect('response',self._manage_response,self._add_repo,*args) |
---|
404 | self.show_question(_("%s already exists. Overwrite it?")%name) |
---|
405 | else: |
---|
406 | self._add_repo(*args) |
---|
407 | |
---|
408 | def _add_repo(self,*args): |
---|
409 | name=args[-3].get_text() |
---|
410 | desc=args[-2].get_text() |
---|
411 | url=args[-1].get_text() |
---|
412 | self.stack.set_sensitive(False) |
---|
413 | self.toolbar.set_sensitive(False) |
---|
414 | listfiles=os.listdir(JSON_SRC_DIR) |
---|
415 | lowfiles={} |
---|
416 | for jsonfile in listfiles: |
---|
417 | lowfile=jsonfile.lower() |
---|
418 | lowfiles[lowfile]=jsonfile |
---|
419 | lowname=name.replace(' ','_').lower()+'.json' |
---|
420 | if lowname in lowfiles.keys(): |
---|
421 | name=lowfiles[lowname].replace('.json','').replace('_',' ') |
---|
422 | err=self.n4d.add_repo(self.credentials,"RepoManager",name,desc,url)['status'] |
---|
423 | if err==0: |
---|
424 | row=(len(self.repos.keys())*2) |
---|
425 | if name in self.repos.keys(): |
---|
426 | row=-1 |
---|
427 | sourcefiles=self.n4d.list_sources(self.credentials,"RepoManager")['data'] |
---|
428 | self.repos.update(sourcefiles) |
---|
429 | if row>=0: |
---|
430 | self._insert_sourceslist_item(self.repobox,name,desc,url,'true',row,True) |
---|
431 | GLib.timeout_add(2000,self.show_info,(_("Added repository %s"%name)),"NOTIF_LABEL",True) |
---|
432 | else: |
---|
433 | #err=1 -> Bad url |
---|
434 | #err=2 -> Can't write json |
---|
435 | #err=3 -> Can't write sources |
---|
436 | #err=4 -> Repository not found at given url |
---|
437 | GLib.timeout_add(3000,self.show_info,(self.err_msg[err]),"ERROR_LABEL") |
---|
438 | #def _add_repo |
---|
439 | |
---|
440 | def _insert_sourceslist_item(self,sourcebox,name,desc,url,enabled='false',index=0,edit=False): |
---|
441 | index*=2 |
---|
442 | repobox=Gtk.VBox(True,True) |
---|
443 | repobox.set_margin_left(MARGIN) |
---|
444 | repobox.set_margin_right(MARGIN) |
---|
445 | repobox.set_margin_bottom(MARGIN) |
---|
446 | repobox.set_margin_top(MARGIN) |
---|
447 | lbl_source=Gtk.Label() |
---|
448 | lbl_source.set_markup('<span size="larger">%s</span>'%name) |
---|
449 | lbl_source.set_halign(Gtk.Align.START) |
---|
450 | lbl_source.set_hexpand(True) |
---|
451 | lbl_source.set_margin_left(MARGIN) |
---|
452 | lbl_source.set_margin_bottom(MARGIN) |
---|
453 | lbl_source.set_margin_top(MARGIN) |
---|
454 | lbl_source.props.halign=Gtk.Align.START |
---|
455 | repobox.add(lbl_source) |
---|
456 | lbl_desc=Gtk.Label() |
---|
457 | lbl_desc.set_ellipsize(3) |
---|
458 | lbl_desc.set_markup('<span size="medium">%s</span>'%desc) |
---|
459 | lbl_desc.set_tooltip_text('%s'%desc) |
---|
460 | lbl_desc.set_halign(Gtk.Align.START) |
---|
461 | repobox.add(lbl_desc) |
---|
462 | if edit: |
---|
463 | img_edit=Gtk.Image.new_from_icon_name(Gtk.STOCK_EDIT,Gtk.IconSize.BUTTON) |
---|
464 | btn_edit=Gtk.Button() |
---|
465 | btn_edit.add(img_edit) |
---|
466 | btn_edit.set_tooltip_text(_("Edit sources file")) |
---|
467 | btn_edit.set_valign(Gtk.Align.CENTER) |
---|
468 | btn_edit.connect("clicked",self._edit_source_file,name) |
---|
469 | sourcebox.attach(btn_edit,1,index,1,1) |
---|
470 | swt_repo=Gtk.Switch() |
---|
471 | swt_repo.set_tooltip_text(_("Enable/disable repository") |
---|
472 | swt_repo.set_halign(Gtk.Align.END) |
---|
473 | if enabled.lower()=="true": |
---|
474 | swt_repo.set_active(True) |
---|
475 | else: |
---|
476 | swt_repo.set_active(False) |
---|
477 | swt_repo.connect("state_set",self._repo_state_changed,name) |
---|
478 | sourcebox.attach(repobox,0,index,1,1) |
---|
479 | sourcebox.attach(swt_repo,2,index,1,1) |
---|
480 | sourcebox.attach(Gtk.Separator(),0,index+1,1,1) |
---|
481 | sourcebox.show_all() |
---|
482 | #def _insert_sourceslist_item |
---|
483 | |
---|
484 | def _load_screen(self,*args): |
---|
485 | self.stack.set_transition_type(self.stack_dir) |
---|
486 | if self.stack_dir==Gtk.StackTransitionType.SLIDE_RIGHT: |
---|
487 | self.stack_dir=Gtk.StackTransitionType.SLIDE_LEFT |
---|
488 | else: |
---|
489 | self.stack_dir=Gtk.StackTransitionType.SLIDE_RIGHT |
---|
490 | screen=args[-1] |
---|
491 | self.stack.set_visible_child_name(screen) |
---|
492 | #def _load_screen |
---|
493 | |
---|
494 | def _begin_update_repos(self,*args): |
---|
495 | spinner=args[-1] |
---|
496 | spinner.show() |
---|
497 | spinner.start() |
---|
498 | self.stack.set_sensitive(False) |
---|
499 | self.toolbar.set_sensitive(False) |
---|
500 | th=threading.Thread(target=self._update_repos,args=[spinner]) |
---|
501 | th.start() |
---|
502 | GLib.timeout_add(1500,self._check_update,th,spinner) |
---|
503 | #def _begin_update_repos |
---|
504 | |
---|
505 | def _update_repos(self,spinner): |
---|
506 | self.result['update']=self.n4d.update_repos(self.credentials,"RepoManager")['status'] |
---|
507 | #def _update_repos |
---|
508 | |
---|
509 | def _check_update(self,th,spinner): |
---|
510 | if th.is_alive(): |
---|
511 | return True |
---|
512 | spinner.stop() |
---|
513 | self.stack.set_sensitive(True) |
---|
514 | self.toolbar.set_sensitive(True) |
---|
515 | self.box_info.hide() |
---|
516 | if self.result['update']: |
---|
517 | return(self.show_info(_("Repositories updated"))) |
---|
518 | else: |
---|
519 | return(self.show_info(self.err_msg[5],"ERROR_LABEL")) |
---|
520 | #def _check_update |
---|
521 | |
---|
522 | def _edit_source_file(self,*args): |
---|
523 | sfile=args[-1].replace(' ','_') |
---|
524 | self._debug("Editing %s.list"%sfile) |
---|
525 | if os.path.isfile("%s/%s.list"%(APT_SRC_DIR,sfile)): |
---|
526 | edit=True |
---|
527 | try: |
---|
528 | display=os.environ['DISPLAY'] |
---|
529 | if self.default_editor=='': |
---|
530 | self.default_editor=subprocess.check_output(["xdg-mime","query","default","text/plain"],universal_newlines=True).strip().rstrip('.desktop;') |
---|
531 | subprocess.run(["xhost","+"]) |
---|
532 | subprocess.run(["pkexec",self.default_editor,"%s/%s.list"%(APT_SRC_DIR,sfile),"--display=%s"%display],check=True) |
---|
533 | subprocess.run(["xhost","-"]) |
---|
534 | except Exception as e: |
---|
535 | self._debug("_edit_source_file error: %s"%e) |
---|
536 | edit=False |
---|
537 | if edit: |
---|
538 | newrepos=[] |
---|
539 | try: |
---|
540 | with open("%s/%s.list"%(APT_SRC_DIR,sfile),'r') as f: |
---|
541 | for line in f: |
---|
542 | newrepos.append(line.strip()) |
---|
543 | except Exception as e: |
---|
544 | self._debug("_edit_source_file failed: %s"%e) |
---|
545 | if sorted(self.repos[args[-1]]['repos'])!=sorted(newrepos): |
---|
546 | self.repos[args[-1]]['repos']=newrepos |
---|
547 | self.n4d.write_repo_json(self.credentials,"RepoManager",{args[-1]:self.repos[args[-1]]}) |
---|
548 | self.box_info.show_all() |
---|
549 | else: |
---|
550 | self._debug("File %s/%s not found"%(APT_SRC_DIR,sfile)) |
---|
551 | #def _edit_source_file |
---|
552 | |
---|
553 | def show_info(self,msg='',style="NOTIF_LABEL",show_info=False): |
---|
554 | if self.rev_info.get_reveal_child(): |
---|
555 | self.rev_info.set_reveal_child(False) |
---|
556 | self.stack.set_sensitive(True) |
---|
557 | self.toolbar.set_sensitive(True) |
---|
558 | if show_info: |
---|
559 | self.box_info.show_all() |
---|
560 | return False |
---|
561 | lbl=None |
---|
562 | for child in self.rev_info.get_children(): |
---|
563 | if type(child)==type(Gtk.Label()): |
---|
564 | lbl=child |
---|
565 | if lbl: |
---|
566 | lbl.set_name(style) |
---|
567 | lbl.set_markup(msg) |
---|
568 | self.rev_info.set_reveal_child(True) |
---|
569 | return True |
---|
570 | #def show_info |
---|
571 | |
---|
572 | def show_question(self,msg='',style="NOTIF_LABEL"): |
---|
573 | self.rev_question.grab_add() |
---|
574 | for child in self.rev_question.get_content_area(): |
---|
575 | if type(child)==type(Gtk.Label()): |
---|
576 | lbl=child |
---|
577 | for child in self.rev_question.get_action_area(): |
---|
578 | if type(child)==type(Gtk.Button()): |
---|
579 | child.show_all() |
---|
580 | |
---|
581 | lbl.set_markup(msg) |
---|
582 | lbl.set_line_wrap(True) |
---|
583 | lbl.set_lines(-1) |
---|
584 | lbl.set_max_width_chars(20) |
---|
585 | lbl.show() |
---|
586 | self.rev_question.show() |
---|
587 | #def show_question |
---|
588 | |
---|
589 | def _manage_response(self,*args): |
---|
590 | self.rev_question.grab_remove() |
---|
591 | response=args[1] |
---|
592 | self.rev_question.hide() |
---|
593 | if response==Gtk.ResponseType.OK: |
---|
594 | args[2](args[-3],args[-2],args[-1]) |
---|
595 | else: |
---|
596 | return False |
---|
597 | |
---|
598 | def _on_destroy(self,*args): |
---|
599 | Gtk.main_quit() |
---|
600 | |
---|
601 | def _set_css_info(self): |
---|
602 | |
---|
603 | css = b""" |
---|
604 | |
---|
605 | GtkEntry{ |
---|
606 | font-family: Roboto; |
---|
607 | border:0px; |
---|
608 | border-bottom:1px grey solid; |
---|
609 | margin-top:0px; |
---|
610 | padding-top:0px; |
---|
611 | } |
---|
612 | |
---|
613 | GtkLabel { |
---|
614 | font-family: Roboto; |
---|
615 | } |
---|
616 | |
---|
617 | #NOTIF_LABEL{ |
---|
618 | background-color: #3366cc; |
---|
619 | font: 11px Roboto; |
---|
620 | color:white; |
---|
621 | border: dashed 1px silver; |
---|
622 | padding:6px; |
---|
623 | } |
---|
624 | |
---|
625 | #ERROR_LABEL{ |
---|
626 | background-color: red; |
---|
627 | font: 11px Roboto; |
---|
628 | color:white; |
---|
629 | border: dashed 1px silver; |
---|
630 | padding:6px; |
---|
631 | } |
---|
632 | |
---|
633 | #ENTRY_LABEL{ |
---|
634 | color:grey; |
---|
635 | padding:6px; |
---|
636 | padding-bottom:0px; |
---|
637 | } |
---|
638 | |
---|
639 | #PLAIN_BTN,#PLAIN_BTN:active{ |
---|
640 | border:0px; |
---|
641 | padding:0px; |
---|
642 | background:white; |
---|
643 | } |
---|
644 | |
---|
645 | #PLAIN_BTN_DISABLED,#PLAIN_BTN_DISABLED:active{ |
---|
646 | border:0px; |
---|
647 | padding:0px; |
---|
648 | background:white; |
---|
649 | font:grey; |
---|
650 | } |
---|
651 | |
---|
652 | #COMPONENT{ |
---|
653 | padding:3px; |
---|
654 | border: dashed 1px silver; |
---|
655 | |
---|
656 | } |
---|
657 | |
---|
658 | #WHITE_BACKGROUND { |
---|
659 | background-color:rgba(255,255,255,1); |
---|
660 | |
---|
661 | } |
---|
662 | |
---|
663 | #BLUE_FONT { |
---|
664 | color: #3366cc; |
---|
665 | font: Roboto Bold 11; |
---|
666 | |
---|
667 | } |
---|
668 | |
---|
669 | |
---|
670 | #TASKGRID_FONT { |
---|
671 | color: #3366cc; |
---|
672 | font: Roboto 11; |
---|
673 | |
---|
674 | } |
---|
675 | |
---|
676 | #LABEL #LABEL_INSTALL{ |
---|
677 | padding: 6px; |
---|
678 | margin:6px; |
---|
679 | font: 12px Roboto; |
---|
680 | } |
---|
681 | |
---|
682 | #LABEL_OPTION{ |
---|
683 | |
---|
684 | font: 48px Roboto; |
---|
685 | padding: 6px; |
---|
686 | margin:6px; |
---|
687 | font-weight:bold; |
---|
688 | } |
---|
689 | |
---|
690 | #ERROR_FONT { |
---|
691 | color: #CC0000; |
---|
692 | font: Roboto Bold 11; |
---|
693 | } |
---|
694 | |
---|
695 | #MENUITEM { |
---|
696 | padding: 12px; |
---|
697 | margin:6px; |
---|
698 | font: 24px Roboto; |
---|
699 | background:white; |
---|
700 | } |
---|
701 | |
---|
702 | #BLUEBUTTON { |
---|
703 | background-color: #3366cc; |
---|
704 | color:white; |
---|
705 | font: 11px Roboto Bold; |
---|
706 | } |
---|
707 | |
---|
708 | """ |
---|
709 | self.style_provider=Gtk.CssProvider() |
---|
710 | self.style_provider.load_from_data(css) |
---|
711 | Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),self.style_provider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) |
---|
712 | #def set_css_info |
---|
713 | |
---|
714 | GObject.threads_init() |
---|
715 | main() |
---|