3 Module for Straw's configuration settings.
5 __copyright__
= "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__
= """ GNU General Public License
8 This program is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
13 This program is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA. """
21 import cPickle
, os
, traceback
23 from error
import log
, logtb
, logparam
29 GCONF_STRAW_ROOT
= "/apps/straw"
31 OPTION_LAST_POLL
= "/general/last_poll"
32 OPTION_ITEMS_STORED
= "/general/number_of_items_stored"
33 OPTION_ITEM_ORDER
= "/general/item_order_newest"
34 OPTION_BROWSER_CMD
= "/general/browser_cmd"
35 OPTION_WINDOW_SIZE_W
= "/ui/window_width"
36 OPTION_WINDOW_SIZE_H
= "/ui/window_height"
37 OPTION_MAIN_PANE_POS
= "/ui/main_pane_position"
38 OPTION_SUB_PANE_POS
= "/ui/sub_pane_position"
39 OPTION_WINDOW_MAX
= "/ui/window_maximized"
40 OPTION_MAGNIFICATION
= "/ui/text_magnification"
41 OPTION_OFFLINE
= "/general/offline"
42 OPTION_POLL_FREQUENCY
= "/general/poll_frequency"
43 OPTION_FEED_ID_SEQ
= "feed_id_seq"
44 OPTION_FEEDS
= "feeds"
45 OPTION_CATEGORIES
= "categories"
47 def ensure_directory(strawdir
):
48 if os
.path
.exists(strawdir
):
49 if not os
.path
.isdir(strawdir
):
56 # Config persistence classes
57 class ConfigPicklePersistence
:
58 def __init__(self
, filename
):
59 self
._config
_file
= filename
62 def save_option(self
, option
, value
):
63 if self
._dict
is None:
64 self
._initialize
_dict
()
65 temp_config_file
= self
._config
_file
+ ".working"
66 pickle_file
= open(temp_config_file
, "w")
67 self
._dict
[option
] = value
68 cPickle
.dump(self
._dict
, pickle_file
, True)
70 os
.rename(temp_config_file
, self
._config
_file
)
73 def load_option(self
, option
):
74 if self
._dict
is None:
75 self
._initialize
_dict
()
76 return self
._dict
.get(option
, None)
78 def _initialize_dict(self
):
79 if os
.path
.exists(self
._config
_file
):
80 pickle_file
= open(self
._config
_file
, "r")
81 self
._dict
= cPickle
.load(pickle_file
)
86 class ConfigGConfPersistence
:
87 SAVERS
= {OPTION_LAST_POLL
: 'int',
88 OPTION_ITEMS_STORED
: 'int',
89 OPTION_ITEM_ORDER
: 'bool',
90 OPTION_BROWSER_CMD
: 'string',
91 OPTION_WINDOW_SIZE_W
: 'int',
92 OPTION_WINDOW_SIZE_H
: 'int',
93 OPTION_MAIN_PANE_POS
: 'int',
94 OPTION_SUB_PANE_POS
: 'int',
95 OPTION_OFFLINE
: 'bool',
96 OPTION_WINDOW_MAX
: 'bool',
97 OPTION_MAGNIFICATION
: 'float',
98 OPTION_POLL_FREQUENCY
: 'int'}
100 def __init__(self
, client
):
103 def save_option(self
, option
, value
):
104 getattr(self
.client
, 'set_' + self
.SAVERS
[option
])(
105 GCONF_STRAW_ROOT
+ option
, value
)
107 def load_option(self
, option
):
108 return getattr(self
.client
, 'get_' + self
.SAVERS
[option
])(
109 GCONF_STRAW_ROOT
+ option
)
111 class ConfigPersistence
:
112 def __init__(self
, *backends
):
113 self
.backends
= backends
115 def save_option(self
, option
, value
):
116 for b
in self
.backends
:
118 b
[0].save_option(option
, value
)
120 def load_option(self
, option
):
121 for b
in self
.backends
:
123 return b
[0].load_option(option
)
125 class ProxyConfig(object):
131 self
._username
= None
132 self
._password
= None
136 doc
= "this will be true when one of the subclasses is used"
139 def fset(self
, active
):
140 self
._active
= active
141 return property(**locals())
145 doc
= "the host name where the proxy server lives"
148 def fset(self
, host
):
150 return property(**locals())
154 doc
= "the port of the proxy server to connect to"
157 def fset(self
, port
):
159 return property(**locals())
163 doc
= "true if we need to authenticate to the proxy server"
166 def fset(self
, isauth
):
168 return property(**locals())
172 doc
= "the username to use when authenticating to the proxy"
174 return self
._username
175 def fset(self
, username
):
176 self
._username
= username
177 return property(**locals())
181 doc
= "the password to use when authenticating to the proxy"
183 return self
._password
184 def fset(self
, password
):
185 self
._password
= password
186 return property(**locals())
189 class GconfProxyConfig(ProxyConfig
):
190 """Encapsulate proxy use and location information (host, port, ip),
191 gconf reading and name lookup logic"""
193 GCONF_HTTP_PROXY
= "/system/http_proxy"
194 GCONF_HTTP_PROXY_USE
= GCONF_HTTP_PROXY
+ "/use_http_proxy"
195 GCONF_HTTP_PROXY_HOST
= GCONF_HTTP_PROXY
+ "/host"
196 GCONF_HTTP_PROXY_PORT
= GCONF_HTTP_PROXY
+ "/port"
197 GCONF_HTTP_PROXY_AUTHENTICATION
= GCONF_HTTP_PROXY
+ "/use_authentication"
198 GCONF_HTTP_PROXY_USER
= GCONF_HTTP_PROXY
+ "/authentication_user"
199 GCONF_HTTP_PROXY_PASSWORD
= GCONF_HTTP_PROXY
+ "/authentication_password"
202 ProxyConfig
.__init
__(self
)
203 client
= gconf
.client_get_default()
204 self
.active
= client
.dir_exists(self
.GCONF_HTTP_PROXY
)
208 client
.add_dir(self
.GCONF_HTTP_PROXY
, gconf
.CLIENT_PRELOAD_RECURSIVE
)
209 client
.notify_add(self
.GCONF_HTTP_PROXY_USE
, self
.proxy_mode_changed
)
210 client
.notify_add(self
.GCONF_HTTP_PROXY_HOST
, self
.proxy_host_changed
)
211 client
.notify_add(self
.GCONF_HTTP_PROXY_PORT
, self
.proxy_port_changed
)
212 client
.notify_add(self
.GCONF_HTTP_PROXY_AUTHENTICATION
, self
.proxy_auth_changed
)
213 client
.notify_add(self
.GCONF_HTTP_PROXY_USER
, self
.proxy_auth_username_changed
)
214 client
.notify_add(self
.GCONF_HTTP_PROXY_PASSWORD
, self
.proxy_auth_password_changed
)
216 self
.use
= client
.get_bool(self
.GCONF_HTTP_PROXY_USE
)
217 self
.host
= client
.get_string(self
.GCONF_HTTP_PROXY_HOST
)
218 self
.port
= client
.get_int(self
.GCONF_HTTP_PROXY_PORT
)
219 self
.auth
= client
.get_bool(self
.GCONF_HTTP_PROXY_AUTHENTICATION
)
221 self
.username
= client
.get_string(self
.GCONF_HTTP_PROXY_USER
)
222 self
.password
= client
.get_string(self
.GCONF_HTTP_PROXY_PASSWORD
)
224 # here be gconf logic
225 def proxy_mode_changed(self
, client
, notify_id
, entry
, *args
):
226 self
.use
= entry
.value
.get_bool()
228 def proxy_host_changed(self
, client
, notify_id
, entry
, *args
):
229 value
= entry
.value
.get_string()
235 def proxy_port_changed(self
, client
, notify_id
, entry
, *args
):
236 value
= entry
.value
.get_int()
242 def proxy_auth_changed(self
, client
, notify_id
, entry
, *args
):
243 value
= entry
.value
.get_bool()
247 def proxy_auth_username_changed(self
, client
, notify_id
, entry
, *args
):
248 value
= entry
.value
.get_string()
249 self
.username
= value
251 def proxy_auth_password_changed(self
, client
, notify_id
, entry
, *args
):
252 value
= entry
.value
.get_string()
253 self
.password
= value
255 class EnvironmentProxyConfig(ProxyConfig
):
256 """Encapsulate proxy use and location information, environment reading"""
259 ProxyConfig
.__init
__(self
)
264 proxies
= urllib
.getproxies()
265 for key
, value
in proxies
.iteritems():
266 proxy
= proxies
.get(key
)
267 user
, authority
= urllib
.splituser(proxy
)
269 self
.host
, self
.port
= urllib
.splitport(authority
)
272 self
.username
, self
.password
= urllib
.splitpasswd(user
)
276 class Config(object, Event
.SignalEmitter
):
277 _straw_dir
= os
.path
.join(os
.getenv("HOME"), ".straw")
278 _straw_config_file
= os
.path
.join(_straw_dir
, "config")
280 def __init__(self
, persistence
):
281 Event
.SignalEmitter
.__init
__(self
)
282 self
.persistence
= persistence
283 self
.initialize_slots(Event
.ItemOrderChangedSignal
,
284 Event
.OfflineModeChangedSignal
,
285 Event
.PollFrequencyChangedSignal
,
286 Event
.NumberOfItemsStoredChangedSignal
)
287 self
._feed
_id
_seq
= 0
288 self
._poll
_freq
= 1800
290 self
._browser
_cmd
= ''
291 self
._items
_stored
= 30
292 self
._item
_order
= False
293 self
._main
_window
_size
= (640,480)
294 self
._main
_pane
_position
= 100
295 self
._sub
_pane
_position
= 100
296 self
.first_time
= None
298 self
._window
_maximized
= False
299 self
._reload
_css
= False
302 def initialize_proxy(self
):
303 # GConfProxyConfig has precedence over EnvironmentProxyConfig
304 pcchoices
= (GconfProxyConfig
, EnvironmentProxyConfig
)
307 if self
._proxy
.active
:
310 def initialize_options(self
):
311 # Call this after calling Config's constructor
312 self
.first_time
= ensure_directory(self
._straw
_dir
)
314 if os
.path
.exists(self
.straw_config_file
):
315 self
._feed
_id
_seq
= self
.persistence
.load_option(OPTION_FEED_ID_SEQ
)
317 self
._poll
_freq
= self
.persistence
.load_option(OPTION_POLL_FREQUENCY
)
318 self
._last
_poll
= self
.persistence
.load_option(OPTION_LAST_POLL
)
319 self
._items
_stored
= self
.persistence
.load_option(OPTION_ITEMS_STORED
)
320 self
._browser
_cmd
= self
.persistence
.load_option(OPTION_BROWSER_CMD
)
321 self
._item
_order
= self
.persistence
.load_option(OPTION_ITEM_ORDER
)
323 width
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_W
)
324 height
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_H
)
329 self
._main
_window
_size
= (width
, height
)
331 self
._main
_pane
_position
= self
.persistence
.load_option(
332 OPTION_MAIN_PANE_POS
)
333 self
._sub
_pane
_position
= self
.persistence
.load_option(
335 self
._window
_maximized
= self
.persistence
.load_option(OPTION_WINDOW_MAX
)
336 self
._text
_magnification
= self
.persistence
.load_option(OPTION_MAGNIFICATION
)
337 self
._offline
= self
.persistence
.load_option(OPTION_OFFLINE
)
341 return self
._straw
_dir
344 def straw_config_file(self
):
345 return self
._straw
_config
_file
353 doc
= "Marshalled feed data"
355 return self
.persistence
.load_option(OPTION_FEEDS
)
356 def fset(self
, feeddata
):
357 self
.persistence
.save_option(
358 OPTION_FEEDS
, feeddata
)
359 return property(**locals())
365 return self
.persistence
.load_option(OPTION_CATEGORIES
)
366 def fset(self
, categorydata
):
367 self
.persistence
.save_option(
368 OPTION_CATEGORIES
, categorydata
)
369 return property(**locals())
372 def poll_frequency():
373 doc
= "Polling frequency"
375 return self
._poll
_freq
376 def fset(self
, poll_frequency
):
377 if self
._poll
_freq
!= poll_frequency
:
378 self
._poll
_freq
= poll_frequency
379 self
.persistence
.save_option(OPTION_POLL_FREQUENCY
, poll_frequency
)
380 self
.emit_signal(Event
.PollFrequencyChangedSignal(self
, poll_frequency
))
381 return property(**locals())
387 return self
._last
_poll
388 def fset(self
, last_poll
):
389 if self
._last
_poll
!= last_poll
:
390 self
._last
_poll
= last_poll
391 self
.persistence
.save_option(OPTION_LAST_POLL
, last_poll
)
392 return property(**locals())
396 doc
= "The browser to use"
398 return self
._browser
_cmd
399 def fset(self
, browser_cmd
):
400 if self
._browser
_cmd
!= browser_cmd
:
401 self
._browser
_cmd
= browser_cmd
402 self
.persistence
.save_option(OPTION_BROWSER_CMD
, browser_cmd
)
403 return property(**locals())
406 def number_of_items_stored():
407 doc
= "Number of items to store per feed"
409 return self
._items
_stored
410 def fset(self
, num
=30):
411 if self
._items
_stored
!= num
:
412 self
._items
_stored
= num
413 self
.persistence
.save_option(OPTION_ITEMS_STORED
, num
)
414 self
.emit_signal(Event
.NumberOfItemsStoredChangedSignal(self
))
415 return property(**locals())
419 doc
= "Ordering of Items"
421 return self
._item
_order
422 def fset(self
, order
):
423 if self
._item
_order
!= order
:
424 self
._item
_order
= order
425 self
.persistence
.save_option(OPTION_ITEM_ORDER
, order
)
426 self
.emit_signal(Event
.ItemOrderChangedSignal(self
))
427 return property(**locals())
433 return self
._feed
_id
_seq
435 self
._feed
_id
_seq
= id
436 self
.persistence
.save_option(OPTION_FEED_ID_SEQ
, id)
437 return property(**locals())
439 def next_feed_id_seq(self
):
440 self
.feed_id_seq
+= 1
441 return self
._feed
_id
_seq
444 def main_window_size():
447 return self
._main
_window
_size
448 def fset(self
, size
):
449 if self
._main
_window
_size
!= size
:
450 self
._main
_window
_size
= size
451 self
.persistence
.save_option(OPTION_WINDOW_SIZE_W
, size
[0])
452 self
.persistence
.save_option(OPTION_WINDOW_SIZE_H
, size
[1])
453 return property(**locals())
460 def fset(self
, mode
):
461 if self
._offline
!= mode
:
463 self
.persistence
.save_option(OPTION_OFFLINE
, mode
)
464 self
.emit_signal(Event
.OfflineModeChangedSignal(self
))
465 return property(**locals())
468 def window_maximized():
471 return self
._window
_maximized
472 def fset(self
, state
):
473 if self
._window
_maximized
is not state
:
474 self
._window
_maximized
= state
475 self
.persistence
.save_option(OPTION_WINDOW_MAX
, state
)
476 return property(**locals())
479 def main_pane_position():
482 return self
._main
_pane
_position
483 def fset(self
, position
):
484 if self
._main
_pane
_position
!= position
:
485 self
._main
_pane
_position
= position
486 self
.persistence
.save_option(OPTION_MAIN_PANE_POS
, position
)
487 return property(**locals())
490 def sub_pane_position():
493 return self
._sub
_pane
_position
494 def fset(self
, position
):
495 if self
._sub
_pane
_position
!= position
:
496 self
._sub
_pane
_position
= position
497 self
.persistence
.save_option(OPTION_SUB_PANE_POS
, position
)
498 return property(**locals())
501 def text_magnification():
502 doc
= "sets the amount of magnification in the item view"
504 return self
._text
_magnification
506 if self
._text
_magnification
is not tm
:
507 self
._text
_magnification
= tm
508 self
.persistence
.save_option(OPTION_MAGNIFICATION
, tm
)
509 return property(**locals())
515 return self
._reload
_css
516 def fset(self
, reload_css
):
517 self
._reload
_css
= reload_css
518 return property(**locals())
520 def convert_if_necessary(config
):
521 if not os
.path
.exists(config
.straw_config_file
):
525 f
= open(config
.straw_config_file
, "r")
528 feeds
= cf
.get('feeds', None)
529 if feeds
and feeds
[0].get('_n_items_unread', None) is None:
531 def _convert_feeds(feeds
, parent
):
533 itemstore
= ItemStore
.get_instance(config
.straw_dir
)
535 if isinstance(feed
, list):
536 _convert_feeds(feed
[1:], parent
)
538 stored
= feed
.get('_items_stored', -1)
542 cutoff
= config
.number_of_items_stored
543 feed
['_n_items_unread'] = itemstore
.get_number_of_unread(feed
['_id'], cutoff
)
546 config
.feeds
= _convert_feeds(feeds
, None)
548 if cf
.has_key('poll_frequency'):
549 config
.poll_frequency
= cf
.get('poll_frequency')
550 config
.number_of_items_stored
= cf
.get('number_of_items_stored')
551 config
.item_order
= cf
.get('item_order')
552 config
.main_window_size
= cf
.get('main_window_size')
553 config
.main_pane_position
= cf
.get('main_pane_position')
554 config
.sub_pane_position
= cf
.get('sub_pane_position')
555 config
.offline
= cf
.get('offline')
557 del cf
['poll_frequency']
558 del cf
['number_of_items_stored']
560 del cf
['main_window_size']
561 del cf
['main_pane_position']
562 del cf
['sub_pane_position']
566 f
= open(config
.straw_config_file
, "w")
569 cPickle
.dump(cf
, f
, True)
574 def create_gconf_persistence():
575 client
= gconf
.client_get_default()
576 client
.add_dir(GCONF_STRAW_ROOT
, gconf
.CLIENT_PRELOAD_ONELEVEL
)
577 return ConfigGConfPersistence(client
)
579 def create_pickle_persistence():
580 return ConfigPicklePersistence(Config
._straw
_config
_file
)
582 def create_instance():
583 # Add your GConf key here as well!
584 cp
= ConfigPersistence(
585 (create_gconf_persistence(),
586 (OPTION_LAST_POLL
, OPTION_ITEMS_STORED
, OPTION_ITEM_ORDER
, OPTION_BROWSER_CMD
,
587 OPTION_WINDOW_SIZE_W
, OPTION_WINDOW_SIZE_H
,
588 OPTION_MAIN_PANE_POS
, OPTION_SUB_PANE_POS
, OPTION_OFFLINE
,
589 OPTION_MAGNIFICATION
,
590 OPTION_POLL_FREQUENCY
, OPTION_WINDOW_MAX
)),
591 (create_pickle_persistence(),
592 (OPTION_FEED_ID_SEQ
, OPTION_FEEDS
, OPTION_CATEGORIES
)))
594 config
.initialize_proxy()
595 config
.initialize_options()
596 convert_if_necessary(config
)
599 config_instance
= None
601 global config_instance
602 if config_instance
is None:
603 config_instance
= create_instance()
604 return config_instance