class MyspaceAccount(social.network, oauth_util.OAuthAccountBase, jsonrpc.RPCClient): url_base = 'http://www.myspace.com/index.cfm' AuthClass = oauth_util.InternalBrowserAuthenticator # UserBrowserAuthenticator #AuthClass = oauth_util.InternalBrowserAuthenticatorOpenID events = events.EventMixin.events | set(( # stuff )) service = protocol = 'myspace' indicators_keys = [ 'blogcommenturl', 'blogsubscriptionposturl', 'picturecommenturl', 'eventinvitationurl', 'commenturl', 'phototagapprovalurl', 'friendsrequesturl', 'videocommenturl', 'groupnotificationurl', 'recentlyaddedfriendurl', 'birthdayurl', #'countpendingim', ] feed_keys = [ 'statuses', 'friends', 'posts', 'groups', 'photos', 'music', 'videos', 'events', 'applications', ] header_fuseactions = dict(( ('Home', 'user'), #('Profile', 'user.viewprofile'), # supposed to take you to myspace.com/yourname but instead just goes to myspace.com (??) ('Inbox', 'mail.inboxV2'), ('Friends', 'user.viewfriends'), ('Blog', 'blog.ListAll'), ('Post Bulletin', 'bulletin'), )) def url_destination(name): def get_url(self): kwargs = DIGSBY_UTM_ARGS.copy() self_userid = getattr(self.connection, 'userid', None) if self_userid is not None: kwargs.update(friendId=self_userid) kwargs.update(fuseaction=self.header_fuseactions[name]) return self.openurl(net.UrlQuery(self.url_base, **kwargs)) return get_url openurl_Home = url_destination("Home") openurl_Profile = url_destination("Profile") openurl_Inbox = url_destination("Inbox") openurl_Friends = url_destination("Friends") openurl_Blog = url_destination("Blog") openurl_Post = url_destination("Post Bulletin") del url_destination def __init__(self, *a, **k): common.Protocol.StateMixin.__init__(self) oauth_util.OAuthAccountBase.__init__(self, **k) self.count = 0 self.connection = None self._dirty = False filters = k.pop('filters', {}) self.filters = dict( indicators=dict( zip( self.indicators_keys, filters.get('indicators', [True] * len(self.indicators_keys)))), feed=dict( zip(self.feed_keys, filters.get('feed', [True] * len(self.feed_keys)))), ) self.header_funcs = ( ('Home', self.openurl_Home), # ('Profile', self.openurl_Profile), ('Inbox', self.openurl_Inbox), ('Friends', self.openurl_Friends), ('Blog', self.openurl_Blog), ('Post Bulletin', self.openurl_Post), ) if 'password' not in k: k['password'] = None social.network.__init__(self, *a, **k) self._remove_password = not self.protocol_info()['needs_password'] if self._remove_password: self.password = None from social.network import SocialFeed self.social_feed = SocialFeed( 'myspace_' + self.username, 'activities', lambda: iter(self.connection.combined_feed()), self.htmlize_activities, self._set_dirty) def htmlize_activities(self, activities, context): t = MyspaceIB(self) return t.get_html(None, set_dirty=False, file='activities.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=dict(activities=activities)) def Connect(self): log.info('Connect called for %r', self) self._update_now() def create_connection(self): if self.connection is not None: raise Exception('Already have a connection') import MyspaceProtocol as MSP self.connection = MSP.MyspaceProtocol(self.username, self.oauth_token, self._decryptedpw, self.api_data, self.filters) self.bind_events() @property def cache_path(self): return os.path.join('myspace-social3', self.username, 'api-results.dat') def _cache_data(self, api_data): self.api_data = api_data api_data = cacheable.cproperty({}, user=True) def on_feed_invalidated(self): ids = [p.id for p in self.connection.combined_feed()] self.social_feed.new_ids(ids) def observe_count(self, callback): self.add_gui_observer(callback, 'count') def unobserve_count(self, callback): self.remove_gui_observer(callback, 'count') def _got_indicators(self, inds): log.info('got indicators: %r', inds) indicators = {} for k in self.indicators_keys: if k in inds and self.filters['indicators'].get(k, False): indicators[k] = inds[k] num_inds = len(indicators) if inds.get('mailurl', None) is not None: num_inds += 1 if getattr(self, '_num_inds', -1) != num_inds: self._num_inds = num_inds self.setnotify('count', num_inds) def get_authenticator(self, url_generator): AuthClass = self._get_auth_class(prefkey='myspace.authenticator') return AuthClass( self.username, url_generator, '/myspace/{username}/oauth'.format(username=self.username), 'MySpace Login - %s' % self.username, "http://www.digsby.com/myspace/", 'serviceicons.myspace') def bind_events(self): oauth_util.OAuthAccountBase.bind_events(self) self.connection.bind('on_indicators', self._got_indicators) def unbind_events(self): conn = oauth_util.OAuthAccountBase.unbind_events(self) if conn is None: return conn.unbind('on_indicators', self._got_indicators) def _on_protocol_connect(self): pass def _connect(self): #assert self.connection is not None self.connection.connect() log.info('Calling connect for connection') def _update_now(self): if self.enabled: self.update_now() else: self.set_offline(self.Reasons.NONE) @common.action( lambda self: common.pref('can_has_social_update', None) or None) def update_now(self): log.info('updating... %r', self) self.start_timer() if self.state == self.Statuses.OFFLINE: self.change_state(self.Statuses.CONNECTING) try: self.create_connection() self._connect() except Exception: import traceback traceback.print_exc() self.Disconnect(self.Reasons.CONN_FAIL) return self._update() def _update_pre(self): if self._has_updated or self._forcing_login: st = self.Statuses.CHECKING else: st = self.Statuses.CONNECTING self.change_state(st) def _reset_connection(self): self._has_updated = False self._on_auth_done() #self.connection.disconnect() def Disconnect(self, reason=None): log.info('Disconnect called') self.pause_timer() self._reset_connection() self.unbind_events() self.connection = None reason = reason or self.Reasons.NONE if self.state != self.Statuses.OFFLINE: self.set_offline(reason) common.UpdateMixin.disconnect(self) disconnect = Disconnect def _update_error(self, e): log.debug("%r got update error: %r", self, e) if hasattr(e, 'read'): log.debug_s('\tbody: %r', e.read()) if isinstance(e, oauth.OAuthError): return self._handle_oauth_error(getattr(e, 'oauth_data', None)) if self.state == self.Statuses.OFFLINE: return if self._has_updated: rsn = self.Reasons.CONN_LOST else: rsn = self.Reasons.CONN_FAIL self.Disconnect(rsn) def _handle_oauth_error(self, details): log.error('oauth error occurred: %r', details) problem = details.get('oauth_problem', None) self.clear_oauth_token() if problem == 'timestamp_refused': self.error_txt = _( "Please set your computer clock to the correct time / timezone." ) self.Disconnect(self.Reasons.BAD_PASSWORD) def update_info(self, **info): filters = info.pop('filters', None) if filters is not None: self.filters.update( dict( indicators=dict( zip( self.indicators_keys, filters.get('indicators', [True] * len(self.indicators_keys)))), feed=dict( zip(self.feed_keys, filters.get('feed', [True] * len(self.feed_keys)))), )) self._set_dirty() if info.get('password') is not None and self._remove_password: info['password'] = None return social.network.update_info(self, **info) def get_options(self): opts = super(MyspaceAccount, self).get_options() opts.update({'informed_ach': True, 'post_ach_all': False}) opts.update( filters=dict(feed=[ bool(self.filters['feed'].get(x, True)) for x in self.feed_keys ], indicators=[ bool(self.filters['indicators'].get(x, True)) for x in self.indicators_keys ])) if opts.get('password') is not None and self._remove_password: opts['password'] = None if 'oauth_token' not in opts: opts['oauth_token'] = self.oauth_token return opts @common.action() def edit_status(self): if common.pref('social.use_global_status', default=False, type=bool): import wx wx.GetApp().SetStatusPrompt([self]) else: from myspacegui.editstatus import get_new_status get_new_status(success=self.set_web_status) DefaultAction = OpenHomeURL = edit_status @callbacks.callsback def SetStatusMessage(self, message, callback=None, **opts): if len(message) == 0: return callback.success() self.connection.set_status_message(message, callback=callback) def _dirty_get(self): return getattr(getattr(self, 'connection', None), '_dirty', True) def _dirty_set(self, val): if self.connection is not None: self.connection._dirty = val _dirty = property(_dirty_get, _dirty_set) @common.action( lambda self: common.pref('can_has_social_update', None) or None) def _set_dirty(self): self._dirty = True ## TODO: Move all this rpc nonsense to a superclass, with bind/unbind/callbacks and some sweet-ass decorator magic. _rpc_handlers = { 'near_bottom': 'more_content', 'post_comment': 'post_comment', 'hook': 'rpc_hook', 'load_comments': 'load_comments', 'initialize_feed': 'initialize_feed', 'next_item': 'next_item', 'do_permissions': 'initiate_login', 'do_like': 'newsfeed_do_like', 'do_dislike': 'newsfeed_do_dislike', } def initiate_login(self, *a, **k): self.connection.userinfo = None oauth_util.OAuthAccountBase.initiate_login(self, *a, **k) def user_dislikes(self, userid, item): return item.user_dislikes(userid) def user_likes(self, userid, item): return item.user_likes(userid) def newsfeed_do_dislike(self, rpc, webview, id, post_id): item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if self.user_dislikes(self.connection.userid, item): log.info("user already dislikes this post") return import myspace.objects as MSO self.post_comment( rpc, webview, id, MSO.MyspaceComment.DISLIKE, post_id, append=False, success=lambda *a, **k: self.dislike_added(webview, id, post_id)) def newsfeed_do_like(self, rpc, webview, id, post_id): item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if self.user_likes(self.connection.userid, item): log.info("user already likes this post") return import myspace.objects as MSO self.post_comment( rpc, webview, id, MSO.MyspaceComment.LIKE, post_id, append=False, success=lambda *a, **k: self.like_added(webview, id, post_id)) def dislike_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, True) hooks.notify('digsby.myspace.dislike_added', post_id) def like_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, False) hooks.notify('digsby.myspace.like_added', post_id) def refresh_likes(self, webview, id, post_id, dis=False): \ log.info("refreshing item: %r", post_id) #regen likes block, regen likes link block, send to callback #regen cached post html item = self.connection.get_post_by_id(post_id) item_html = self.generate_newsfeed_html([item]) self.Dsuccess(webview, id, item_html=item_html) @callbacks.callsback def post_comment(self, rpc, webview, id, comment, post_id, append=True, callback=None): post = self.connection.get_post_by_id(post_id) if append: post._numComments += 1 callback.success += lambda *a: self.append_comments( webview, id, post_id) else: import myspace.objects as MSO post.comments.append( MSO.MyspaceComment.from_json( dict( userid='myspace.com.person.%s' % self.connection.userid, text=comment, commentId=str(random.randint(0, 0x7fffffff)), postedDate_parsed=time.time(), ))) callback.error += lambda *a: self.Dexcept(webview, id, *a) self._post_comment(comment, post_id, callback=callback) @callbacks.callsback def _post_comment(self, comment, post_id, callback=None): self.connection.post_comment(post_id, comment, callback=callback) def generate_newsfeed_html(self, activities, _context_id=None, do_comments=True): t = MyspaceIB(self) activities_html = t.get_html( None, set_dirty=False, file='activities.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=dict(activities=activities)) return activities_html def append_comments(self, webview, id, post_id): t = MyspaceIB(self) context = {} context['item'] = self.connection.get_post_by_id(post_id) comments_html = t.get_html( None, set_dirty=False, file='comment_section.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) bottom_row_html = t.get_html( None, set_dirty=False, file='bottom_row.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) self.Dsuccess(webview, id, comments_html=comments_html, bottom_row_html=bottom_row_html) self.api_data['friend_status'] = self.connection.friend_status self._cache_data(self.api_data) def more_content(self, rpc, webview, id, **params): current_posts, _last_post_id = params.get('current_posts', 0), params.get( 'last_post_id', None) if current_posts < len(self.connection.combined_feed()): activities = self.connection.combined_feed( )[current_posts:current_posts + 1] activities_html = self.generate_newsfeed_html(activities) self.Dsuccess(webview, id, contents=activities_html) else: self.Derror(webview, id) def initialize_feed(self, rpc, webview, id, *extra, **params): self.social_feed.jscall_initialize_feed(webview, id) def next_item(self, rpc, webview, id, *extra, **params): self.social_feed.jscall_next_item(webview, id) def load_comments(self, rpc, webview, id, post_id): self.connection.get_comments_for( post_id, #success = lambda: self.refresh_likes(webview, id, post_id), success=lambda: self.append_comments(webview, id, post_id), error=lambda error_obj=None, **k: self.Dexcept( webview, id, error_obj=error_obj, **k)) #self.more_content(args['current_posts'], args.pop('last_post_id', None), rpc.pop('id')) def user_from_activity(self, act): return self.connection.user_from_activity(act) def get_imageurl_for_user(self, user): if user is None: return None else: return user.get('image', user.get('thumbnailUrl', None)) def user_from_id(self, id): return self.connection.user_from_id(id) def _name_for_user(self, user): name = getattr(user, 'name', None) if name is None: return getattr(user, 'displayName', 'Private User') firstname, lastname = name.get('givenName', None), name.get('familyName', None) if (firstname, lastname) == (None, None): return name if firstname and lastname: return u"%s %s" % (firstname, lastname) return firstname or lastname
class LinkedInAccount(SocialNetwork, oauth_util.OAuthAccountBase, jsonrpc.RPCClient): _rpc_handlers = { "post_comment": "newsfeed_post_comment", "next_item": "newsfeed_next_item", "initialize_feed": "newsfeed_initialize_feed", "near_bottom": "newsfeed_more_content", #'load_comments' : 'newsfeed_load_comments', "do_like": "newsfeed_do_like", "do_dislike": "newsfeed_do_dislike", } updatefreq = 10 * 60 # ten minutes class Statuses(SocialNetwork.Statuses): CHECKING = _("Checking now...") BORDER = "0" service = protocol = "linkedin" AuthClass = oauth_util.InternalBrowserAuthenticator # AuthClass = oauth_util.UserBrowserAuthenticator webroot = "https://www.linkedin.com" def weblink(self, resource=""): return net.httpjoin(self.webroot, resource) @property def header_funcs(self): return ( (_("Home"), self.weblink()), (_("Inbox"), self.weblink("inBox")), (_("Contacts"), self.weblink("connections")), (_("Profile"), self.weblink("myprofile")), ) def _get_inbox_count_str(self): if self.count: return " (%d)" % self.count else: return "" def __init__(self, **options): self.time_offset = None options["password"] = None oauth_util.OAuthAccountBase.__init__(self, **options) self.connection = None self._dirty = False SocialNetwork.__init__(self, **options) self.social_feed = SocialFeed( "linkedin_" + self.username, "linkedin_" + self.username, self.get_newsfeed_iter, self.generate_newsfeed_html, lambda: self.set_infobox_dirty, ) def set_dirty(self): log.info("set dirty") self._dirty = True def _cache_data(self, api_data): self.api_data = api_data api_data = cacheable.cproperty({}, user=True) def get_newsfeed_iter(self): return iter(getattr(getattr(self, "connection", None), "updates", [])) def set_infobox_dirty(self): self._dirty = True self.notify("dirty") @property def cache_path(self): return os.path.join(self.protocol, self.name, "api-results.dat") def get_options(self): opts = {"informed_ach": True, "post_ach_all": False} opts.update(SocialNetwork.get_options(self)) opts.update(oauth_util.OAuthAccountBase.get_options(self)) return opts def Connect(self): self.error_txt = None log.info("Connect called for %r", self) self._update_now() def _update_now(self): if self.enabled: self.update_now() else: self.set_offline(self.Reasons.NONE) def _connect(self): self.connection.connect() def create_connection(self): if self.connection is not None: raise Exception("Already have a connection") import LinkedInProtocol as LIP self.connection = LIP.LinkedInProtocol( self.username, self.oauth_token, self.api_data, self.filters, time_offset=self.time_offset ) self.bind_events() def connect_failed(self, e): self._dirty_error = True self.Disconnect(reason=self.Reasons.CONN_FAIL) def Disconnect(self, reason=None): if reason is None: reason = self.Reasons.NONE self.unbind_events() self.connection = None self.set_offline(reason) common.UpdateMixin.disconnect(self) def handle_rate_limited(self): log.info("rate limited!") self.error_txt = _("API request limit has been reached.") self.handle_update_failed("RATE_LIMIT") def handle_update_failed(self, _reason): reason = getattr(self.Reasons, _reason, self.Reasons.CONN_FAIL) self.Disconnect(reason=reason) def update_item_to_notification(self, item): import weakref import gui.browser.webkit.imageloader as imageloader default_icon = skin.get("BuddiesPanel.BuddyIcons.NoIcon", None) if hasattr(item, "content_body"): body = item.content_body() else: html = self.generate_item_html(item) body = util.strip_html(html).strip() n = util.Storage( acct=weakref.ref(self), icon=imageloader.LazyWebKitImage(item.person.picture_url, default_icon), body=body, title=item.person.name, url=getattr(item, "url", item.person.profile_url), post_id=item.id, ) return n def do_notifications(self, updates): if not updates: return items = [] for item in updates: try: items.append(self.update_item_to_notification(item)) except Exception: traceback.print_exc() common.fire( "linkedin.newsfeed", items=items, popupid="%d.linkedin" % id(self), update="paged", badge=skin.get("serviceicons.linkedin", None), buttons=self.get_popup_buttons, onclick=self.on_popup_click, ) def get_popup_buttons(self, item): self._last_popup_item = item buttons = [] my_item = self.connection.get_post_by_id(item.item.post_id) if my_item is None: return buttons def count_str(count): return (" (%s)" % count) if count and count != 0 else "" if my_item.supports_comments: buttons.append( (_("Comment") + count_str(len(my_item.get_comments())), input_callback(self.on_popup_comment)) ) return buttons def on_popup_comment(self, item, control, text, options): post_id = options["item"].post_id self._post_comment(post_id, text, success=(lambda *a, **k: (self.set_dirty(), control.update_buttons()))) on_popup_comment.takes_popup_control = True def on_popup_click(self, item): import wx url = getattr(item, "url", None) if url: import wx wx.LaunchDefaultBrowser(url) def bind_events(self): conn = oauth_util.OAuthAccountBase.bind_events(self) bind = conn.bind bind("on_rate_limit", self.handle_rate_limited) bind("update_error", self.handle_update_failed) bind("newsfeed_updates", self.do_notifications) def unbind_events(self): conn = oauth_util.OAuthAccountBase.unbind_events(self) if conn is None: return unbind = conn.unbind unbind("on_rate_limit", self.handle_rate_limited) unbind("update_error", self.handle_update_failed) unbind("newsfeed_updates", self.do_notifications) @common.action() def SetStatus(self): if common.pref("social.use_global_status", default=False, type=bool): import wx wx.GetApp().SetStatusPrompt([self]) else: log.error("No alternative to global status dialog for new linked in account!") DefaultAction = OpenHomeURL = SetStatus @callbacks.callsback def _set_status(self, new_message, callback=None, **k): # callback.success += lambda * a: self.update_now() if new_message: callback.success += lambda *a: setattr(self.connection.users[self.connection.userid], "status", new_message) callback.success += lambda *a: self.set_dirty() callback.success += lambda *a: hooks.notify("digsby.linkedin.status_updated", self, new_message, *a) self.connection.set_status(new_message, callback=callback) SetStatusMessage = _set_status def disconnect(self, *a, **k): pass def observe_count(self, callback): return NotImplemented def observe_state(self, callback): return NotImplemented def unobserve_count(self, callback): return NotImplemented def unobserve_state(self, callback): return NotImplemented def OnClickHomeURL(self): return self.weblink() def launchbrowser(self, what): import wx wx.LaunchDefaultBrowser(self.weblink(what)) @common.action() def openurl_Home(self): self.launchbrowser("") @common.action() def openurl_Inbox(self): self.launchbrowser("inBox") @common.action() def openurl_Friends(self): self.launchbrowser("connections") @common.action() def openurl_Profile(self): self.launchbrowser("myprofile") @common.action( lambda self: ((self.state == self.Statuses.ONLINE) and common.pref("can_has_social_update", False)) or None ) def update_now(self): log.info("updating... %r", self) self.start_timer() log.info("current state: %r", self.state) if self.state == self.Statuses.OFFLINE or self.connection is None: self.change_state(self.Statuses.CONNECTING) try: self.create_connection() self._connect() except Exception: traceback.print_exc() self.Disconnect(self.Reasons.CONN_FAIL) return self._update() def request_status(self): self.set_waiting("status") self.connection.request_status() def _on_protocol_connect(self): log.info("connection ready") def handle_connect(self): self.change_state(self.Statuses.AUTHENTICATING) def handle_status(self, status_info=None): log.info("Got status info: %r", status_info) # def update_info(self, **info): # return SocialNetwork.update_info(self, **info) def on_feed_invalidated(self): self.social_feed.new_ids([p.id for p in self.connection.updates]) def _handle_oauth_error(self, details): log.error("oauth error occurred: %r", details) problem = net.WebFormData.parse(details.get("oauth_problem", "")) self.clear_oauth_token() if "timestamp_refused" in problem: self.error_txt = _("Please set your computer clock to the correct time / timezone.") self.Disconnect(self.Reasons.BAD_PASSWORD) def get_authenticator(self, url_generator): AuthClass = self._get_auth_class(prefkey="linkedin.authenticator") return AuthClass( self.username, url_generator, "/linkedin/{username}/oauth".format(username=self.username), "LinkedIn Login - %s" % self.username, "http://www.digsby.com/myspace", "serviceicons.linkedin", ) def _authenticate_post(self): log.info("authenticated successfully!") oauth_util.OAuthAccountBase._authenticate_post(self) self.update_now() @callbacks.callsback def newsfeed_post_comment(self, rpc, webview, id, comment, post_id, append=True, callback=None): if append: callback.success += lambda *a: self.append_comments(webview, id, post_id) callback.error += lambda error_obj=None: self.Dexcept(webview, id, error_obj=error_obj) self._post_comment(post_id, comment, callback=callback) @callbacks.callsback def _post_comment(self, post_id, comment, callback=None): self.connection.get_post_by_id(post_id).comments.append( LIO.LinkedInComment(sequence_number=-1, text=comment, person=self.connection.users[self.connection.userid]) ) self.connection.post_comment(post_id, comment, callback=callback) def append_comments(self, webview, id, post_id): hooks.notify("digsby.linkedin.comment_added", {}) t = LinkedInIB(self) context = {} context["item"] = self.connection.get_post_by_id(post_id) comments_html = t.get_html( None, set_dirty=False, file="comments_list.tenjin", dir=t.get_context()["app"].get_res_dir("base"), context=context, ) comment_link_html = t.get_html( None, set_dirty=False, file="comment_link.tenjin", dir=t.get_context()["app"].get_res_dir("base"), context=context, ) log.debug("comments_html = %r; comment_link_html = %r", comments_html, comment_link_html) self.Dsuccess(webview, id, comments_html=comments_html, comment_link_html=comment_link_html) def generate_newsfeed_html(self, items, _context_id=None, do_comments=True): t = LinkedInIB(self) context = {} context["items"] = items context["conn"] = self.connection context["do_comments"] = do_comments html = t.get_html( None, set_dirty=False, file="items.tenjin", dir=t.get_context()["app"].get_res_dir("base"), context=context ) return html def generate_item_html(self, item): t = LinkedInIB(self) context = {} context["item"] = item context["conn"] = self.connection context["friend"] = item.person html = t.get_html( None, set_dirty=False, file="%s.tenjin" % item.type, dir=t.get_context()["app"].get_res_dir("base"), context=context, ) return html def _update_post(self): super(LinkedInAccount, self)._update_post() def newsfeed_next_item(self, rpc, webview, id, *extra, **params): return self.social_feed.jscall_next_item(webview, id) def more_content(self, rpc, webview, id, **params): current_posts, _last_post_id = params.get("current_posts", 0), params.get("last_post_id", None) t = LinkedInIB(self) if current_posts < len(self.connection.updates): items = self.connection.updates[current_posts : current_posts + 1] items_html = self.generate_newsfeed_html(items) self.Dsuccess(webview, id, contents=items_html) else: self.Derror(webview, id) def newsfeed_initialize_feed(self, rpc, webview, id, *extra, **params): return self.social_feed.jscall_initialize_feed(webview, id) def newsfeed_do_dislike(self, rpc, webview, id, post_id, **kwds): log.info("do dislike: kwds = %r", kwds) item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if item.user_dislikes(self.connection.userid): log.info("user already dislikes this post") return self.newsfeed_post_comment( rpc, webview, id, LIO.LinkedInComment.DISLIKE, post_id, append=False, success=lambda *a, **k: self.dislike_added(webview, id, post_id), ) def dislike_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, True) hooks.notify("digsby.linkedin.dislike_added", post_id) def newsfeed_do_like(self, rpc, webview, id, post_id, **kwds): log.info("do like: kwds = %r", kwds) item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if item.user_likes(self.connection.userid): log.info("user already likes this post") return self.newsfeed_post_comment( rpc, webview, id, LIO.LinkedInComment.LIKE, post_id, append=False, success=lambda *a, **k: self.like_added(webview, id, post_id), ) def like_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, True) hooks.notify("digsby.linkedin.like_added", post_id) def refresh_likes(self, webview, id, post_id, dis=False): log.info("refreshing item: %r", post_id) # regen likes block, regen likes link block, send to callback # regen cached post html item_html = self.generate_newsfeed_html([self.connection.get_post_by_id(post_id)], None) self.Dsuccess(webview, id, item_html=item_html) def _create_activity(self, body): self.connection.create_activity(body=body)
class LinkedInAccount(SocialNetwork, oauth_util.OAuthAccountBase, jsonrpc.RPCClient): _rpc_handlers = { 'post_comment': 'newsfeed_post_comment', 'next_item': 'newsfeed_next_item', 'initialize_feed': 'newsfeed_initialize_feed', 'near_bottom': 'newsfeed_more_content', #'load_comments' : 'newsfeed_load_comments', 'do_like': 'newsfeed_do_like', 'do_dislike': 'newsfeed_do_dislike', } updatefreq = 10 * 60 # ten minutes class Statuses(SocialNetwork.Statuses): CHECKING = _('Checking now...') BORDER = '0' service = protocol = 'linkedin' AuthClass = oauth_util.InternalBrowserAuthenticator #AuthClass = oauth_util.UserBrowserAuthenticator webroot = 'https://www.linkedin.com' def weblink(self, resource=''): return net.httpjoin(self.webroot, resource) @property def header_funcs(self): return ( (_('Home'), self.weblink()), (_('Inbox'), self.weblink('inBox')), (_('Contacts'), self.weblink('connections')), (_('Profile'), self.weblink('myprofile')), ) def _get_inbox_count_str(self): if self.count: return ' (%d)' % self.count else: return '' def __init__(self, **options): self.time_offset = None options['password'] = None oauth_util.OAuthAccountBase.__init__(self, **options) self.connection = None self._dirty = False SocialNetwork.__init__(self, **options) self.social_feed = SocialFeed('linkedin_' + self.username, 'linkedin_' + self.username, self.get_newsfeed_iter, self.generate_newsfeed_html, lambda: self.set_infobox_dirty) def set_dirty(self): log.info("set dirty") self._dirty = True def _cache_data(self, api_data): self.api_data = api_data api_data = cacheable.cproperty({}, user=True) def get_newsfeed_iter(self): return iter(getattr(getattr(self, 'connection', None), 'updates', [])) def set_infobox_dirty(self): self._dirty = True self.notify('dirty') @property def cache_path(self): return os.path.join(self.protocol, self.name, 'api-results.dat') def get_options(self): opts = {'informed_ach': True, 'post_ach_all': False} opts.update(SocialNetwork.get_options(self)) opts.update(oauth_util.OAuthAccountBase.get_options(self)) return opts def Connect(self): self.error_txt = None log.info('Connect called for %r', self) self._update_now() def _update_now(self): if self.enabled: self.update_now() else: self.set_offline(self.Reasons.NONE) def _connect(self): self.connection.connect() def create_connection(self): if self.connection is not None: raise Exception('Already have a connection') import LinkedInProtocol as LIP self.connection = LIP.LinkedInProtocol(self.username, self.oauth_token, self.api_data, self.filters, time_offset=self.time_offset) self.bind_events() def connect_failed(self, e): self._dirty_error = True self.Disconnect(reason=self.Reasons.CONN_FAIL) def Disconnect(self, reason=None): if reason is None: reason = self.Reasons.NONE self.unbind_events() self.connection = None self.set_offline(reason) common.UpdateMixin.disconnect(self) def handle_rate_limited(self): log.info("rate limited!") self.error_txt = _("API request limit has been reached.") self.handle_update_failed('RATE_LIMIT') def handle_update_failed(self, _reason): reason = getattr(self.Reasons, _reason, self.Reasons.CONN_FAIL) self.Disconnect(reason=reason) def update_item_to_notification(self, item): import weakref import gui.browser.webkit.imageloader as imageloader default_icon = skin.get('BuddiesPanel.BuddyIcons.NoIcon', None) if hasattr(item, 'content_body'): body = item.content_body() else: html = self.generate_item_html(item) body = util.strip_html(html).strip() n = util.Storage(acct=weakref.ref(self), icon=imageloader.LazyWebKitImage( item.person.picture_url, default_icon), body=body, title=item.person.name, url=getattr(item, 'url', item.person.profile_url), post_id=item.id) return n def do_notifications(self, updates): if not updates: return items = [] for item in updates: try: items.append(self.update_item_to_notification(item)) except Exception: traceback.print_exc() common.fire('linkedin.newsfeed', items=items, popupid='%d.linkedin' % id(self), update='paged', badge=skin.get('serviceicons.linkedin', None), buttons=self.get_popup_buttons, onclick=self.on_popup_click) def get_popup_buttons(self, item): self._last_popup_item = item buttons = [] my_item = self.connection.get_post_by_id(item.item.post_id) if my_item is None: return buttons def count_str(count): return (' (%s)' % count) if count and count != 0 else '' if my_item.supports_comments: buttons.append( (_("Comment") + count_str(len(my_item.get_comments())), input_callback(self.on_popup_comment))) return buttons def on_popup_comment(self, item, control, text, options): post_id = options['item'].post_id self._post_comment( post_id, text, success=(lambda *a, **k: (self.set_dirty(), control.update_buttons()))) on_popup_comment.takes_popup_control = True def on_popup_click(self, item): import wx url = getattr(item, 'url', None) if url: import wx wx.LaunchDefaultBrowser(url) def bind_events(self): conn = oauth_util.OAuthAccountBase.bind_events(self) bind = conn.bind bind('on_rate_limit', self.handle_rate_limited) bind('update_error', self.handle_update_failed) bind('newsfeed_updates', self.do_notifications) def unbind_events(self): conn = oauth_util.OAuthAccountBase.unbind_events(self) if conn is None: return unbind = conn.unbind unbind('on_rate_limit', self.handle_rate_limited) unbind('update_error', self.handle_update_failed) unbind('newsfeed_updates', self.do_notifications) @common.action() def SetStatus(self): if common.pref('social.use_global_status', default=False, type=bool): import wx wx.GetApp().SetStatusPrompt([self]) else: log.error( "No alternative to global status dialog for new linked in account!" ) DefaultAction = OpenHomeURL = SetStatus @callbacks.callsback def _set_status(self, new_message, callback=None, **k): # callback.success += lambda * a: self.update_now() if new_message: callback.success += lambda *a: setattr( self.connection.users[self.connection.userid], 'status', new_message) callback.success += lambda *a: self.set_dirty() callback.success += lambda *a: hooks.notify( 'digsby.linkedin.status_updated', self, new_message, *a) self.connection.set_status(new_message, callback=callback) SetStatusMessage = _set_status def disconnect(self, *a, **k): pass def observe_count(self, callback): return NotImplemented def observe_state(self, callback): return NotImplemented def unobserve_count(self, callback): return NotImplemented def unobserve_state(self, callback): return NotImplemented def OnClickHomeURL(self): return self.weblink() def launchbrowser(self, what): import wx wx.LaunchDefaultBrowser(self.weblink(what)) @common.action() def openurl_Home(self): self.launchbrowser('') @common.action() def openurl_Inbox(self): self.launchbrowser('inBox') @common.action() def openurl_Friends(self): self.launchbrowser('connections') @common.action() def openurl_Profile(self): self.launchbrowser('myprofile') @common.action(lambda self: ((self.state == self.Statuses.ONLINE) and common.pref( 'can_has_social_update', False)) or None) def update_now(self): log.info('updating... %r', self) self.start_timer() log.info("current state: %r", self.state) if self.state == self.Statuses.OFFLINE or self.connection is None: self.change_state(self.Statuses.CONNECTING) try: self.create_connection() self._connect() except Exception: traceback.print_exc() self.Disconnect(self.Reasons.CONN_FAIL) return self._update() def request_status(self): self.set_waiting('status') self.connection.request_status() def _on_protocol_connect(self): log.info("connection ready") def handle_connect(self): self.change_state(self.Statuses.AUTHENTICATING) def handle_status(self, status_info=None): log.info('Got status info: %r', status_info) # def update_info(self, **info): # return SocialNetwork.update_info(self, **info) def on_feed_invalidated(self): self.social_feed.new_ids([p.id for p in self.connection.updates]) def _handle_oauth_error(self, details): log.error('oauth error occurred: %r', details) problem = net.WebFormData.parse(details.get('oauth_problem', '')) self.clear_oauth_token() if 'timestamp_refused' in problem: self.error_txt = _( "Please set your computer clock to the correct time / timezone." ) self.Disconnect(self.Reasons.BAD_PASSWORD) def get_authenticator(self, url_generator): AuthClass = self._get_auth_class(prefkey='linkedin.authenticator') return AuthClass( self.username, url_generator, '/linkedin/{username}/oauth'.format(username=self.username), 'LinkedIn Login - %s' % self.username, 'http://www.digsby.com/myspace', 'serviceicons.linkedin') def _authenticate_post(self): log.info("authenticated successfully!") oauth_util.OAuthAccountBase._authenticate_post(self) self.update_now() @callbacks.callsback def newsfeed_post_comment(self, rpc, webview, id, comment, post_id, append=True, callback=None): if append: callback.success += lambda *a: self.append_comments( webview, id, post_id) callback.error += lambda error_obj=None: self.Dexcept( webview, id, error_obj=error_obj) self._post_comment(post_id, comment, callback=callback) @callbacks.callsback def _post_comment(self, post_id, comment, callback=None): self.connection.get_post_by_id(post_id).comments.append( LIO.LinkedInComment( sequence_number=-1, text=comment, person=self.connection.users[self.connection.userid])) self.connection.post_comment(post_id, comment, callback=callback) def append_comments(self, webview, id, post_id): hooks.notify('digsby.linkedin.comment_added', {}) t = LinkedInIB(self) context = {} context['item'] = self.connection.get_post_by_id(post_id) comments_html = t.get_html( None, set_dirty=False, file='comments_list.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) comment_link_html = t.get_html( None, set_dirty=False, file='comment_link.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) log.debug("comments_html = %r; comment_link_html = %r", comments_html, comment_link_html) self.Dsuccess(webview, id, comments_html=comments_html, comment_link_html=comment_link_html) def generate_newsfeed_html(self, items, _context_id=None, do_comments=True): t = LinkedInIB(self) context = {} context['items'] = items context['conn'] = self.connection context['do_comments'] = do_comments html = t.get_html(None, set_dirty=False, file='items.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) return html def generate_item_html(self, item): t = LinkedInIB(self) context = {} context['item'] = item context['conn'] = self.connection context['friend'] = item.person html = t.get_html(None, set_dirty=False, file='%s.tenjin' % item.type, dir=t.get_context()['app'].get_res_dir('base'), context=context) return html def _update_post(self): super(LinkedInAccount, self)._update_post() def newsfeed_next_item(self, rpc, webview, id, *extra, **params): return self.social_feed.jscall_next_item(webview, id) def more_content(self, rpc, webview, id, **params): current_posts, _last_post_id = params.get('current_posts', 0), params.get( 'last_post_id', None) t = LinkedInIB(self) if current_posts < len(self.connection.updates): items = self.connection.updates[current_posts:current_posts + 1] items_html = self.generate_newsfeed_html(items) self.Dsuccess(webview, id, contents=items_html) else: self.Derror(webview, id) def newsfeed_initialize_feed(self, rpc, webview, id, *extra, **params): return self.social_feed.jscall_initialize_feed(webview, id) def newsfeed_do_dislike(self, rpc, webview, id, post_id, **kwds): log.info("do dislike: kwds = %r", kwds) item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if item.user_dislikes(self.connection.userid): log.info("user already dislikes this post") return self.newsfeed_post_comment( rpc, webview, id, LIO.LinkedInComment.DISLIKE, post_id, append=False, success=lambda *a, **k: self.dislike_added(webview, id, post_id)) def dislike_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, True) hooks.notify('digsby.linkedin.dislike_added', post_id) def newsfeed_do_like(self, rpc, webview, id, post_id, **kwds): log.info("do like: kwds = %r", kwds) item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if item.user_likes(self.connection.userid): log.info("user already likes this post") return self.newsfeed_post_comment( rpc, webview, id, LIO.LinkedInComment.LIKE, post_id, append=False, success=lambda *a, **k: self.like_added(webview, id, post_id)) def like_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, True) hooks.notify('digsby.linkedin.like_added', post_id) def refresh_likes(self, webview, id, post_id, dis=False): \ log.info("refreshing item: %r", post_id) #regen likes block, regen likes link block, send to callback #regen cached post html item_html = self.generate_newsfeed_html( [self.connection.get_post_by_id(post_id)], None) self.Dsuccess(webview, id, item_html=item_html) def _create_activity(self, body): self.connection.create_activity(body=body)
class MyspaceAccount(social.network, oauth_util.OAuthAccountBase, jsonrpc.RPCClient): url_base = 'http://www.myspace.com/index.cfm' AuthClass = oauth_util.InternalBrowserAuthenticator # UserBrowserAuthenticator #AuthClass = oauth_util.InternalBrowserAuthenticatorOpenID events = events.EventMixin.events | set(( # stuff )) service = protocol = 'myspace' indicators_keys = ['blogcommenturl', 'blogsubscriptionposturl', 'picturecommenturl', 'eventinvitationurl', 'commenturl', 'phototagapprovalurl', 'friendsrequesturl', 'videocommenturl', 'groupnotificationurl', 'recentlyaddedfriendurl', 'birthdayurl', #'countpendingim', ] feed_keys = [ 'statuses', 'friends', 'posts', 'groups', 'photos', 'music', 'videos', 'events', 'applications', ] header_fuseactions = dict(( ('Home', 'user'), #('Profile', 'user.viewprofile'), # supposed to take you to myspace.com/yourname but instead just goes to myspace.com (??) ('Inbox', 'mail.inboxV2'), ('Friends', 'user.viewfriends'), ('Blog', 'blog.ListAll'), ('Post Bulletin', 'bulletin'), )) def url_destination(name): def get_url(self): kwargs = DIGSBY_UTM_ARGS.copy() self_userid = getattr(self.connection, 'userid', None) if self_userid is not None: kwargs.update(friendId = self_userid) kwargs.update(fuseaction = self.header_fuseactions[name]) return self.openurl(net.UrlQuery(self.url_base, **kwargs)) return get_url openurl_Home = url_destination("Home") openurl_Profile = url_destination("Profile") openurl_Inbox = url_destination("Inbox") openurl_Friends = url_destination("Friends") openurl_Blog = url_destination("Blog") openurl_Post = url_destination("Post Bulletin") del url_destination def __init__(self, *a, **k): common.Protocol.StateMixin.__init__(self) oauth_util.OAuthAccountBase.__init__(self, **k) self.count = 0 self.connection = None self._dirty = False filters = k.pop('filters', {}) self.filters = dict(indicators=dict(zip(self.indicators_keys, filters.get('indicators', [True]*len(self.indicators_keys)))), feed = dict(zip(self.feed_keys, filters.get('feed', [True]*len(self.feed_keys)))), ) self.header_funcs = ( ('Home', self.openurl_Home), # ('Profile', self.openurl_Profile), ('Inbox', self.openurl_Inbox), ('Friends', self.openurl_Friends), ('Blog', self.openurl_Blog), ('Post Bulletin', self.openurl_Post), ) if 'password' not in k: k['password'] = None social.network.__init__(self, *a, **k) self._remove_password = not self.protocol_info()['needs_password'] if self._remove_password: self.password = None from social.network import SocialFeed self.social_feed = SocialFeed('myspace_' + self.username, 'activities', lambda: iter(self.connection.combined_feed()), self.htmlize_activities, self._set_dirty) def htmlize_activities(self, activities, context): t = MyspaceIB(self) return t.get_html(None, set_dirty=False, file='activities.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=dict(activities = activities)) def Connect(self): log.info('Connect called for %r', self) self._update_now() def create_connection(self): if self.connection is not None: raise Exception('Already have a connection') import MyspaceProtocol as MSP self.connection = MSP.MyspaceProtocol(self.username, self.oauth_token, self._decryptedpw, self.api_data, self.filters) self.bind_events() @property def cache_path(self): return os.path.join('myspace-social3', self.username, 'api-results.dat') def _cache_data(self, api_data): self.api_data = api_data api_data = cacheable.cproperty({}, user = True) def on_feed_invalidated(self): ids = [p.id for p in self.connection.combined_feed()] self.social_feed.new_ids(ids) def observe_count(self,callback): self.add_gui_observer(callback, 'count') def unobserve_count(self,callback): self.remove_gui_observer(callback, 'count') def _got_indicators(self, inds): log.info('got indicators: %r', inds) indicators = {} for k in self.indicators_keys: if k in inds and self.filters['indicators'].get(k, False): indicators[k] = inds[k] num_inds = len(indicators) if inds.get('mailurl', None) is not None: num_inds += 1 if getattr(self, '_num_inds', -1) != num_inds: self._num_inds = num_inds self.setnotify('count', num_inds) def get_authenticator(self, url_generator): AuthClass = self._get_auth_class(prefkey = 'myspace.authenticator') return AuthClass(self.username, url_generator, '/myspace/{username}/oauth'.format(username = self.username), 'MySpace Login - %s' % self.username, "http://www.digsby.com/myspace/", 'serviceicons.myspace') def bind_events(self): oauth_util.OAuthAccountBase.bind_events(self) self.connection.bind('on_indicators', self._got_indicators) def unbind_events(self): conn = oauth_util.OAuthAccountBase.unbind_events(self) if conn is None: return conn.unbind('on_indicators', self._got_indicators) def _on_protocol_connect(self): pass def _connect(self): #assert self.connection is not None self.connection.connect() log.info('Calling connect for connection') def _update_now(self): if self.enabled: self.update_now() else: self.set_offline(self.Reasons.NONE) @common.action(lambda self: common.pref('can_has_social_update', None) or None) def update_now(self): log.info('updating... %r', self) self.start_timer() if self.state == self.Statuses.OFFLINE: self.change_state(self.Statuses.CONNECTING) try: self.create_connection() self._connect() except Exception: import traceback; traceback.print_exc() self.Disconnect(self.Reasons.CONN_FAIL) return self._update() def _update_pre(self): if self._has_updated or self._forcing_login: st = self.Statuses.CHECKING else: st = self.Statuses.CONNECTING self.change_state(st) def _reset_connection(self): self._has_updated = False self._on_auth_done() #self.connection.disconnect() def Disconnect(self, reason = None): log.info('Disconnect called') self.pause_timer() self._reset_connection() self.unbind_events() self.connection = None reason = reason or self.Reasons.NONE if self.state != self.Statuses.OFFLINE: self.set_offline(reason) common.UpdateMixin.disconnect(self) disconnect = Disconnect def _update_error(self, e): log.debug("%r got update error: %r", self, e) if hasattr(e, 'read'): log.debug_s('\tbody: %r', e.read()) if isinstance(e, oauth.OAuthError): return self._handle_oauth_error(getattr(e, 'oauth_data', None)) if self.state == self.Statuses.OFFLINE: return if self._has_updated: rsn = self.Reasons.CONN_LOST else: rsn = self.Reasons.CONN_FAIL self.Disconnect(rsn) def _handle_oauth_error(self, details): log.error('oauth error occurred: %r', details) problem = details.get('oauth_problem', None) self.clear_oauth_token() if problem == 'timestamp_refused': self.error_txt = _("Please set your computer clock to the correct time / timezone.") self.Disconnect(self.Reasons.BAD_PASSWORD) def update_info(self, **info): filters = info.pop('filters', None) if filters is not None: self.filters.update( dict(indicators=dict(zip(self.indicators_keys, filters.get('indicators', [True]*len(self.indicators_keys)))), feed = dict(zip(self.feed_keys, filters.get('feed', [True]*len(self.feed_keys)))), )) self._set_dirty() if info.get('password') is not None and self._remove_password: info['password'] = None return social.network.update_info(self, **info) def get_options(self): opts = super(MyspaceAccount, self).get_options() opts.update({'informed_ach': True, 'post_ach_all': False}) opts.update(filters = dict(feed = [bool(self.filters['feed'].get(x, True)) for x in self.feed_keys], indicators = [bool(self.filters['indicators'].get(x, True)) for x in self.indicators_keys])) if opts.get('password') is not None and self._remove_password: opts['password'] = None if 'oauth_token' not in opts: opts['oauth_token'] = self.oauth_token return opts @common.action() def edit_status(self): if common.pref('social.use_global_status', default = False, type = bool): import wx wx.GetApp().SetStatusPrompt([self]) else: from myspacegui.editstatus import get_new_status get_new_status(success = self.set_web_status) DefaultAction = OpenHomeURL = edit_status @callbacks.callsback def SetStatusMessage(self, message, callback = None, **opts): if len(message) == 0: return callback.success() self.connection.set_status_message(message, callback = callback) def _dirty_get(self): return getattr(getattr(self, 'connection', None), '_dirty', True) def _dirty_set(self, val): if self.connection is not None: self.connection._dirty = val _dirty = property(_dirty_get, _dirty_set) @common.action(lambda self: common.pref('can_has_social_update', None) or None) def _set_dirty(self): self._dirty = True ## TODO: Move all this rpc nonsense to a superclass, with bind/unbind/callbacks and some sweet-ass decorator magic. _rpc_handlers = { 'near_bottom' : 'more_content', 'post_comment' : 'post_comment', 'hook' : 'rpc_hook', 'load_comments' : 'load_comments', 'initialize_feed' : 'initialize_feed', 'next_item' : 'next_item', 'do_permissions': 'initiate_login', 'do_like' : 'newsfeed_do_like', 'do_dislike' : 'newsfeed_do_dislike', } def initiate_login(self, *a, **k): self.connection.userinfo = None oauth_util.OAuthAccountBase.initiate_login(self, *a, **k) def user_dislikes(self, userid, item): return item.user_dislikes(userid) def user_likes(self, userid, item): return item.user_likes(userid) def newsfeed_do_dislike(self, rpc, webview, id, post_id): item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if self.user_dislikes(self.connection.userid, item): log.info("user already dislikes this post") return import myspace.objects as MSO self.post_comment(rpc, webview, id, MSO.MyspaceComment.DISLIKE, post_id, append = False, success = lambda *a, **k: self.dislike_added(webview, id, post_id)) def newsfeed_do_like(self, rpc, webview, id, post_id): item = self.connection.get_post_by_id(post_id) if item is None: log.error("%r: no post for post_id %r", self, post_id) return if self.user_likes(self.connection.userid, item): log.info("user already likes this post") return import myspace.objects as MSO self.post_comment(rpc, webview, id, MSO.MyspaceComment.LIKE, post_id, append = False, success = lambda *a, **k: self.like_added(webview, id, post_id)) def dislike_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, True) hooks.notify('digsby.myspace.dislike_added', post_id) def like_added(self, webview, id, post_id): self.refresh_likes(webview, id, post_id, False) hooks.notify('digsby.myspace.like_added', post_id) def refresh_likes(self, webview, id, post_id, dis = False):\ log.info("refreshing item: %r", post_id) #regen likes block, regen likes link block, send to callback #regen cached post html item = self.connection.get_post_by_id(post_id) item_html = self.generate_newsfeed_html([item]) self.Dsuccess(webview, id, item_html = item_html) @callbacks.callsback def post_comment(self, rpc, webview, id, comment, post_id, append = True, callback = None): post = self.connection.get_post_by_id(post_id) if append: post._numComments += 1 callback.success += lambda *a: self.append_comments(webview, id, post_id) else: import myspace.objects as MSO post.comments.append(MSO.MyspaceComment.from_json( dict( userid = 'myspace.com.person.%s' % self.connection.userid, text = comment, commentId = str(random.randint(0, 0x7fffffff)), postedDate_parsed = time.time(), ))) callback.error += lambda *a: self.Dexcept(webview, id, *a) self._post_comment(comment, post_id, callback = callback) @callbacks.callsback def _post_comment(self, comment, post_id, callback = None): self.connection.post_comment(post_id, comment, callback = callback) def generate_newsfeed_html(self, activities, _context_id = None, do_comments = True): t = MyspaceIB(self) activities_html = t.get_html(None, set_dirty=False, file='activities.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=dict(activities = activities)) return activities_html def append_comments(self, webview, id, post_id): t = MyspaceIB(self) context = {} context['item'] = self.connection.get_post_by_id(post_id) comments_html = t.get_html(None, set_dirty=False, file='comment_section.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) bottom_row_html = t.get_html(None, set_dirty=False, file='bottom_row.tenjin', dir=t.get_context()['app'].get_res_dir('base'), context=context) self.Dsuccess(webview, id, comments_html = comments_html, bottom_row_html = bottom_row_html) self.api_data['friend_status'] = self.connection.friend_status self._cache_data(self.api_data) def more_content(self, rpc, webview, id, **params): current_posts, _last_post_id = params.get('current_posts', 0), params.get('last_post_id', None) if current_posts < len(self.connection.combined_feed()): activities = self.connection.combined_feed()[current_posts:current_posts+1] activities_html = self.generate_newsfeed_html(activities) self.Dsuccess(webview, id, contents = activities_html) else: self.Derror(webview, id) def initialize_feed(self, rpc, webview, id, *extra, **params): self.social_feed.jscall_initialize_feed(webview, id) def next_item(self, rpc, webview, id, *extra, **params): self.social_feed.jscall_next_item(webview, id) def load_comments(self, rpc, webview, id, post_id): self.connection.get_comments_for(post_id, #success = lambda: self.refresh_likes(webview, id, post_id), success = lambda: self.append_comments(webview, id, post_id), error = lambda error_obj = None, **k: self.Dexcept(webview, id, error_obj = error_obj, **k)) #self.more_content(args['current_posts'], args.pop('last_post_id', None), rpc.pop('id')) def user_from_activity(self, act): return self.connection.user_from_activity(act) def get_imageurl_for_user(self, user): if user is None: return None else: return user.get('image', user.get('thumbnailUrl', None)) def user_from_id(self, id): return self.connection.user_from_id(id) def _name_for_user(self, user): name = getattr(user, 'name', None) if name is None: return getattr(user, 'displayName', 'Private User') firstname, lastname = name.get('givenName', None), name.get('familyName', None) if (firstname, lastname) == (None, None): return name if firstname and lastname: return u"%s %s" % (firstname, lastname) return firstname or lastname