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
22 from urlparse
import urlparse
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
)
127 class BaseProxyConfig(object):
129 self
._config
_working
= False
132 def config_working(self
):
133 return self
._config
_working
140 def fset(self
, host
):
143 return property(**locals())
151 assert self
._ip
is None, "Overwriting proxy IP not allowed!"
155 return property(**locals())
158 def is_waiting(self
):
159 return self
.use
and not self
._ip
161 def _ip_lookup_callback(self
, host
, ip
, data
):
164 class GconfProxyConfig(BaseProxyConfig
):
165 """Encapsulate proxy use and location information (host, port, ip),
166 gconf reading and name lookup logic"""
168 GCONF_PROXY_ROOT
= "/system/proxy"
169 GCONF_PROXY_MODE
= GCONF_PROXY_ROOT
+ "/mode"
170 GCONF_HTTP_PROXY_ROOT
= "/system/http_proxy"
171 GCONF_HTTP_PROXY_HOST
= GCONF_HTTP_PROXY_ROOT
+ "/host"
172 GCONF_HTTP_PROXY_PORT
= GCONF_HTTP_PROXY_ROOT
+ "/port"
173 GCONF_HTTP_PROXY_USE_AUTHENTICATION
= GCONF_HTTP_PROXY_ROOT
+ "/use_authentication"
174 GCONF_HTTP_PROXY_USER
= GCONF_HTTP_PROXY_ROOT
+ "/authentication_user"
175 GCONF_HTTP_PROXY_PASSWORD
= GCONF_HTTP_PROXY_ROOT
+ "/authentication_password"
178 BaseProxyConfig
.__init
__(self
)
179 client
= gconf
.client_get_default()
180 self
._config
_working
= client
.dir_exists(self
.GCONF_PROXY_ROOT
) and client
.dir_exists(self
.GCONF_HTTP_PROXY_ROOT
)
181 if not self
._config
_working
:
183 client
.add_dir(self
.GCONF_PROXY_ROOT
, gconf
.CLIENT_PRELOAD_RECURSIVE
)
184 client
.add_dir(self
.GCONF_HTTP_PROXY_ROOT
, gconf
.CLIENT_PRELOAD_RECURSIVE
)
185 client
.notify_add(self
.GCONF_PROXY_MODE
, self
.proxy_mode_changed
)
186 client
.notify_add(self
.GCONF_HTTP_PROXY_HOST
, self
.proxy_host_changed
)
187 client
.notify_add(self
.GCONF_HTTP_PROXY_PORT
, self
.proxy_port_changed
)
188 client
.notify_add(self
.GCONF_HTTP_PROXY_USE_AUTHENTICATION
,
189 self
.proxy_auth_changed
)
190 client
.notify_add(self
.GCONF_HTTP_PROXY_USER
, self
.proxy_auth_changed
)
191 client
.notify_add(self
.GCONF_HTTP_PROXY_PASSWORD
, self
.proxy_auth_changed
)
194 pm
= client
.get_string(self
.GCONF_PROXY_MODE
)
195 self
.use
= pm
is not None and pm
!= "none"
196 self
.host
= client
.get_string(self
.GCONF_HTTP_PROXY_HOST
)
197 self
.port
= client
.get_int(self
.GCONF_HTTP_PROXY_PORT
)
198 self
.use_authentication
= client
.get_bool(
199 self
.GCONF_HTTP_PROXY_USE_AUTHENTICATION
)
200 self
.user
= client
.get_string(self
.GCONF_HTTP_PROXY_USER
)
201 self
.password
= client
.get_string(self
.GCONF_HTTP_PROXY_PASSWORD
)
203 # here be gconf logic
204 def proxy_mode_changed(self
, client
, notify_id
, entry
, *args
):
205 value
= entry
.value
.get_string()
206 self
.use
= value
is not None and value
!= "none"
208 def proxy_host_changed(self
, client
, notify_id
, entry
, *args
):
209 value
= entry
.value
.get_string()
210 if value
is not None and len(value
):
215 def proxy_port_changed(self
, client
, notify_id
, entry
, *args
):
216 value
= entry
.value
.get_int()
217 if value
is not None and value
> 0:
222 def proxy_auth_changed(self
, client
, notify_id
, entry
, *args
):
224 if entry
.key
== self
.GCONF_HTTP_PROXY_USE_AUTHENTICATION
:
226 self
.use_authentication
= 0
228 self
.use_authentication
= 1
229 elif entry
.key
== self
.GCONF_HTTP_PROXY_USER
:
231 elif entry
.key
== self
.GCONF_HTTP_PROXY_PASSWORD
:
232 self
.password
= value
234 class EnvironmentProxyConfig(BaseProxyConfig
):
235 """Encapsulate proxy use and location information, environment reading"""
238 BaseProxyConfig
.__init
__(self
)
240 result
= self
._read
_env
()
243 self
._config
_working
= result
244 self
.use
= self
._config
_working
247 proxy
= os
.getenv("http_proxy")
250 pparts
= urlparse(proxy
)
253 if proto
!= "http" or not len(host
):
255 hparts
= host
.split('@')
256 auth
= self
.user
= self
.password
= self
.use_authentication
= None
262 aparts
= auth
.split(':')
265 self
.user
, self
.password
= aparts
266 self
.use_authentication
= 1
269 hparts
= host
.split(':')
273 self
.host
= hparts
[0]
274 self
.port
= int(hparts
[1])
277 class NullProxyConfig(BaseProxyConfig
):
279 BaseProxyConfig
.__init
__(self
)
281 self
._config
_working
= 0
287 self
.use_authentication
= None
291 class Config(object, Event
.SignalEmitter
):
292 _straw_dir
= os
.path
.join(os
.getenv("HOME"), ".straw")
293 _straw_config_file
= os
.path
.join(_straw_dir
, "config")
295 def __init__(self
, persistence
):
296 Event
.SignalEmitter
.__init
__(self
)
297 self
.persistence
= persistence
298 self
.initialize_slots(Event
.ItemOrderChangedSignal
,
299 Event
.OfflineModeChangedSignal
,
300 Event
.PollFrequencyChangedSignal
,
301 Event
.NumberOfItemsStoredChangedSignal
)
302 self
._feed
_id
_seq
= 0
303 self
._poll
_freq
= 1800
305 self
._browser
_cmd
= ''
306 self
._items
_stored
= 30
307 self
._item
_order
= False
308 self
._main
_window
_size
= (640,480)
309 self
._main
_pane
_position
= 100
310 self
._sub
_pane
_position
= 100
311 self
.first_time
= None
313 self
._window
_maximized
= False
314 self
._reload
_css
= False
316 def initialize_proxy(self
):
317 pcchoices
= (GconfProxyConfig
, EnvironmentProxyConfig
, NullProxyConfig
)
319 self
._proxy
_config
= p()
320 if self
._proxy
_config
.config_working
:
323 def initialize_options(self
):
324 # Call this after calling Config's constructor
325 self
.first_time
= ensure_directory(self
._straw
_dir
)
327 if os
.path
.exists(self
.straw_config_file
):
328 self
._feed
_id
_seq
= self
.persistence
.load_option(OPTION_FEED_ID_SEQ
)
330 self
._poll
_freq
= self
.persistence
.load_option(OPTION_POLL_FREQUENCY
)
331 self
._last
_poll
= self
.persistence
.load_option(OPTION_LAST_POLL
)
332 self
._items
_stored
= self
.persistence
.load_option(OPTION_ITEMS_STORED
)
333 self
._browser
_cmd
= self
.persistence
.load_option(OPTION_BROWSER_CMD
)
334 self
._item
_order
= self
.persistence
.load_option(OPTION_ITEM_ORDER
)
336 width
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_W
)
337 height
= self
.persistence
.load_option(OPTION_WINDOW_SIZE_H
)
342 self
._main
_window
_size
= (width
, height
)
344 self
._main
_pane
_position
= self
.persistence
.load_option(
345 OPTION_MAIN_PANE_POS
)
346 self
._sub
_pane
_position
= self
.persistence
.load_option(
348 self
._window
_maximized
= self
.persistence
.load_option(OPTION_WINDOW_MAX
)
349 self
._text
_magnification
= self
.persistence
.load_option(OPTION_MAGNIFICATION
)
350 self
._offline
= self
.persistence
.load_option(OPTION_OFFLINE
)
354 return self
._straw
_dir
357 def straw_config_file(self
):
358 return self
._straw
_config
_file
361 def proxy_config(self
):
362 return self
._proxy
_config
366 doc
= "Marshalled feed data"
368 return self
.persistence
.load_option(OPTION_FEEDS
)
369 def fset(self
, feeddata
):
370 self
.persistence
.save_option(
371 OPTION_FEEDS
, feeddata
)
372 return property(**locals())
378 return self
.persistence
.load_option(OPTION_CATEGORIES
)
379 def fset(self
, categorydata
):
380 self
.persistence
.save_option(
381 OPTION_CATEGORIES
, categorydata
)
382 return property(**locals())
385 def poll_frequency():
386 doc
= "Polling frequency"
388 return self
._poll
_freq
389 def fset(self
, poll_frequency
):
390 if self
._poll
_freq
!= poll_frequency
:
391 self
._poll
_freq
= poll_frequency
392 self
.persistence
.save_option(OPTION_POLL_FREQUENCY
, poll_frequency
)
393 self
.emit_signal(Event
.PollFrequencyChangedSignal(self
, poll_frequency
))
394 return property(**locals())
400 return self
._last
_poll
401 def fset(self
, last_poll
):
402 if self
._last
_poll
!= last_poll
:
403 self
._last
_poll
= last_poll
404 self
.persistence
.save_option(OPTION_LAST_POLL
, last_poll
)
405 return property(**locals())
409 doc
= "The browser to use"
411 return self
._browser
_cmd
412 def fset(self
, browser_cmd
):
413 if self
._browser
_cmd
!= browser_cmd
:
414 self
._browser
_cmd
= browser_cmd
415 self
.persistence
.save_option(OPTION_BROWSER_CMD
, browser_cmd
)
416 return property(**locals())
419 def number_of_items_stored():
420 doc
= "Number of items to store per feed"
422 return self
._items
_stored
423 def fset(self
, num
=30):
424 if self
._items
_stored
!= num
:
425 self
._items
_stored
= num
426 self
.persistence
.save_option(OPTION_ITEMS_STORED
, num
)
427 self
.emit_signal(Event
.NumberOfItemsStoredChangedSignal(self
))
428 return property(**locals())
432 doc
= "Ordering of Items"
434 return self
._item
_order
435 def fset(self
, order
):
436 if self
._item
_order
!= order
:
437 self
._item
_order
= order
438 self
.persistence
.save_option(OPTION_ITEM_ORDER
, order
)
439 self
.emit_signal(Event
.ItemOrderChangedSignal(self
))
440 return property(**locals())
446 return self
._feed
_id
_seq
448 self
._feed
_id
_seq
= id
449 self
.persistence
.save_option(OPTION_FEED_ID_SEQ
, id)
450 return property(**locals())
452 def next_feed_id_seq(self
):
453 self
.feed_id_seq
+= 1
454 return self
._feed
_id
_seq
457 def main_window_size():
460 return self
._main
_window
_size
461 def fset(self
, size
):
462 if self
._main
_window
_size
!= size
:
463 self
._main
_window
_size
= size
464 self
.persistence
.save_option(OPTION_WINDOW_SIZE_W
, size
[0])
465 self
.persistence
.save_option(OPTION_WINDOW_SIZE_H
, size
[1])
466 return property(**locals())
473 def fset(self
, mode
):
474 if self
._offline
!= mode
:
476 self
.persistence
.save_option(OPTION_OFFLINE
, mode
)
477 self
.emit_signal(Event
.OfflineModeChangedSignal(self
))
478 return property(**locals())
481 def window_maximized():
484 return self
._window
_maximized
485 def fset(self
, state
):
486 if self
._window
_maximized
is not state
:
487 self
._window
_maximized
= state
488 self
.persistence
.save_option(OPTION_WINDOW_MAX
, state
)
489 return property(**locals())
492 def main_pane_position():
495 return self
._main
_pane
_position
496 def fset(self
, position
):
497 if self
._main
_pane
_position
!= position
:
498 self
._main
_pane
_position
= position
499 self
.persistence
.save_option(OPTION_MAIN_PANE_POS
, position
)
500 return property(**locals())
503 def sub_pane_position():
506 return self
._sub
_pane
_position
507 def fset(self
, position
):
508 if self
._sub
_pane
_position
!= position
:
509 self
._sub
_pane
_position
= position
510 self
.persistence
.save_option(OPTION_SUB_PANE_POS
, position
)
511 return property(**locals())
514 def text_magnification():
515 doc
= "sets the amount of magnification in the item view"
517 return self
._text
_magnification
519 if self
._text
_magnification
is not tm
:
520 self
._text
_magnification
= tm
521 self
.persistence
.save_option(OPTION_MAGNIFICATION
, tm
)
522 return property(**locals())
528 return self
._reload
_css
529 def fset(self
, reload_css
):
530 self
._reload
_css
= reload_css
531 return property(**locals())
533 def convert_if_necessary(config
):
534 if not os
.path
.exists(config
.straw_config_file
):
538 f
= open(config
.straw_config_file
, "r")
541 feeds
= cf
.get('feeds', None)
542 if feeds
and feeds
[0].get('_n_items_unread', None) is None:
544 def _convert_feeds(feeds
, parent
):
546 itemstore
= ItemStore
.get_instance(config
.straw_dir
)
548 if isinstance(feed
, list):
549 _convert_feeds(feed
[1:], parent
)
551 stored
= feed
.get('_items_stored', -1)
555 cutoff
= config
.number_of_items_stored
556 feed
['_n_items_unread'] = itemstore
.get_number_of_unread(feed
['_id'], cutoff
)
559 config
.feeds
= _convert_feeds(feeds
, None)
561 if cf
.has_key('poll_frequency'):
562 config
.poll_frequency
= cf
.get('poll_frequency')
563 config
.number_of_items_stored
= cf
.get('number_of_items_stored')
564 config
.item_order
= cf
.get('item_order')
565 config
.main_window_size
= cf
.get('main_window_size')
566 config
.main_pane_position
= cf
.get('main_pane_position')
567 config
.sub_pane_position
= cf
.get('sub_pane_position')
568 config
.offline
= cf
.get('offline')
570 del cf
['poll_frequency']
571 del cf
['number_of_items_stored']
573 del cf
['main_window_size']
574 del cf
['main_pane_position']
575 del cf
['sub_pane_position']
579 f
= open(config
.straw_config_file
, "w")
582 cPickle
.dump(cf
, f
, True)
587 def create_gconf_persistence():
588 client
= gconf
.client_get_default()
589 client
.add_dir(GCONF_STRAW_ROOT
, gconf
.CLIENT_PRELOAD_ONELEVEL
)
590 return ConfigGConfPersistence(client
)
592 def create_pickle_persistence():
593 return ConfigPicklePersistence(Config
._straw
_config
_file
)
595 def create_instance():
596 # Add your GConf key here as well!
597 cp
= ConfigPersistence(
598 (create_gconf_persistence(),
599 (OPTION_LAST_POLL
, OPTION_ITEMS_STORED
, OPTION_ITEM_ORDER
, OPTION_BROWSER_CMD
,
600 OPTION_WINDOW_SIZE_W
, OPTION_WINDOW_SIZE_H
,
601 OPTION_MAIN_PANE_POS
, OPTION_SUB_PANE_POS
, OPTION_OFFLINE
,
602 OPTION_MAGNIFICATION
,
603 OPTION_POLL_FREQUENCY
, OPTION_WINDOW_MAX
)),
604 (create_pickle_persistence(),
605 (OPTION_FEED_ID_SEQ
, OPTION_FEEDS
, OPTION_CATEGORIES
)))
607 config
.initialize_proxy()
608 config
.initialize_options()
609 convert_if_necessary(config
)
612 config_instance
= None
614 global config_instance
615 if config_instance
is None:
616 config_instance
= create_instance()
617 return config_instance