Merge lp:~mvo/software-center/expunge-cache-5.0 into lp:software-center
- expunge-cache-5.0
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mvo/software-center/expunge-cache-5.0 |
Merge into: | lp:software-center |
Diff against target: |
1104 lines (+786/-86) (has conflicts) 13 files modified
debian/changelog (+42/-2) softwarecenter/backend/scagent.py (+7/-0) softwarecenter/db/update.py (+163/-0) softwarecenter/ui/gtk3/app.py (+30/-0) softwarecenter/ui/gtk3/panes/availablepane.py (+12/-3) softwarecenter/ui/gtk3/panes/installedpane.py (+17/-0) softwarecenter/ui/gtk3/panes/softwarepane.py (+90/-1) softwarecenter/ui/gtk3/views/purchaseview.py (+17/-0) softwarecenter/ui/gtk3/widgets/buttons.py (+5/-0) test/test_database.py (+4/-0) test/test_utils.py (+39/-0) utils/expunge-cache.py (+166/-80) utils/piston-helpers/piston_get_scagent_available_apps.py.OTHER (+194/-0) Text conflict in debian/changelog Text conflict in softwarecenter/backend/scagent.py Text conflict in softwarecenter/db/update.py Text conflict in softwarecenter/ui/gtk3/app.py Text conflict in softwarecenter/ui/gtk3/panes/availablepane.py Text conflict in softwarecenter/ui/gtk3/panes/installedpane.py Text conflict in softwarecenter/ui/gtk3/panes/softwarepane.py Text conflict in softwarecenter/ui/gtk3/views/purchaseview.py Text conflict in softwarecenter/ui/gtk3/widgets/buttons.py Text conflict in test/test_database.py Text conflict in utils/expunge-cache.py Contents conflict in utils/piston-helpers/piston_get_scagent_available_apps.py |
To merge this branch: | bzr merge lp:~mvo/software-center/expunge-cache-5.0 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Lasker (community) | Approve | ||
Review via email: mp+95966@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-03-06.
Commit message
Description of the change
Trivial addition to be more gently on the CPU with os.nice()
Unmerged revisions
- 2491. By Michael Vogt
-
add os.nice()
- 2490. By Michael Vogt
-
merged expunge-cache branch for 5.0
- 2489. By Gary Lasker
-
testing (LP: #918746)
- 2488. By Michael Vogt
-
* lp:~gary-lasker/software-center/staging-certs-2-for-5.0:
- add SOFTWARE_CENTER_ FORCE_DISABLE_ CERTS_CHECK to allow QA easier
testing - 2487. By Michael Vogt
-
grab exhibits for the current series only (LP: #899257)
- 2486. By Michael Vogt
-
* lp:~mvo/software-center/fix-server-pagination:
- reset server side review "page" when switching to a different
app - 2485. By Michael Vogt
-
* lp:~gary-lasker/software-center/fix-lp913756-for-5.0:
- do not offer to add an icon to the Unity launcher for packages
that do not have an Exec entry in their corresponding desktop
file, e.g. ubuntu-restricted- extras, wine (LP: #913756) - 2484. By Michael Vogt
-
releasing version 5.0.4
- 2483. By Gary Lasker
-
download icons directly using the provided URL (LP: #914054)
- 2482. By Michael Vogt
-
test fixes
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2012-03-01 19:53:28 +0000 |
3 | +++ debian/changelog 2012-03-05 18:23:26 +0000 |
4 | @@ -1,3 +1,4 @@ |
5 | +<<<<<<< TREE |
6 | software-center (5.1.11) precise; urgency=low |
7 | |
8 | [ Michael Vogt ] |
9 | @@ -553,11 +554,50 @@ |
10 | |
11 | software-center (5.0.3.2) UNRELEASED; urgency=low |
12 | |
13 | +======= |
14 | +software-center (5.0.5) UNRELEASED; urgency=low |
15 | + |
16 | + [ Gary Lasker ] |
17 | + * lp:~gary-lasker/software-center/fix-lp913756-for-5.0: |
18 | + - do not offer to add an icon to the Unity launcher for packages |
19 | + that do not have an Exec entry in their corresponding desktop |
20 | + file, e.g. ubuntu-restricted-extras, wine (LP: #913756) |
21 | + * lp:~gary-lasker/software-center/staging-certs-2-for-5.0: |
22 | + - add SOFTWARE_CENTER_FORCE_DISABLE_CERTS_CHECK to allow QA easier |
23 | + testing (LP: #918746) |
24 | + |
25 | + [ Michael Vogt ] |
26 | + * lp:~mvo/software-center/fix-server-pagination: |
27 | + - reset server side review "page" when switching to a different |
28 | + app |
29 | + |
30 | + [ Kiwinote ] |
31 | + * grab exhibits for the current series only (LP: #899257) |
32 | + |
33 | + -- Gary Lasker <gary.lasker@canonical.com> Thu, 19 Jan 2012 10:54:47 -0500 |
34 | + |
35 | +software-center (5.0.4) oneiric-proposed; urgency=low |
36 | + |
37 | + [ Gary Lasker ] |
38 | +>>>>>>> MERGE-SOURCE |
39 | * lp:~gary-lasker/software-center/fix-lp891499-for-5.0: |
40 | - be more robust about problems reading the cataloged_times file |
41 | as problems here can hang the UI (LP: #891499) |
42 | - |
43 | - -- Gary Lasker <gary.lasker@canonical.com> Mon, 28 Nov 2011 14:22:24 -0500 |
44 | + |
45 | + [ Gabor Kelemen ] |
46 | + * lp:~kelemeng/software-center/bug869935: |
47 | + - Update help translations from Launchpad (LP: #869935) |
48 | + * lp:~kelemeng/software-center/bug880757: |
49 | + - Mark strings containing the “ character as Unicode, to fix |
50 | + their translations. LP: #880757 |
51 | + |
52 | + [ Michael Vogt ] |
53 | + * fix cache opening to improve startup time |
54 | + * lp:~mvo/software-center/icon-data-for-5.0: |
55 | + - remove the need for inline icon data from the agent, instead |
56 | + download icons directly using the provided URL (LP: #914054) |
57 | + |
58 | + -- Michael Vogt <michael.vogt@ubuntu.com> Tue, 10 Jan 2012 09:52:09 +0100 |
59 | |
60 | software-center (5.0.3.1) oneiric-proposed; urgency=low |
61 | |
62 | |
63 | === modified file 'setup.py' |
64 | === modified file 'softwarecenter/backend/scagent.py' |
65 | --- softwarecenter/backend/scagent.py 2012-01-17 15:56:14 +0000 |
66 | +++ softwarecenter/backend/scagent.py 2012-03-05 18:23:26 +0000 |
67 | @@ -107,6 +107,13 @@ |
68 | self.emit("available-for-me", piston_available_for_me) |
69 | |
70 | def query_exhibits(self): |
71 | +<<<<<<< TREE |
72 | +======= |
73 | + cmd = self.HELPER_CMD[:] |
74 | + cmd.append("exhibits") |
75 | + cmd.append(get_language()) |
76 | + cmd.append(self.distro.get_codename()) |
77 | +>>>>>>> MERGE-SOURCE |
78 | spawner = SpawnHelper() |
79 | spawner.parent_xid = self.xid |
80 | spawner.ignore_cache = self.ignore_cache |
81 | |
82 | === modified file 'softwarecenter/db/update.py' |
83 | --- softwarecenter/db/update.py 2012-02-28 22:35:19 +0000 |
84 | +++ softwarecenter/db/update.py 2012-03-05 18:23:26 +0000 |
85 | @@ -672,6 +672,11 @@ |
86 | while context.pending(): |
87 | context.iteration() |
88 | try: |
89 | +<<<<<<< TREE |
90 | +======= |
91 | + # magic channel |
92 | + entry.channel = AVAILABLE_FOR_PURCHASE_MAGIC_CHANNEL_NAME |
93 | +>>>>>>> MERGE-SOURCE |
94 | # now the normal parser |
95 | parser = SCAApplicationParser(entry) |
96 | index_app_info_from_parser(parser, db, cache) |
97 | @@ -932,8 +937,166 @@ |
98 | seen.add(name) |
99 | |
100 | index_name(doc, name, term_generator) |
101 | +<<<<<<< TREE |
102 | |
103 | pkgname = doc.get_value(XapianValues.PKGNAME) |
104 | +======= |
105 | + doc.add_value(XapianValues.APPNAME_UNTRANSLATED, untranslated_name) |
106 | + |
107 | + # check if we should ignore this file |
108 | + if parser.has_option_desktop("X-AppInstall-Ignore"): |
109 | + ignore = parser.get_desktop("X-AppInstall-Ignore") |
110 | + if ignore.strip().lower() == "true": |
111 | + LOG.debug("X-AppInstall-Ignore found for '%s'" % parser.desktopf) |
112 | + return |
113 | + # architecture |
114 | + pkgname_extension = '' |
115 | + if parser.has_option_desktop("X-AppInstall-Architectures"): |
116 | + arches = parser.get_desktop("X-AppInstall-Architectures") |
117 | + doc.add_value(XapianValues.ARCHIVE_ARCH, arches) |
118 | + native_archs = get_current_arch() in arches.split(',') |
119 | + foreign_archs = list(set(arches.split(',')) & set(get_foreign_architectures())) |
120 | + if not (native_archs or foreign_archs): return |
121 | + if not native_archs and foreign_archs: |
122 | + pkgname_extension = ':' + foreign_archs[0] |
123 | + # package name |
124 | + pkgname = parser.get_desktop("X-AppInstall-Package") + pkgname_extension |
125 | + doc.add_term("AP"+pkgname) |
126 | + if '-' in pkgname: |
127 | + # we need this to work around xapian oddness |
128 | + doc.add_term(pkgname.replace('-','_')) |
129 | + doc.add_value(XapianValues.PKGNAME, pkgname) |
130 | + doc.add_value(XapianValues.DESKTOP_FILE, parser.desktopf) |
131 | + # display name |
132 | + if "display_name" in axi_values: |
133 | + doc.add_value(axi_values["display_name"], name) |
134 | + # cataloged_times |
135 | + if "catalogedtime" in axi_values: |
136 | + if pkgname in cataloged_times: |
137 | + doc.add_value(axi_values["catalogedtime"], |
138 | + xapian.sortable_serialise(cataloged_times[pkgname])) |
139 | + else: |
140 | + # also catalog apps not found in axi (e.g. for-purchase apps) |
141 | + doc.add_value(axi_values["catalogedtime"], |
142 | + xapian.sortable_serialise(time.time())) |
143 | + # pocket (main, restricted, ...) |
144 | + if parser.has_option_desktop("X-AppInstall-Section"): |
145 | + archive_section = parser.get_desktop("X-AppInstall-Section") |
146 | + doc.add_term("AS"+archive_section) |
147 | + doc.add_value(XapianValues.ARCHIVE_SECTION, archive_section) |
148 | + # section (mail, base, ..) |
149 | + if pkgname in cache and cache[pkgname].candidate: |
150 | + section = cache[pkgname].section |
151 | + doc.add_term("AE"+section) |
152 | + # channel (third party stuff) |
153 | + if parser.has_option_desktop("X-AppInstall-Channel"): |
154 | + archive_channel = parser.get_desktop("X-AppInstall-Channel") |
155 | + doc.add_term("AH"+archive_channel) |
156 | + doc.add_value(XapianValues.ARCHIVE_CHANNEL, archive_channel) |
157 | + # signing key (third party) |
158 | + if parser.has_option_desktop("X-AppInstall-Signing-Key-Id"): |
159 | + keyid = parser.get_desktop("X-AppInstall-Signing-Key-Id") |
160 | + doc.add_value(XapianValues.ARCHIVE_SIGNING_KEY_ID, keyid) |
161 | + # license (third party) |
162 | + if parser.has_option_desktop("X-AppInstall-License"): |
163 | + license = parser.get_desktop("X-AppInstall-License") |
164 | + doc.add_value(XapianValues.LICENSE, license) |
165 | + # purchased date |
166 | + if parser.has_option_desktop("X-AppInstall-Purchased-Date"): |
167 | + date = parser.get_desktop("X-AppInstall-Purchased-Date") |
168 | + # strip the subseconds from the end of the date string |
169 | + doc.add_value(XapianValues.PURCHASED_DATE, str(date).split(".")[0]) |
170 | + # deb-line (third party) |
171 | + if parser.has_option_desktop("X-AppInstall-Deb-Line"): |
172 | + debline = parser.get_desktop("X-AppInstall-Deb-Line") |
173 | + doc.add_value(XapianValues.ARCHIVE_DEB_LINE, debline) |
174 | + # license key (third party) |
175 | + if parser.has_option_desktop("X-AppInstall-License-Key"): |
176 | + key = parser.get_desktop("X-AppInstall-License-Key") |
177 | + doc.add_value(XapianValues.LICENSE_KEY, key) |
178 | + # license keypath (third party) |
179 | + if parser.has_option_desktop("X-AppInstall-License-Key-Path"): |
180 | + path = parser.get_desktop("X-AppInstall-License-Key-Path") |
181 | + doc.add_value(XapianValues.LICENSE_KEY_PATH, path) |
182 | + # PPA (third party stuff) |
183 | + if parser.has_option_desktop("X-AppInstall-PPA"): |
184 | + archive_ppa = parser.get_desktop("X-AppInstall-PPA") |
185 | + doc.add_value(XapianValues.ARCHIVE_PPA, archive_ppa) |
186 | + # add archive origin data here so that its available even if |
187 | + # the PPA is not (yet) enabled |
188 | + doc.add_term("XOO"+"lp-ppa-%s" % archive_ppa.replace("/", "-")) |
189 | + # screenshot (for third party) |
190 | + if parser.has_option_desktop("X-AppInstall-Screenshot-Url"): |
191 | + url = parser.get_desktop("X-AppInstall-Screenshot-Url") |
192 | + doc.add_value(XapianValues.SCREENSHOT_URL, url) |
193 | + # thumbnail (for third party) |
194 | + if parser.has_option_desktop("X-AppInstall-Thumbnail-Url"): |
195 | + url = parser.get_desktop("X-AppInstall-Thumbnail-Url") |
196 | + doc.add_value(XapianValues.THUMBNAIL_URL, url) |
197 | + # icon (for third party) |
198 | + if parser.has_option_desktop("X-AppInstall-Icon-Url"): |
199 | + url = parser.get_desktop("X-AppInstall-Icon-Url") |
200 | + doc.add_value(XapianValues.ICON_URL, url) |
201 | + if not parser.has_option_desktop("X-AppInstall-Icon"): |
202 | + # prefix pkgname to avoid name clashes |
203 | + doc.add_value(XapianValues.ICON, "%s-icon-%s" % ( |
204 | + pkgname, os.path.basename(url))) |
205 | + |
206 | + # price (pay stuff) |
207 | + if parser.has_option_desktop("X-AppInstall-Price"): |
208 | + price = parser.get_desktop("X-AppInstall-Price") |
209 | + doc.add_value(XapianValues.PRICE, price) |
210 | + # since this is a commercial app, indicate it in the component value |
211 | + doc.add_value(XapianValues.ARCHIVE_SECTION, "commercial") |
212 | + # icon |
213 | + if parser.has_option_desktop("Icon"): |
214 | + icon = parser.get_desktop("Icon") |
215 | + doc.add_value(XapianValues.ICON, icon) |
216 | + # write out categories |
217 | + for cat in parser.get_desktop_categories(): |
218 | + doc.add_term("AC"+cat.lower()) |
219 | + categories_string = ";".join(parser.get_desktop_categories()) |
220 | + doc.add_value(XapianValues.CATEGORIES, categories_string) |
221 | + for mime in parser.get_desktop_mimetypes(): |
222 | + doc.add_term("AM"+mime.lower()) |
223 | + # get type (to distinguish between apps and packages |
224 | + if parser.has_option_desktop("Type"): |
225 | + type = parser.get_desktop("Type") |
226 | + doc.add_term("AT"+type.lower()) |
227 | + # check gettext domain |
228 | + if parser.has_option_desktop("X-Ubuntu-Gettext-Domain"): |
229 | + domain = parser.get_desktop("X-Ubuntu-Gettext-Domain") |
230 | + doc.add_value(XapianValues.GETTEXT_DOMAIN, domain) |
231 | + # Description (software-center extension) |
232 | + if parser.has_option_desktop("X-AppInstall-Description"): |
233 | + descr = parser.get_desktop("X-AppInstall-Description") |
234 | + doc.add_value(XapianValues.SC_DESCRIPTION, descr) |
235 | + # popcon |
236 | + # FIXME: popularity not only based on popcon but also |
237 | + # on archive section, third party app etc |
238 | + if parser.has_option_desktop("X-AppInstall-Popcon"): |
239 | + popcon = float(parser.get_desktop("X-AppInstall-Popcon")) |
240 | + # sort_by_value uses string compare, so we need to pad here |
241 | + doc.add_value(XapianValues.POPCON, |
242 | + xapian.sortable_serialise(popcon)) |
243 | + global popcon_max |
244 | + popcon_max = max(popcon_max, popcon) |
245 | + |
246 | + # comment goes into the summary data if there is one, |
247 | + # other wise we try GenericName and if nothing else, |
248 | + # the summary of the package |
249 | + if parser.has_option_desktop("Comment"): |
250 | + s = parser.get_desktop("Comment") |
251 | + doc.add_value(XapianValues.SUMMARY, s) |
252 | + elif parser.has_option_desktop("GenericName"): |
253 | + s = parser.get_desktop("GenericName") |
254 | + if s != name: |
255 | + doc.add_value(XapianValues.SUMMARY, s) |
256 | + elif pkgname in cache and cache[pkgname].candidate: |
257 | + s = cache[pkgname].candidate.summary |
258 | + doc.add_value(XapianValues.SUMMARY, s) |
259 | + |
260 | +>>>>>>> MERGE-SOURCE |
261 | # add packagename as meta-data too |
262 | term_generator.index_text_without_positions(pkgname, WEIGHT_APT_PKGNAME) |
263 | |
264 | |
265 | === modified file 'softwarecenter/ui/gtk3/app.py' |
266 | --- softwarecenter/ui/gtk3/app.py 2012-02-29 17:23:24 +0000 |
267 | +++ softwarecenter/ui/gtk3/app.py 2012-03-05 18:23:26 +0000 |
268 | @@ -387,6 +387,7 @@ |
269 | if not (options.enable_lp or och): |
270 | file_menu.remove(self.builder.get_object("separator_login")) |
271 | else: |
272 | +<<<<<<< TREE |
273 | # running the agent will trigger a db reload so we do it later |
274 | GObject.timeout_add_seconds(30, self._run_software_center_agent) |
275 | |
276 | @@ -394,6 +395,22 @@ |
277 | # keep the cache clean |
278 | GObject.timeout_add_seconds(15, self._run_expunge_cache_helper) |
279 | |
280 | +======= |
281 | + sc_agent_update = os.path.join( |
282 | + self.datadir, "update-software-center-agent") |
283 | + (pid, stdin, stdout, stderr) = GObject.spawn_async( |
284 | + [sc_agent_update, "--datadir", datadir], |
285 | + flags=GObject.SPAWN_DO_NOT_REAP_CHILD) |
286 | + GObject.child_watch_add( |
287 | + pid, self._on_update_software_center_agent_finished) |
288 | + |
289 | + if options.disable_buy and not options.enable_lp: |
290 | + file_menu.remove(self.builder.get_object("separator_login")) |
291 | + |
292 | + # keep the cache clean |
293 | + GObject.timeout_add_seconds(45, self._run_expunge_cache_helper) |
294 | + |
295 | +>>>>>>> MERGE-SOURCE |
296 | # TODO: Remove the following two lines once we have remove repository |
297 | # support in aptdaemon (see LP: #723911) |
298 | file_menu = self.builder.get_object("menu1") |
299 | @@ -413,6 +430,7 @@ |
300 | |
301 | |
302 | # helper |
303 | +<<<<<<< TREE |
304 | def _run_software_center_agent(self): |
305 | """ helper that triggers the update-software-center-agent helper """ |
306 | sc_agent_update = os.path.join( |
307 | @@ -433,6 +451,18 @@ |
308 | softwarecenter.paths.SOFTWARE_CENTER_CACHE_DIR, |
309 | ]) |
310 | |
311 | +======= |
312 | + def _run_expunge_cache_helper(self): |
313 | + """ helper that expires the piston-mini-client cache """ |
314 | + sc_expunge_cache = os.path.join( |
315 | + self.datadir, "expunge-cache.py") |
316 | + (pid, stdin, stdout, stderr) = GObject.spawn_async( |
317 | + [sc_expunge_cache, |
318 | + "--by-unsuccessful-http-states", |
319 | + softwarecenter.paths.SOFTWARE_CENTER_CACHE_DIR, |
320 | + ]) |
321 | + |
322 | +>>>>>>> MERGE-SOURCE |
323 | def _rebuild_and_reopen_local_db(self, pathname): |
324 | """ helper that rebuilds a db and reopens it """ |
325 | from softwarecenter.db.update import rebuild_database |
326 | |
327 | === modified file 'softwarecenter/ui/gtk3/models/appstore2.py' |
328 | === modified file 'softwarecenter/ui/gtk3/panes/availablepane.py' |
329 | --- softwarecenter/ui/gtk3/panes/availablepane.py 2012-02-28 13:14:06 +0000 |
330 | +++ softwarecenter/ui/gtk3/panes/availablepane.py 2012-03-05 18:23:26 +0000 |
331 | @@ -134,9 +134,18 @@ |
332 | #~ self.app_view._append_appcount(appcount) |
333 | #~ liststore.connect('appcount-changed', on_appcount_changed) |
334 | self.app_view.set_model(liststore) |
335 | - liststore.connect( |
336 | - "needs-refresh", lambda helper, pkgname: self.app_view.queue_draw()) |
337 | - |
338 | +<<<<<<< TREE |
339 | + liststore.connect( |
340 | + "needs-refresh", lambda helper, pkgname: self.app_view.queue_draw()) |
341 | + |
342 | +======= |
343 | + # setup purchase stuff |
344 | + self.app_details_view.connect("purchase-requested", |
345 | + self.on_purchase_requested) |
346 | + liststore.connect( |
347 | + "needs-refresh", lambda helper, pkgname: self.app_view.queue_draw()) |
348 | + |
349 | +>>>>>>> MERGE-SOURCE |
350 | # purchase view |
351 | self.purchase_view = PurchaseView() |
352 | app_manager = get_appmanager() |
353 | |
354 | === modified file 'softwarecenter/ui/gtk3/panes/installedpane.py' |
355 | --- softwarecenter/ui/gtk3/panes/installedpane.py 2012-02-28 13:51:20 +0000 |
356 | +++ softwarecenter/ui/gtk3/panes/installedpane.py 2012-03-05 18:23:26 +0000 |
357 | @@ -156,9 +156,14 @@ |
358 | oneconftoolbar.set_orientation(Gtk.Orientation.HORIZONTAL) |
359 | oneconfpropertymenu = Gtk.Menu() |
360 | self.oneconfproperty = MenuButton(oneconfpropertymenu, Gtk.Image.new_from_stock(Gtk.STOCK_PROPERTIES, Gtk.IconSize.BUTTON)) |
361 | +<<<<<<< TREE |
362 | self.stopsync_label = _(u"Stop Syncing “%s”") |
363 | stop_oneconf_share_menuitem = Gtk.MenuItem(label=self.stopsync_label % platform.node()) |
364 | stop_oneconf_share_menuitem.connect("activate", self._on_stop_oneconf_hostshare_clicked) |
365 | +======= |
366 | + stop_oneconf_share_menuitem = Gtk.MenuItem(label=_(u"Stop Syncing “%s”") % platform.node()) |
367 | + stop_oneconf_share_menuitem.connect("activate", self._on_stop_showing_oneconf_clicked) |
368 | +>>>>>>> MERGE-SOURCE |
369 | stop_oneconf_share_menuitem.show() |
370 | oneconfpropertymenu.append(stop_oneconf_share_menuitem) |
371 | self.oneconfcontrol.pack_start(oneconftoolbar, False, False, 1) |
372 | @@ -433,9 +438,15 @@ |
373 | L = len(enq.matches) |
374 | |
375 | if L: |
376 | +<<<<<<< TREE |
377 | cat_title = utf8(ngettext(u'%(amount)s item on “%(machine)s” not on this computer', |
378 | u'%(amount)s items on “%(machine)s” not on this computer', |
379 | L)) % { 'amount' : L, 'machine': utf8(self.current_hostname)} |
380 | +======= |
381 | + cat_title = ngettext(u'%(amount)s item on “%(machine)s” not on this computer', |
382 | + u'%(amount)s items on “%(machine)s” not on this computer', |
383 | + L) % { 'amount' : L, 'machine': self.current_hostname} |
384 | +>>>>>>> MERGE-SOURCE |
385 | i += L |
386 | docs = enq.get_documents() |
387 | self.cat_docid_map["missingpkg"] = set([doc.get_docid() for doc in docs]) |
388 | @@ -455,9 +466,15 @@ |
389 | |
390 | L = len(enq.matches) |
391 | if L: |
392 | +<<<<<<< TREE |
393 | cat_title = utf8(ngettext(u'%(amount)s item on this computer not on “%(machine)s”', |
394 | '%(amount)s items on this computer not on “%(machine)s”', |
395 | L)) % { 'amount' : L, 'machine': utf8(self.current_hostname)} |
396 | +======= |
397 | + cat_title = ngettext(u'%(amount)s item on this computer not on “%(machine)s”', |
398 | + u'%(amount)s items on this computer not on “%(machine)s”', |
399 | + L) % { 'amount' : L, 'machine': self.current_hostname} |
400 | +>>>>>>> MERGE-SOURCE |
401 | i += L |
402 | docs = enq.get_documents() |
403 | self.cat_docid_map["additionalpkg"] = set([doc.get_docid() for doc in docs]) |
404 | |
405 | === modified file 'softwarecenter/ui/gtk3/panes/softwarepane.py' |
406 | --- softwarecenter/ui/gtk3/panes/softwarepane.py 2012-02-28 13:51:20 +0000 |
407 | +++ softwarecenter/ui/gtk3/panes/softwarepane.py 2012-03-05 18:23:26 +0000 |
408 | @@ -34,7 +34,8 @@ |
409 | |
410 | from softwarecenter.utils import (ExecutionTime, |
411 | wait_for_apt_cache_ready, |
412 | - utf8 |
413 | + utf8, |
414 | + get_exec_line_from_desktop |
415 | ) |
416 | |
417 | from softwarecenter.ui.gtk3.session.viewmanager import get_viewmanager |
418 | @@ -43,8 +44,15 @@ |
419 | from softwarecenter.ui.gtk3.widgets.searchaid import SearchAid |
420 | |
421 | from softwarecenter.ui.gtk3.views.appview import AppView |
422 | +<<<<<<< TREE |
423 | from softwarecenter.ui.gtk3.views.appdetailsview import AppDetailsView |
424 | |
425 | +======= |
426 | +from softwarecenter.ui.gtk3.views.appdetailsview_gtk import ( |
427 | + AppDetailsViewGtk as |
428 | + AppDetailsView) |
429 | + |
430 | +>>>>>>> MERGE-SOURCE |
431 | from basepane import BasePane |
432 | |
433 | LOG = logging.getLogger(__name__) |
434 | @@ -283,6 +291,87 @@ |
435 | vm = get_viewmanager() |
436 | vm.nav_forward() |
437 | |
438 | +<<<<<<< TREE |
439 | +======= |
440 | + def on_transaction_started(self, backend, pkgname, appname, trans_id, |
441 | + trans_type): |
442 | + self._register_unity_launcher_transaction_started( |
443 | + backend, pkgname, appname, trans_id, trans_type) |
444 | + |
445 | + |
446 | + def _get_onscreen_icon_details_for_launcher_service(self, app): |
447 | + if self.is_app_details_view_showing(): |
448 | + return self.app_details_view.get_app_icon_details() |
449 | + else: |
450 | + # TODO: implement the app list view case once it has been specified |
451 | + return (0, 0, 0) |
452 | + |
453 | + def _register_unity_launcher_transaction_started(self, backend, pkgname, |
454 | + appname, trans_id, |
455 | + trans_type): |
456 | + # mvo: use use softwarecenter.utils explictely so that we can monkey |
457 | + # patch it in the test |
458 | + if not softwarecenter.utils.is_unity_running(): |
459 | + return |
460 | + # add to launcher only applies in the details view currently |
461 | + if not self.is_app_details_view_showing(): |
462 | + return |
463 | + # we only care about getting the launcher information on an install |
464 | + if not trans_type == TransactionTypes.INSTALL: |
465 | + if pkgname in self.unity_launcher_items: |
466 | + self.unity_launcher_items.pop(pkgname) |
467 | + self.action_bar.clear() |
468 | + return |
469 | + # gather details for this transaction and create the launcher_info object |
470 | + app = Application(pkgname=pkgname, appname=appname) |
471 | + appdetails = app.get_details(self.db) |
472 | + (icon_size, icon_x, icon_y) = self._get_onscreen_icon_details_for_launcher_service(app) |
473 | + launcher_info = UnityLauncherInfo(app.name, |
474 | + appdetails.icon, |
475 | + "", # we set the icon_file_path value *after* install |
476 | + icon_x, |
477 | + icon_y, |
478 | + icon_size, |
479 | + appdetails.desktop_file, |
480 | + "", # we set the installed_desktop_file_path *after* install |
481 | + trans_id) |
482 | + self.unity_launcher_items[app.pkgname] = launcher_info |
483 | + self.show_add_to_launcher_panel(backend, pkgname, appname, app, appdetails, trans_id, trans_type) |
484 | + |
485 | + def show_add_to_launcher_panel(self, backend, pkgname, appname, app, appdetails, trans_id, trans_type): |
486 | + """ |
487 | + if Unity is currently running, display a panel to allow the user |
488 | + the choose whether to add a newly-installed application to the |
489 | + launcher |
490 | + """ |
491 | + # TODO: handle local deb install case |
492 | + # TODO: implement the list view case (once it is specified) |
493 | + # only show the panel if unity is running and this is a package install |
494 | + # |
495 | + # we only show the prompt for apps with a desktop file |
496 | + if not appdetails.desktop_file: |
497 | + return |
498 | + # do not add apps that have no Exec entry in their desktop file |
499 | + # (e.g. wine, see LP: #848437 or ubuntu-restricted-extras, |
500 | + # see LP: #913756) |
501 | + if (os.path.exists(appdetails.desktop_file) and |
502 | + not get_exec_line_from_desktop(appdetails.desktop_file)): |
503 | + return |
504 | + |
505 | + self.action_bar.add_button(ActionButtons.CANCEL_ADD_TO_LAUNCHER, |
506 | + _("Not Now"), |
507 | + self.on_cancel_add_to_launcher, |
508 | + pkgname) |
509 | + self.action_bar.add_button(ActionButtons.ADD_TO_LAUNCHER, |
510 | + _("Add to Launcher"), |
511 | + self.on_add_to_launcher, |
512 | + pkgname, |
513 | + app, |
514 | + appdetails, |
515 | + trans_id) |
516 | + self.action_bar.set_label(utf8(_("Add %s to the launcher?")) % utf8(app.name)) |
517 | + |
518 | +>>>>>>> MERGE-SOURCE |
519 | def on_query_complete(self, enquirer): |
520 | self.emit("app-list-changed", len(enquirer.matches)) |
521 | self.app_view.display_matches(enquirer.matches, |
522 | |
523 | === modified file 'softwarecenter/ui/gtk3/views/appdetailsview.py' |
524 | === modified file 'softwarecenter/ui/gtk3/views/purchaseview.py' |
525 | --- softwarecenter/ui/gtk3/views/purchaseview.py 2012-02-09 10:34:40 +0000 |
526 | +++ softwarecenter/ui/gtk3/views/purchaseview.py 2012-03-05 18:23:26 +0000 |
527 | @@ -20,14 +20,21 @@ |
528 | from gi.repository import GObject |
529 | from gi.repository import Gtk |
530 | from gi.repository import Gdk |
531 | +<<<<<<< TREE |
532 | from gi.repository import Pango |
533 | +======= |
534 | +from gi.repository import WebKit as webkit |
535 | +>>>>>>> MERGE-SOURCE |
536 | import logging |
537 | import os |
538 | import json |
539 | import sys |
540 | import urllib |
541 | +<<<<<<< TREE |
542 | import urlparse |
543 | from gi.repository import WebKit as webkit |
544 | +======= |
545 | +>>>>>>> MERGE-SOURCE |
546 | |
547 | from gettext import gettext as _ |
548 | |
549 | @@ -36,6 +43,16 @@ |
550 | |
551 | LOG = logging.getLogger(__name__) |
552 | |
553 | +# enable certificates validation in webkit views unless specified otherwise |
554 | +if not "SOFTWARE_CENTER_FORCE_DISABLE_CERTS_CHECK" in os.environ: |
555 | + session = webkit.get_default_session() |
556 | + session.set_property("ssl-ca-file", "/etc/ssl/certs/ca-certificates.crt") |
557 | +else: |
558 | + # WARN the user!! Do not remove this |
559 | + LOG.warning("SOFTWARE_CENTER_FORCE_DISABLE_CERTS_CHECK " + |
560 | + "has been specified, all purchase transactions " + |
561 | + "are now INSECURE and UNENCRYPTED!!") |
562 | + |
563 | class LocaleAwareWebView(webkit.WebView): |
564 | |
565 | def __init__(self): |
566 | |
567 | === modified file 'softwarecenter/ui/gtk3/widgets/buttons.py' |
568 | --- softwarecenter/ui/gtk3/widgets/buttons.py 2012-02-20 15:59:17 +0000 |
569 | +++ softwarecenter/ui/gtk3/widgets/buttons.py 2012-03-05 18:23:26 +0000 |
570 | @@ -186,8 +186,13 @@ |
571 | label = helper.get_appname(doc) |
572 | icon = helper.get_icon_at_size(doc, icon_size, icon_size) |
573 | stats = helper.get_review_stats(doc) |
574 | +<<<<<<< TREE |
575 | helper.update_availability(doc) |
576 | helper.connect("needs-refresh", self._on_needs_refresh, doc, icon_size) |
577 | +======= |
578 | + doc.installed = doc.available = None |
579 | + helper.connect("needs-refresh", self._on_needs_refresh, doc, icon_size) |
580 | +>>>>>>> MERGE-SOURCE |
581 | self.is_installed = helper.is_installed(doc) |
582 | self._overlay = helper.icons.load_icon(Icons.INSTALLED_OVERLAY, |
583 | self.INSTALLED_OVERLAY_SIZE, |
584 | |
585 | === modified file 'test/test_database.py' |
586 | --- test/test_database.py 2012-02-28 22:35:19 +0000 |
587 | +++ test/test_database.py 2012-03-05 18:23:26 +0000 |
588 | @@ -150,12 +150,16 @@ |
589 | ppa.count("/") == 1, |
590 | "ARCHIVE_PPA value incorrect, got '%s'" % ppa) |
591 | self.assertTrue( |
592 | +<<<<<<< TREE |
593 | "-icon-" in doc.get_value(XapianValues.ICON)) |
594 | # check support url in the DB |
595 | url=doc.get_value(XapianValues.SUPPORT_SITE_URL) |
596 | if url: |
597 | self.assertTrue(url.startswith("http") or |
598 | url.startswith("mailto:")) |
599 | +======= |
600 | + "-icon-" in doc.get_value(XapianValues.ICON)) |
601 | +>>>>>>> MERGE-SOURCE |
602 | |
603 | def test_license_string_data_from_software_center_agent(self): |
604 | #os.environ["SOFTWARE_CENTER_DEBUG_HTTP"] = "1" |
605 | |
606 | === modified file 'test/test_utils.py' |
607 | --- test/test_utils.py 2012-03-01 11:32:37 +0000 |
608 | +++ test/test_utils.py 2012-03-05 18:23:26 +0000 |
609 | @@ -161,6 +161,45 @@ |
610 | self.assertTrue(os.path.exists(os.path.join(dirname, "foo-random"))) |
611 | |
612 | |
613 | +class TestExpungeCache(unittest.TestCase): |
614 | + |
615 | + def test_expunge_cache(self): |
616 | + import subprocess |
617 | + import tempfile |
618 | + dirname = tempfile.mkdtemp('s-c-testsuite') |
619 | + for name, content in [ ("foo-301", "status: 301"), |
620 | + ("foo-200", "status: 200"), |
621 | + ("foo-random", "random"), |
622 | + ]: |
623 | + fullpath = os.path.join(dirname, name) |
624 | + open(fullpath, "w").write(content) |
625 | + # set to 1970+1s time to ensure the cleaner finds it |
626 | + os.utime(fullpath, (1,1)) |
627 | + res = subprocess.call(["../utils/expunge-cache.py", dirname]) |
628 | + # no arguments |
629 | + self.assertEqual(res, 1) |
630 | + # by status |
631 | + res = subprocess.call(["../utils/expunge-cache.py", |
632 | + "--debug", |
633 | + "--by-unsuccessful-http-states", |
634 | + dirname]) |
635 | + self.assertFalse(os.path.exists(os.path.join(dirname, "foo-301"))) |
636 | + self.assertTrue(os.path.exists(os.path.join(dirname, "foo-200"))) |
637 | + self.assertTrue(os.path.exists(os.path.join(dirname, "foo-random"))) |
638 | + |
639 | + # by time |
640 | + res = subprocess.call(["../utils/expunge-cache.py", |
641 | + "--debug", |
642 | + "--by-days", "1", |
643 | + dirname]) |
644 | + # now we expect the old file to be gone but the unknown one not to |
645 | + # be touched |
646 | + self.assertFalse(os.path.exists(os.path.join(dirname, "foo-200"))) |
647 | + self.assertTrue(os.path.exists(os.path.join(dirname, "foo-random"))) |
648 | + |
649 | + |
650 | + |
651 | + |
652 | if __name__ == "__main__": |
653 | import logging |
654 | logging.basicConfig(level=logging.DEBUG) |
655 | |
656 | === modified file 'utils/expunge-cache.py' |
657 | --- utils/expunge-cache.py 2012-02-29 17:14:43 +0000 |
658 | +++ utils/expunge-cache.py 2012-03-05 18:23:26 +0000 |
659 | @@ -1,80 +1,166 @@ |
660 | -#!/usr/bin/python |
661 | - |
662 | -""" |
663 | -Expunge httplib2 caches |
664 | -""" |
665 | - |
666 | -import argparse |
667 | -import logging |
668 | -import os |
669 | -import time |
670 | -import sys |
671 | - |
672 | -class ExpungeCache(object): |
673 | - def __init__(self, dirs, args): |
674 | - self.dirs = dirs |
675 | - # days to keep data in the cache (0 == disabled) |
676 | - self.keep_time = 60*60*24* args.by_days |
677 | - self.keep_only_http200 = args.by_unsuccessful_http_states |
678 | - self.dry_run = args.dry_run |
679 | - |
680 | - def _rm(self, f): |
681 | - if self.dry_run: |
682 | - print "Would delete: %s" % f |
683 | - else: |
684 | - logging.debug("Deleting: %s" % f) |
685 | - os.unlink(f) |
686 | - |
687 | - def clean(self): |
688 | - # go over the directories |
689 | - now = time.time() |
690 | - for d in self.dirs: |
691 | - for root, dirs, files in os.walk(d): |
692 | - for f in files: |
693 | - fullpath = os.path.join(root, f) |
694 | - header = open(fullpath).readline().strip() |
695 | - if not header.startswith("status:"): |
696 | - logging.debug( |
697 | - "Skipping files with unknown header: '%s'" % f) |
698 | - continue |
699 | - if self.keep_only_http200 and header != "status: 200": |
700 | - self._rm(fullpath) |
701 | - if self.keep_time: |
702 | - mtime = os.path.getmtime(fullpath) |
703 | - logging.debug("mtime of '%s': '%s" % (f, mtime)) |
704 | - if (mtime + self.keep_time) < now: |
705 | - self._rm(fullpath) |
706 | - |
707 | -if __name__ == "__main__": |
708 | - parser = argparse.ArgumentParser( |
709 | - description='clean software-center httplib2 cache') |
710 | - parser.add_argument( |
711 | - '--debug', action="store_true", |
712 | - help='show debug output') |
713 | - parser.add_argument( |
714 | - '--dry-run', action="store_true", |
715 | - help='do not act, just show what would be done') |
716 | - parser.add_argument( |
717 | - 'directories', metavar='directory', nargs='+', type=str, |
718 | - help='directories to be checked') |
719 | - parser.add_argument( |
720 | - '--by-days', type=int, default=0, |
721 | - help='expire everything older than N days') |
722 | - parser.add_argument( |
723 | - '--by-unsuccessful-http-states', action="store_true", |
724 | - help='expire any non 200 status responses') |
725 | - args = parser.parse_args() |
726 | - |
727 | - if args.debug: |
728 | - logging.basicConfig(level=logging.DEBUG) |
729 | - else: |
730 | - logging.basicConfig(level=logging.INFO) |
731 | - |
732 | - # sanity checking |
733 | - if args.by_days == 0 and not args.by_unsuccessful_http_states: |
734 | - print "Need either --by-days or --by-unsuccessful-http-states argument" |
735 | - sys.exit(1) |
736 | - |
737 | - # do it |
738 | - cleaner = ExpungeCache(args.directories, args) |
739 | - cleaner.clean() |
740 | +<<<<<<< TREE |
741 | +#!/usr/bin/python |
742 | + |
743 | +""" |
744 | +Expunge httplib2 caches |
745 | +""" |
746 | + |
747 | +import argparse |
748 | +import logging |
749 | +import os |
750 | +import time |
751 | +import sys |
752 | + |
753 | +class ExpungeCache(object): |
754 | + def __init__(self, dirs, args): |
755 | + self.dirs = dirs |
756 | + # days to keep data in the cache (0 == disabled) |
757 | + self.keep_time = 60*60*24* args.by_days |
758 | + self.keep_only_http200 = args.by_unsuccessful_http_states |
759 | + self.dry_run = args.dry_run |
760 | + |
761 | + def _rm(self, f): |
762 | + if self.dry_run: |
763 | + print "Would delete: %s" % f |
764 | + else: |
765 | + logging.debug("Deleting: %s" % f) |
766 | + os.unlink(f) |
767 | + |
768 | + def clean(self): |
769 | + # go over the directories |
770 | + now = time.time() |
771 | + for d in self.dirs: |
772 | + for root, dirs, files in os.walk(d): |
773 | + for f in files: |
774 | + fullpath = os.path.join(root, f) |
775 | + header = open(fullpath).readline().strip() |
776 | + if not header.startswith("status:"): |
777 | + logging.debug( |
778 | + "Skipping files with unknown header: '%s'" % f) |
779 | + continue |
780 | + if self.keep_only_http200 and header != "status: 200": |
781 | + self._rm(fullpath) |
782 | + if self.keep_time: |
783 | + mtime = os.path.getmtime(fullpath) |
784 | + logging.debug("mtime of '%s': '%s" % (f, mtime)) |
785 | + if (mtime + self.keep_time) < now: |
786 | + self._rm(fullpath) |
787 | + |
788 | +if __name__ == "__main__": |
789 | + parser = argparse.ArgumentParser( |
790 | + description='clean software-center httplib2 cache') |
791 | + parser.add_argument( |
792 | + '--debug', action="store_true", |
793 | + help='show debug output') |
794 | + parser.add_argument( |
795 | + '--dry-run', action="store_true", |
796 | + help='do not act, just show what would be done') |
797 | + parser.add_argument( |
798 | + 'directories', metavar='directory', nargs='+', type=str, |
799 | + help='directories to be checked') |
800 | + parser.add_argument( |
801 | + '--by-days', type=int, default=0, |
802 | + help='expire everything older than N days') |
803 | + parser.add_argument( |
804 | + '--by-unsuccessful-http-states', action="store_true", |
805 | + help='expire any non 200 status responses') |
806 | + args = parser.parse_args() |
807 | + |
808 | + if args.debug: |
809 | + logging.basicConfig(level=logging.DEBUG) |
810 | + else: |
811 | + logging.basicConfig(level=logging.INFO) |
812 | + |
813 | + # sanity checking |
814 | + if args.by_days == 0 and not args.by_unsuccessful_http_states: |
815 | + print "Need either --by-days or --by-unsuccessful-http-states argument" |
816 | + sys.exit(1) |
817 | + |
818 | + # do it |
819 | + cleaner = ExpungeCache(args.directories, args) |
820 | + cleaner.clean() |
821 | +======= |
822 | +#!/usr/bin/python |
823 | + |
824 | +""" |
825 | +Expunge httplib2 caches |
826 | +""" |
827 | + |
828 | +import argparse |
829 | +import logging |
830 | +import os |
831 | +import time |
832 | +import sys |
833 | + |
834 | +class ExpungeCache(object): |
835 | + def __init__(self, dirs, args): |
836 | + self.dirs = dirs |
837 | + # days to keep data in the cache (0 == disabled) |
838 | + self.keep_time = 60*60*24* args.by_days |
839 | + self.keep_only_http200 = args.by_unsuccessful_http_states |
840 | + self.dry_run = args.dry_run |
841 | + |
842 | + def _rm(self, f): |
843 | + if self.dry_run: |
844 | + print "Would delete: %s" % f |
845 | + else: |
846 | + logging.debug("Deleting: %s" % f) |
847 | + os.unlink(f) |
848 | + |
849 | + def clean(self): |
850 | + # go over the directories |
851 | + now = time.time() |
852 | + for d in self.dirs: |
853 | + for root, dirs, files in os.walk(d): |
854 | + for f in files: |
855 | + fullpath = os.path.join(root, f) |
856 | + header = open(fullpath).readline().strip() |
857 | + if not header.startswith("status:"): |
858 | + logging.debug( |
859 | + "Skipping files with unknown header: '%s'" % f) |
860 | + continue |
861 | + if self.keep_only_http200 and header != "status: 200": |
862 | + self._rm(fullpath) |
863 | + if self.keep_time: |
864 | + mtime = os.path.getmtime(fullpath) |
865 | + logging.debug("mtime of '%s': '%s" % (f, mtime)) |
866 | + if (mtime + self.keep_time) < now: |
867 | + self._rm(fullpath) |
868 | + |
869 | +if __name__ == "__main__": |
870 | + parser = argparse.ArgumentParser( |
871 | + description='clean software-center httplib2 cache') |
872 | + parser.add_argument( |
873 | + '--debug', action="store_true", |
874 | + help='show debug output') |
875 | + parser.add_argument( |
876 | + '--dry-run', action="store_true", |
877 | + help='do not act, just show what would be done') |
878 | + parser.add_argument( |
879 | + 'directories', metavar='directory', nargs='+', type=str, |
880 | + help='directories to be checked') |
881 | + parser.add_argument( |
882 | + '--by-days', type=int, default=0, |
883 | + help='expire everything older than N days') |
884 | + parser.add_argument( |
885 | + '--by-unsuccessful-http-states', action="store_true", |
886 | + help='expire any non 200 status responses') |
887 | + args = parser.parse_args() |
888 | + |
889 | + if args.debug: |
890 | + logging.basicConfig(level=logging.DEBUG) |
891 | + else: |
892 | + logging.basicConfig(level=logging.INFO) |
893 | + |
894 | + # sanity checking |
895 | + if args.by_days == 0 and not args.by_unsuccessful_http_states: |
896 | + print "Need either --by-days or --by-unsuccessful-http-states argument" |
897 | + sys.exit(1) |
898 | + |
899 | + # be nice |
900 | + os.nice(19) |
901 | + |
902 | + # do it |
903 | + cleaner = ExpungeCache(args.directories, args) |
904 | + cleaner.clean() |
905 | +>>>>>>> MERGE-SOURCE |
906 | |
907 | === added file 'utils/piston-helpers/piston_get_scagent_available_apps.py.OTHER' |
908 | --- utils/piston-helpers/piston_get_scagent_available_apps.py.OTHER 1970-01-01 00:00:00 +0000 |
909 | +++ utils/piston-helpers/piston_get_scagent_available_apps.py.OTHER 2012-03-05 18:23:26 +0000 |
910 | @@ -0,0 +1,194 @@ |
911 | +#!/usr/bin/python |
912 | + |
913 | +from gi.repository import GObject |
914 | + |
915 | +import argparse |
916 | +import logging |
917 | +import os |
918 | +import pickle |
919 | +import sys |
920 | + |
921 | +import piston_mini_client.auth |
922 | + |
923 | +from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING, |
924 | + SOFTWARE_CENTER_SSO_DESCRIPTION, |
925 | + ) |
926 | +from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR |
927 | +from softwarecenter.backend.piston.scaclient import SoftwareCenterAgentAPI |
928 | +from softwarecenter.backend.login_sso import get_sso_backend |
929 | +from softwarecenter.backend.restfulclient import UbuntuSSOAPI |
930 | +from softwarecenter.utils import clear_token_from_ubuntu_sso |
931 | + |
932 | +from gettext import gettext as _ |
933 | + |
934 | +LOG = logging.getLogger(__name__) |
935 | + |
936 | +class SSOLoginHelper(object): |
937 | + def __init__(self, xid=0): |
938 | + self.oauth = None |
939 | + self.xid = xid |
940 | + self.loop = GObject.MainLoop(GObject.main_context_default()) |
941 | + |
942 | + def _login_successful(self, sso_backend, oauth_result): |
943 | + self.oauth = oauth_result |
944 | + # FIXME: actually verify the token against ubuntu SSO |
945 | + self.loop.quit() |
946 | + |
947 | + def verify_token(self, token): |
948 | + def _whoami_done(sso, me): |
949 | + self._whoami = me |
950 | + self.loop.quit() |
951 | + self._whoami = None |
952 | + sso = UbuntuSSOAPI(token) |
953 | + sso.connect("whoami", _whoami_done) |
954 | + sso.connect("error", lambda sso, err: self.loop.quit()) |
955 | + sso.whoami() |
956 | + self.loop.run() |
957 | + # check if the token is valid |
958 | + if self._whoami is None: |
959 | + return False |
960 | + else: |
961 | + return True |
962 | + |
963 | + def clear_token(self): |
964 | + clear_token_from_ubuntu_sso(SOFTWARE_CENTER_NAME_KEYRING) |
965 | + |
966 | + def get_oauth_token_sync(self): |
967 | + self.oauth = None |
968 | + sso = get_sso_backend( |
969 | + self.xid, |
970 | + SOFTWARE_CENTER_NAME_KEYRING, |
971 | + _(SOFTWARE_CENTER_SSO_DESCRIPTION)) |
972 | + sso.connect("login-successful", self._login_successful) |
973 | + sso.connect("login-failed", lambda s: self.loop.quit()) |
974 | + sso.connect("login-canceled", lambda s: self.loop.quit()) |
975 | + sso.login_or_register() |
976 | + self.loop.run() |
977 | + return self.oauth |
978 | + |
979 | +if __name__ == "__main__": |
980 | + logging.basicConfig() |
981 | + |
982 | + # command line parser |
983 | + parser = argparse.ArgumentParser(description="Helper for software-center-agent") |
984 | + parser.add_argument("--debug", action="store_true", default=False, |
985 | + help="enable debug output") |
986 | + parser.add_argument("--ignore-cache", action="store_true", default=False, |
987 | + help="force ignore cache") |
988 | + parser.add_argument("--parent-xid", default=0, |
989 | + help="xid of the parent window") |
990 | + |
991 | + subparser = parser.add_subparsers(title="Commands") |
992 | + # available_apps |
993 | + command = subparser.add_parser("available_apps") |
994 | + command.add_argument("lang") |
995 | + command.add_argument("series") |
996 | + command.add_argument("arch") |
997 | + command.set_defaults(command="available_apps") |
998 | + |
999 | + # available_apps_qa |
1000 | + command = subparser.add_parser("available_apps_qa") |
1001 | + command.add_argument("lang") |
1002 | + command.add_argument("series") |
1003 | + command.add_argument("arch") |
1004 | + command.set_defaults(command="available_apps_qa") |
1005 | + # subscriptions |
1006 | + command = subparser.add_parser("subscriptions_for_me") |
1007 | + command.set_defaults(command="subscriptions_for_me") |
1008 | + # exhibits |
1009 | + command = subparser.add_parser("exhibits") |
1010 | + command.add_argument("lang") |
1011 | + command.add_argument("series") |
1012 | + command.set_defaults(command="exhibits") |
1013 | + |
1014 | + args = parser.parse_args() |
1015 | + |
1016 | + if args.debug: |
1017 | + LOG.setLevel(logging.DEBUG) |
1018 | + |
1019 | + if args.ignore_cache: |
1020 | + cachedir = None |
1021 | + else: |
1022 | + cachedir = os.path.join(SOFTWARE_CENTER_CACHE_DIR, "scaclient") |
1023 | + |
1024 | + |
1025 | + # check if auth is required |
1026 | + if args.command in ("available_apps_qa", "subscriptions_for_me"): |
1027 | + helper = SSOLoginHelper(args.parent_xid) |
1028 | + token = helper.get_oauth_token_sync() |
1029 | + # check if the token is valid and reset it if it is not |
1030 | + if token and not helper.verify_token(token): |
1031 | + helper.clear_token() |
1032 | + # re-trigger login |
1033 | + token = helper.get_oauth_token_sync() |
1034 | + # if we don't have a token, error here |
1035 | + if not token: |
1036 | + sys.stderr.write("ERROR: can not obtain a oauth token\n") |
1037 | + sys.exit(1) |
1038 | + |
1039 | + auth = piston_mini_client.auth.OAuthAuthorizer(token["token"], |
1040 | + token["token_secret"], |
1041 | + token["consumer_key"], |
1042 | + token["consumer_secret"]) |
1043 | + scaclient = SoftwareCenterAgentAPI(cachedir=cachedir, auth=auth) |
1044 | + else: |
1045 | + scaclient = SoftwareCenterAgentAPI(cachedir=cachedir) |
1046 | + |
1047 | + piston_reply = None |
1048 | + |
1049 | + # common kwargs |
1050 | + if args.command in ("available_apps", "available_apps_qa"): |
1051 | + kwargs = {"lang": args.lang, |
1052 | + "series": args.series, |
1053 | + "arch": args.arch |
1054 | + } |
1055 | + |
1056 | + # handle the args |
1057 | + if args.command == "available_apps": |
1058 | + try: |
1059 | + piston_reply = scaclient.available_apps(**kwargs) |
1060 | + except: |
1061 | + LOG.exception("available_apps") |
1062 | + sys.exit(1) |
1063 | + |
1064 | + elif args.command == "available_apps_qa": |
1065 | + try: |
1066 | + piston_reply = scaclient.available_apps_qa(**kwargs) |
1067 | + except: |
1068 | + LOG.exception("available_apps_qa") |
1069 | + sys.exit(1) |
1070 | + elif args.command == "subscriptions_for_me": |
1071 | + try: |
1072 | + piston_reply = scaclient.subscriptions_for_me(complete_only=True) |
1073 | + # the new piston API send the data in a nasty format, most |
1074 | + # interessting stuff is in the "application" dict, move it |
1075 | + # back int othe main object here so that the parser understands it |
1076 | + for item in piston_reply: |
1077 | + for k, v in item.application.iteritems(): |
1078 | + setattr(item, k, v) |
1079 | + except: |
1080 | + LOG.exception("subscriptions_for_me") |
1081 | + sys.exit(1) |
1082 | + if args.command == "exhibits": |
1083 | + try: |
1084 | + piston_reply = scaclient.exhibits(lang=args.lang, series=args.series) |
1085 | + except: |
1086 | + LOG.exception("exhibits") |
1087 | + sys.exit(1) |
1088 | + |
1089 | + if args.debug: |
1090 | + LOG.debug("reply: %s" % piston_reply) |
1091 | + for item in piston_reply: |
1092 | + for var in vars(item): |
1093 | + print "%s: %s" % (var, getattr(item, var)) |
1094 | + print "\n\n" |
1095 | + |
1096 | + |
1097 | + # print to stdout where its consumed by the parent |
1098 | + if piston_reply is not None: |
1099 | + try: |
1100 | + print pickle.dumps(piston_reply) |
1101 | + except IOError: |
1102 | + # this can happen if the parent gets killed, no need to trigger |
1103 | + # apport for this |
1104 | + pass |
Just a note that I merged the lp:~mvo/software-center/expunge-cache branch, which is the one targeting trunk. Thanks!