[gwibber] 3.3.1.1
Tom Callaway
spot at fedoraproject.org
Tue Dec 20 17:52:25 UTC 2011
commit f8a5046133e90686d2d5eceef4320c3868872f18
Author: Tom Callaway <spot at fedoraproject.org>
Date: Tue Dec 20 12:52:17 2011 -0500
3.3.1.1
.gitignore | 1 +
gwibber-2.91.92-gowalla-icons.patch | Bin 8093 -> 0 bytes
gwibber-3.3.1.1-facebook-fedora.patch | 12 +
gwibber-3.3.1.1-fix-twitter-oauth.patch | 14 +
gwibber-3.3.1.1-kitchen-unicode.patch | 692 +++++++++++++++++++++++++
gwibber-3.3.1.1-no-gtkspell.patch | 14 +
gwibber-3.3.1.1-no-unity.patch | 18 +
gwibber-3.3.1.1-sina.patch | 817 ++++++++++++++++++++++++++++++
gwibber-3.3.1.1-sqlite-catch_error.patch | 89 ++++
gwibber.spec | 168 ++++---
sources | 2 +-
11 files changed, 1766 insertions(+), 61 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index b35fae3..be54aec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ gwibber-pre3-738bzr.tar.gz
/gwibber-3.0.0.tar.gz
/gwibber-3.0.0.1.tar.gz
/gwibber-3.1.0.tar.gz
+/gwibber-3.3.1.1.tar.gz
diff --git a/gwibber-3.3.1.1-facebook-fedora.patch b/gwibber-3.3.1.1-facebook-fedora.patch
new file mode 100644
index 0000000..e771dc8
--- /dev/null
+++ b/gwibber-3.3.1.1-facebook-fedora.patch
@@ -0,0 +1,12 @@
+diff -up gwibber-3.3.1.1/gwibber/microblog/util/const.py.in.fedora gwibber-3.3.1.1/gwibber/microblog/util/const.py.in
+--- gwibber-3.3.1.1/gwibber/microblog/util/const.py.in.fedora 2011-11-10 11:21:01.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/util/const.py.in 2011-11-30 14:36:03.513287754 -0500
+@@ -10,7 +10,7 @@ from os import environ
+ if environ.has_key("FB_APP_KEY"):
+ FB_APP_KEY = environ["FB_APP_KEY"]
+ else:
+- FB_APP_KEY = "71b85c6d8cb5bbb9f1a3f8bbdcdd4b05"
++ FB_APP_KEY = "1e51e9b88c054048dc201f81ea5543b7"
+
+ TWITTER_OAUTH_KEY = "qMBra1U4bpNYvDz947M5Q"
+ TWITTER_OAUTH_SECRET = "Lzdkhg0WvGYFzD9tnsuwC0zYmpJ4z7HrZl3yOxU1g"
diff --git a/gwibber-3.3.1.1-fix-twitter-oauth.patch b/gwibber-3.3.1.1-fix-twitter-oauth.patch
new file mode 100644
index 0000000..672d122
--- /dev/null
+++ b/gwibber-3.3.1.1-fix-twitter-oauth.patch
@@ -0,0 +1,14 @@
+diff -up gwibber-3.3.1.1/gwibber/microblog/util/const.py.in.orig gwibber-3.3.1.1/gwibber/microblog/util/const.py.in
+--- gwibber-3.3.1.1/gwibber/microblog/util/const.py.in.orig 2011-11-30 14:42:45.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/util/const.py.in 2011-11-30 14:43:23.566762443 -0500
+@@ -12,8 +12,8 @@ if environ.has_key("FB_APP_KEY"):
+ else:
+ FB_APP_KEY = "1e51e9b88c054048dc201f81ea5543b7"
+
+-TWITTER_OAUTH_KEY = "qMBra1U4bpNYvDz947M5Q"
+-TWITTER_OAUTH_SECRET = "Lzdkhg0WvGYFzD9tnsuwC0zYmpJ4z7HrZl3yOxU1g"
++TWITTER_OAUTH_KEY = "GDbYbywyvbcPfe26MqrifQ"
++TWITTER_OAUTH_SECRET = "U4C34CzUW6YYNFinX8RjSTsWWn2bWX08WVdFsxyQ"
+
+ SINA_OAUTH_KEY = "4014350411"
+ SINA_OAUTH_SECRET = "92e7877ad12d59a8410d850c19787701"
diff --git a/gwibber-3.3.1.1-kitchen-unicode.patch b/gwibber-3.3.1.1-kitchen-unicode.patch
new file mode 100644
index 0000000..665c9fd
--- /dev/null
+++ b/gwibber-3.3.1.1-kitchen-unicode.patch
@@ -0,0 +1,692 @@
+diff -up gwibber-3.3.1.1/gwibber/microblog/dispatcher.py.kitchen gwibber-3.3.1.1/gwibber/microblog/dispatcher.py
+--- gwibber-3.3.1.1/gwibber/microblog/dispatcher.py.kitchen 2011-11-10 09:40:58.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/dispatcher.py 2011-11-30 14:21:00.417769649 -0500
+@@ -5,6 +5,7 @@ import multiprocessing, threading, trace
+ import gobject, dbus, dbus.service
+ import sqlite3, mx.DateTime, re, uuid
+ import urlshorter, storage, network, util, uploader
++from kitchen.text.converters import to_unicode
+ from gettext import lgettext as _
+ import signal
+
+@@ -73,9 +74,9 @@ def perform_operation((account, opname,
+ for m in message_data:
+ try:
+ if isinstance(m, dict) and m.has_key("mid"):
+- m["id"] = uuid.uuid1().hex
+- m["operation"] = opname
+- m["stream"] = m.get("stream", stream)
++ m["id"] = to_unicode(uuid.uuid1().hex)
++ m["operation"] = to_unicode(opname)
++ m["stream"] = to_unicode(m.get("stream", stream))
+ m["transient"] = transient
+ m["time"] = m.get("time", 0)
+ if not m["text"]: m["text"] = ""
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/buzz/__init__.py.kitchen gwibber-3.3.1.1/gwibber/microblog/plugins/buzz/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/buzz/__init__.py.kitchen 2011-06-02 09:25:01.000000000 -0400
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/buzz/__init__.py 2011-11-30 14:19:55.706601610 -0500
+@@ -2,6 +2,7 @@ from gwibber.microblog import network, u
+ from gwibber.microblog.util import resources
+ import json
+ from oauth import oauth
++from kitchen.text.converters import to_unicode
+
+ PROTOCOL_INFO = {
+ "name": "Buzz",
+@@ -43,32 +44,32 @@ class Client:
+
+ def _actor(self, user):
+ return {
+- "name": user["name"],
+- "nick": user["id"],
+- "id": user["id"],
+- "image": user.get("thumbnailUrl", "https://mail.google.com/mail/images/blue_ghost.jpg?sz=45"),
+- "url": user.get("profileUrl", None),
++ "name": to_unicode(user["name"]),
++ "nick": to_unicode(user["id"]),
++ "id": to_unicode(user["id"]),
++ "image": to_unicode(user.get("thumbnailUrl", "https://mail.google.com/mail/images/blue_ghost.jpg?sz=45")),
++ "url": to_unicode(user.get("profileUrl", None)),
+ "is_me": user["id"] == self.account["user_id"],
+ }
+
+ def _message(self, data):
+ m = {
+ "mid": data["id"],
+- "service": "buzz",
+- "account": self.account["id"],
++ "service": u"buzz",
++ "account": to_unicode(self.account["id"]),
+ "time": util.parsetime(data["published"]),
+- "url": data.get("links", {})["alternate"][0].get("href", ""),
+- "source": data.get("source", {}).get("title", None),
++ "url": to_unicode(data.get("links", {})["alternate"][0].get("href", "")),
++ "source": to_unicode(data.get("source", {}).get("title", None)),
+ "sender": self._actor(data["actor"]),
+ }
+
+- m["text"] = data["object"]["content"]
++ m["text"] = to_unicode(data["object"]["content"])
+
+ if data.get("source", {}).get("title", 0) == "Twitter":
+ m["text"] = m["text"].split(">:", 1)[1].strip()
+
+- m["html"] = m["text"]
+- m["content"] = m["text"]
++ m["html"] = to_unicode(m["text"])
++ m["content"] = to_unicode(m["text"])
+
+ if data.get("geocode", 0):
+ m["location"] = {
+@@ -77,24 +78,24 @@ class Client:
+ }
+
+ if data.get("address", 0):
+- m["location"]["address"] = data["address"]
++ m["location"]["address"] = to_unicode(data["address"])
+
+ m["images"] = []
+ for a in data["object"].get("attachments", []):
+ if a["type"] == "photo":
+ m["images"].append({
+- "src": a["links"]["preview"][0]["href"],
+- "url": a["links"]["enclosure"][0]["href"]
++ "src": to_unicode(a["links"]["preview"][0]["href"]),
++ "url": to_unicode(a["links"]["enclosure"][0]["href"])
+ })
+
+ if a["type"] == "video":
+ m["images"].append({
+- "src": a["links"]["preview"][0]["href"],
+- "url": a["links"]["alternate"][0]["href"],
++ "src": to_unicode(a["links"]["preview"][0]["href"]),
++ "url": to_unicode(a["links"]["alternate"][0]["href"]),
+ })
+
+ if a["type"] == "article":
+- m["content"] += "<p><b><a href=\"%s\">%s</a></b></p>" % (a["links"]["alternate"][0]["href"], a["title"])
++ m["content"] += to_unicode("<p><b><a href=\"%s\">%s</a></b></p>" % (a["links"]["alternate"][0]["href"], a["title"]))
+
+ return m
+
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/digg/__init__.py.kitchen gwibber-3.3.1.1/gwibber/microblog/plugins/digg/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/digg/__init__.py.kitchen 2011-06-02 09:25:01.000000000 -0400
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/digg/__init__.py 2011-11-30 14:19:55.707601597 -0500
+@@ -1,6 +1,7 @@
+ from gwibber.microblog import network, util
+ from gwibber.microblog.util import resources
+ from gettext import lgettext as _
++from kitchen.text.converters import to_unicode
+
+ PROTOCOL_INFO = {
+ "name": "Digg",
+@@ -33,33 +34,33 @@ class Client:
+ def _story(self, data):
+ m = {};
+ m["mid"] = str(data["id"])
+- m["service"] = "digg"
+- m["account"] = self.account["id"]
++ m["service"] = u"digg"
++ m["account"] = to_unicode(self.account["id"])
+ m["time"] = data["submit_date"]
+
+- m["text"] = data["title"] + "\n" + data["description"]
+- m["content"] = "<b>%(title)s</b><br />%(description)s" % data
+- m["html"] = "<b>%(title)s</b><br />%(description)s" % data
++ m["text"] = to_unicode(data["title"]) + u"\n" + to_unicode(data["description"])
++ m["content"] = to_unicode("<b>%(title)s</b><br />%(description)s" % data)
++ m["html"] = to_unicode("<b>%(title)s</b><br />%(description)s" % data)
+ user = data["friends"]["users"][0]
+
+ m["sender"] = {}
+- m["sender"]["nick"] = user["name"]
+- m["sender"]["id"] = user["name"]
+- m["sender"]["image"] = user["icon"]
+- m["sender"]["url"] = "http://digg.com/users/%s" % user["name"]
++ m["sender"]["nick"] = to_unicode(user["name"])
++ m["sender"]["id"] = to_unicode(user["name"])
++ m["sender"]["image"] = to_unicode(user["icon"])
++ m["sender"]["url"] = to_unicode("http://digg.com/users/%s" % user["name"])
+ m["sender"]["is_me"] = user["name"] == self.account["username"]
+- if user.get("fullname", 0): m["sender"]["name"] = user["fullname"]
++ if user.get("fullname", 0): m["sender"]["name"] = to_unicode(user["fullname"])
+
+- m["url"] = data["link"]
++ m["url"] = to_unicode(data["link"])
+ m["likes"] = {"count": data["diggs"]}
+
+- m["html"] = util.linkify(m["text"],
++ m["html"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % URL_PREFIX),
+- (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX)))
++ (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX))))
+
+- m["content"] = util.linkify(m["text"],
++ m["content"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a class="hash" href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
+- (util.PARSE_NICK, '@<a class="nick" href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])))
++ (util.PARSE_NICK, '@<a class="nick" href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"]))))
+
+ return m
+
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/facebook/__init__.py.kitchen gwibber-3.3.1.1/gwibber/microblog/plugins/facebook/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/facebook/__init__.py.kitchen 2011-08-22 11:13:29.000000000 -0400
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/facebook/__init__.py 2011-11-30 14:23:54.571537504 -0500
+@@ -6,6 +6,7 @@ import hashlib, mx.DateTime, time
+ from os.path import join, getmtime, exists
+ from gettext import lgettext as _
+ from gwibber.microblog.util.const import *
++from kitchen.text.converters import to_unicode
+ # Try to import * from custom, install custom.py to include packaging
+ # customizations like distro API keys, etc
+ try:
+@@ -101,11 +102,11 @@ class Client:
+
+ def _sender(self, data):
+ sender = {
+- "name": data["name"],
++ "name": to_unicode(data["name"]),
+ "id": str(data.get("id", '')),
+ "is_me": str(data.get("id", '')) == self.user_id,
+- "image": URL_PREFIX + data["id"] + "/picture",
+- "url": "https://www.facebook.com/profile.php?id=" + str(data.get("id", ''))
++ "image": to_unicode(URL_PREFIX) + to_unicode(data["id"]) + u"/picture",
++ "url": u"https://www.facebook.com/profile.php?id=" + to_unicode(str(data.get("id", '')))
+ }
+ return sender
+
+@@ -115,27 +116,27 @@ class Client:
+ return {}
+
+ m = {}
+- m["mid"] = str(data["id"])
+- m["service"] = "facebook"
++ m["mid"] = to_unicode(str(data["id"]))
++ m["service"] = u"facebook"
+ m["account"] = self.account["id"]
+
+ m["time"] = int(mx.DateTime.DateTimeFrom(str(data.get("updated_time", data["created_time"]))))
+- m["url"] = "https://facebook.com/" + data["id"].split("_")[0] + "/posts/" + data["id"].split("_")[1]
++ m["url"] = u"https://facebook.com/" + to_unicode(data["id"].split("_")[0]) + u"/posts/" + to_unicode(data["id"].split("_")[1])
+
+
+ if data.get("attribution", 0):
+- m["source"] = util.strip_urls(data["attribution"]).replace("via ", "")
++ m["source"] = to_unicode(util.strip_urls(data["attribution"]).replace("via ", ""))
+
+ if data.has_key("message"):
+ m["to_me"] = ("@%s" % self.account["username"]) in data["message"]
+ if data.get("message", "").strip():
+- m["text"] = data["message"]
+- m["html"] = util.linkify(data["message"])
+- m["content"] = m["html"]
++ m["text"] = to_unicode(data["message"])
++ m["html"] = to_unicode(util.linkify(data["message"]))
++ m["content"] = to_unicode(m["html"])
+ else:
+- m["text"] = ""
+- m["html"] = ""
+- m["content"] = ""
++ m["text"] = u""
++ m["html"] = u""
++ m["content"] = u""
+
+ m["sender"] = self._sender(data["from"])
+
+@@ -186,17 +187,17 @@ class Client:
+ if data["comments"].has_key("data"):
+ for item in data["comments"]["data"]:
+ m["comments"].append({
+- "text": item["message"],
++ "text": to_unicode(item["message"]),
+ "time": int(mx.DateTime.DateTimeFrom(str(data.get("updated_time", data["created_time"])))),
+ "sender": self._sender(item["from"]),
+ })
+
+ if data.get("attachment", 0):
+ if data["attachment"].get("name", 0):
+- m["content"] += "<p><b>%s</b></p>" % data["attachment"]["name"]
++ m["content"] += to_unicode("<p><b>%s</b></p>" % data["attachment"]["name"])
+
+ if data["attachment"].get("description", 0):
+- m["content"] += "<p>%s</p>" % data["attachment"]["description"]
++ m["content"] += to_unicode("<p>%s</p>" % data["attachment"]["description"])
+
+ return m
+
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/identica/__init__.py.kitchen gwibber-3.3.1.1/gwibber/microblog/plugins/identica/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/identica/__init__.py.kitchen 2011-11-10 09:40:58.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/identica/__init__.py 2011-11-30 14:27:18.931930075 -0500
+@@ -4,6 +4,7 @@ import gnomekeyring
+ from oauth import oauth
+ from gwibber.microblog.util import log, resources
+ from gettext import lgettext as _
++from kitchen.text.converters import to_unicode
+ log.logger.name = "Identi.ca"
+
+ PROTOCOL_INFO = {
+@@ -62,21 +63,21 @@ class Client:
+ m = {}
+ try:
+ m["mid"] = str(data["id"])
+- m["service"] = "identica"
++ m["service"] = u"identica"
+ m["account"] = self.account["id"]
+ if data.has_key("created_at"):
+ m["time"] = util.parsetime(data["created_at"])
+- m["source"] = data.get("source", False)
+- m["text"] = util.unescape(data["text"])
++ m["source"] = to_unicode(data.get("source", False))
++ m["text"] = to_unicode(util.unescape(data["text"]))
+ m["to_me"] = ("@%s" % self.account["username"]) in data["text"]
+
+- m["html"] = util.linkify(m["text"],
++ m["html"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % self.url_prefix),
+- (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % self.url_prefix)), escape=False)
++ (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % self.url_prefix)), escape=False))
+
+- m["content"] = util.linkify(m["text"],
++ m["content"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
+- (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True)
++ (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True))
+
+ m["favorited"] = data.get("favorited", False)
+
+@@ -85,7 +86,7 @@ class Client:
+ for a in data["attachments"]:
+ mime = a.get("mimetype", "")
+ if mime and mime.startswith("image") and a.get("url", 0):
+- images.append({"src": a["url"], "url": a["url"]})
++ images.append({"src": to_unicode(a["url"]), "url": to_unicode(a["url"])})
+
+ images.extend(util.imgpreview(m["text"]))
+
+@@ -99,19 +100,19 @@ class Client:
+
+ def _user(self, user):
+ return {
+- "name": user.get("name", None),
+- "nick": user.get("screen_name", None),
++ "name": to_unicode(user.get("name", None)),
++ "nick": to_unicode(user.get("screen_name", None)),
+ "id": user.get("id", None),
+- "location": user.get("location", None),
++ "location": to_unicode(user.get("location", None)),
+ "followers": user.get("followers_count", None),
+ "friends": user.get("friends_count", None),
+- "description": user.get("description", None),
++ "description": to_unicode(user.get("description", None)),
+ "following": user.get("following", None),
+ "protected": user.get("protected", None),
+ "statuses": user.get("statuses_count", None),
+- "image": user.get("profile_image_url", None),
+- "website": user.get("url", None),
+- "url": "/".join((self.url_prefix, user["screen_name"])) or None,
++ "image": to_unicode(user.get("profile_image_url", None)),
++ "website": to_unicode(user.get("url", None)),
++ "url": to_unicode("/".join((self.url_prefix, user["screen_name"]))) or None,
+ "is_me": user.get("screen_name", None) == self.account["username"],
+ }
+
+@@ -140,9 +141,9 @@ class Client:
+ if data["in_reply_to_status_id"]:
+ m["reply"] = {}
+ m["reply"]["id"] = data["in_reply_to_status_id"]
+- m["reply"]["nick"] = data["in_reply_to_screen_name"]
++ m["reply"]["nick"] = to_unicode(data["in_reply_to_screen_name"])
+ if m["reply"]["id"] and m["reply"]["nick"]:
+- m["reply"]["url"] = "/".join((self.url_prefix, "notice", str(m["reply"]["id"])))
++ m["reply"]["url"] = to_unicode("/".join((self.url_prefix, "notice", str(m["reply"]["id"]))))
+ else:
+ m["reply"]["url"] = None
+
+@@ -155,12 +156,12 @@ class Client:
+ m = self._message(data)
+ m["private"] = True
+ m["recipient"] = {}
+- m["recipient"]["name"] = data["recipient"]["name"]
+- m["recipient"]["nick"] = data["recipient"]["screen_name"]
++ m["recipient"]["name"] = to_unicode(data["recipient"]["name"])
++ m["recipient"]["nick"] = to_unicode(data["recipient"]["screen_name"])
+ m["recipient"]["id"] = data["recipient"]["id"]
+- m["recipient"]["image"] = data["recipient"]["profile_image_url"]
+- m["recipient"]["location"] = data["recipient"]["location"]
+- m["recipient"]["url"] = "/".join((self.url_prefix, m["recipient"]["nick"]))
++ m["recipient"]["image"] = to_unicode(data["recipient"]["profile_image_url"])
++ m["recipient"]["location"] = to_unicode(data["recipient"]["location"])
++ m["recipient"]["url"] = to_unicode("/".join((self.url_prefix, m["recipient"]["nick"])))
+ m["recipient"]["is_me"] = m["recipient"]["nick"].lower() == self.account["username"].lower()
+ m["to_me"] = m["recipient"]["is_me"]
+ return m
+@@ -171,14 +172,14 @@ class Client:
+ if data["to_user_id"]:
+ m["reply"] = {}
+ m["reply"]["id"] = data["to_user_id"]
+- m["reply"]["nick"] = data["to_user"]
++ m["reply"]["nick"] = to_unicode(data["to_user"])
+
+ m["sender"] = {}
+- m["sender"]["nick"] = data["from_user"]
++ m["sender"]["nick"] = to_unicode(data["from_user"])
+ m["sender"]["id"] = data["from_user_id"]
+- m["sender"]["image"] = data["profile_image_url"]
+- m["sender"]["url"] = "/".join((self.url_prefix, m["sender"]["nick"]))
+- m["url"] = "/".join((self.url_prefix, "notice", str(m["mid"])))
++ m["sender"]["image"] = to_unicode(data["profile_image_url"])
++ m["sender"]["url"] = to_unicode("/".join((self.url_prefix, m["sender"]["nick"])))
++ m["url"] = to_unicode("/".join((self.url_prefix, "notice", str(m["mid"]))))
+ return m
+
+ def _profile(self, data):
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/statusnet/__init__.py.kitchen gwibber-3.3.1.1/gwibber/microblog/plugins/statusnet/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/statusnet/__init__.py.kitchen 2011-11-10 09:40:58.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/statusnet/__init__.py 2011-11-30 14:30:02.877846782 -0500
+@@ -4,6 +4,7 @@ import gnomekeyring
+ from oauth import oauth
+ from gwibber.microblog.util import log, resources
+ from gettext import lgettext as _
++from kitchen.text.converters import to_unicode
+ log.logger.name = "StatusNet"
+
+ PROTOCOL_INFO = {
+@@ -65,21 +66,21 @@ class Client:
+ m = {}
+ try:
+ m["mid"] = str(data["id"])
+- m["service"] = "statusnet"
++ m["service"] = u"statusnet"
+ m["account"] = self.account["id"]
+ if data.has_key("created_at"):
+ m["time"] = util.parsetime(data["created_at"])
+- m["source"] = data.get("source", False)
+- m["text"] = util.unescape(data["text"])
++ m["source"] = to_unicode(data.get("source", False))
++ m["text"] = to_unicode(util.unescape(data["text"]))
+ m["to_me"] = ("@%s" % self.account["username"]) in data["text"]
+
+- m["html"] = util.linkify(m["text"],
++ m["html"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % self.account["url_prefix"]),
+- (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % self.account["url_prefix"])), escape=False)
++ (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % self.account["url_prefix"])), escape=False))
+
+- m["content"] = util.linkify(m["text"],
++ m["content"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
+- (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True)
++ (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True))
+
+ m["favorited"] = data.get("favorited", False)
+
+@@ -88,7 +89,7 @@ class Client:
+ for a in data["attachments"]:
+ mime = a.get("mimetype", "")
+ if mime and mime.startswith("image") and a.get("url", 0):
+- images.append({"src": a["url"], "url": a["url"]})
++ images.append({"src": to_unicode(a["url"]), "url": to_unicode(a["url"])})
+
+ images.extend(util.imgpreview(m["text"]))
+
+@@ -102,19 +103,19 @@ class Client:
+
+ def _user(self, user):
+ return {
+- "name": user.get("name", None),
+- "nick": user.get("screen_name", None),
++ "name": to_unicode(user.get("name", None)),
++ "nick": to_unicode(user.get("screen_name", None)),
+ "id": user.get("id", None),
+ "location": user.get("location", None),
+ "followers": user.get("followers_count", None),
+ "friends": user.get("friends_count", None),
+- "description": user.get("description", None),
++ "description": to_unicode(user.get("description", None)),
+ "following": user.get("following", None),
+ "protected": user.get("protected", None),
+ "statuses": user.get("statuses_count", None),
+- "image": user.get("profile_image_url", None),
+- "website": user.get("url", None),
+- "url": "/".join((self.url_prefix, user["screen_name"])) or None,
++ "image": to_unicode(user.get("profile_image_url", None)),
++ "website": to_unicode(user.get("url", None)),
++ "url": to_unicode("/".join((self.url_prefix, user["screen_name"]))) or None,
+ "is_me": user.get("screen_name", None) == self.account["username"],
+ }
+
+@@ -142,14 +143,14 @@ class Client:
+ if data["in_reply_to_status_id"]:
+ m["reply"] = {}
+ m["reply"]["id"] = data["in_reply_to_status_id"]
+- m["reply"]["nick"] = data["in_reply_to_screen_name"]
++ m["reply"]["nick"] = to_unicode(data["in_reply_to_screen_name"])
+ if m["reply"]["id"] and m["reply"]["nick"]:
+- m["reply"]["url"] = "/".join((self.account["url_prefix"], "notice", str(m["reply"]["id"])))
++ m["reply"]["url"] = to_unicode("/".join((self.account["url_prefix"], "notice", str(m["reply"]["id"]))))
+ else:
+ m["reply"]["url"] = None
+
+ m["sender"] = self._user(data["user"] if "user" in data else data["sender"])
+- m["url"] = "/".join((self.account["url_prefix"], "notice", str(m["mid"])))
++ m["url"] = to_unicode("/".join((self.account["url_prefix"], "notice", str(m["mid"]))))
+
+ return m
+
+@@ -157,12 +158,12 @@ class Client:
+ m = self._message(data)
+ m["private"] = True
+ m["recipient"] = {}
+- m["recipient"]["name"] = data["recipient"]["name"]
+- m["recipient"]["nick"] = data["recipient"]["screen_name"]
++ m["recipient"]["name"] = to_unicode(data["recipient"]["name"])
++ m["recipient"]["nick"] = to_unicode(data["recipient"]["screen_name"])
+ m["recipient"]["id"] = data["recipient"]["id"]
+- m["recipient"]["image"] = data["recipient"]["profile_image_url"]
+- m["recipient"]["location"] = data["recipient"]["location"]
+- m["recipient"]["url"] = "/".join((self.account["url_prefix"], m["recipient"]["nick"]))
++ m["recipient"]["image"] = to_unicode(data["recipient"]["profile_image_url"])
++ m["recipient"]["location"] = to_unicode(data["recipient"]["location"])
++ m["recipient"]["url"] = to_unicode("/".join((self.account["url_prefix"], m["recipient"]["nick"])))
+ m["recipient"]["is_me"] = m["recipient"]["nick"].lower() == self.account["username"].lower()
+ m["to_me"] = m["recipient"]["is_me"]
+ return m
+@@ -176,11 +177,11 @@ class Client:
+ m["reply"]["nick"] = data["to_user"]
+
+ m["sender"] = {}
+- m["sender"]["nick"] = data["from_user"]
++ m["sender"]["nick"] = to_unicode(data["from_user"])
+ m["sender"]["id"] = data["from_user_id"]
+- m["sender"]["image"] = data["profile_image_url"]
+- m["sender"]["url"] = "/".join((self.account["url_prefix"], m["sender"]["nick"]))
+- m["url"] = "/".join((self.account["url_prefix"], "notice", str(m["mid"])))
++ m["sender"]["image"] = to_unicode(data["profile_image_url"])
++ m["sender"]["url"] = to_unicode("/".join((self.account["url_prefix"], m["sender"]["nick"])))
++ m["url"] = to_unicode("/".join((self.account["url_prefix"], "notice", str(m["mid"]))))
+ return m
+
+ def _profile(self, data):
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/twitter/__init__.py.kitchen gwibber-3.3.1.1/gwibber/microblog/plugins/twitter/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/twitter/__init__.py.kitchen 2011-11-10 11:20:13.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/twitter/__init__.py 2011-11-30 14:34:34.582409176 -0500
+@@ -4,6 +4,7 @@ import gnomekeyring
+ from oauth import oauth
+ from gwibber.microblog.util import log, resources
+ from gettext import lgettext as _
++from kitchen.text.converters import to_unicode
+ log.logger.name = "Twitter"
+
+ PROTOCOL_INFO = {
+@@ -75,20 +76,20 @@ class Client:
+ try:
+
+ m["mid"] = str(data["id"])
+- m["service"] = "twitter"
++ m["service"] = u"twitter"
+ m["account"] = self.account["id"]
+ if data.has_key("created_at"):
+ m["time"] = util.parsetime(data["created_at"])
+- m["text"] = util.unescape(data["text"])
++ m["text"] = to_unicode(util.unescape(data["text"]))
+ m["to_me"] = ("@%s" % self.account["username"]) in data["text"]
+
+- m["html"] = util.linkify(m["text"],
++ m["html"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % URL_PREFIX),
+- (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX)), escape=False)
++ (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX)), escape=False))
+
+- m["content"] = util.linkify(m["text"],
++ m["content"] = to_unicode(util.linkify(m["text"],
+ ((util.PARSE_HASH, '#<a href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
+- (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True)
++ (util.PARSE_NICK, '@<a href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=True))
+
+ m["favorited"] = data.get("favorited", False)
+
+@@ -104,19 +105,19 @@ class Client:
+
+ def _user(self, user):
+ return {
+- "name": user.get("name", None),
+- "nick": user.get("screen_name", None),
++ "name": to_unicode(user.get("name", None)),
++ "nick": to_unicode(user.get("screen_name", None)),
+ "id": user.get("id", None),
+- "location": user.get("location", None),
++ "location": to_unicode(user.get("location", None)),
+ "followers": user.get("followers_count", None),
+ "friends": user.get("friends_count", None),
+- "description": user.get("description", None),
++ "description": to_unicode(user.get("description", None)),
+ "following": user.get("following", None),
+ "protected": user.get("protected", None),
+ "statuses": user.get("statuses_count", None),
+- "image": user.get("profile_image_url", None),
+- "website": user.get("url", None),
+- "url": "/".join((URL_PREFIX, user.get("screen_name", ""))) or None,
++ "image": to_unicode(user.get("profile_image_url", None)),
++ "website": to_unicode(user.get("url", None)),
++ "url": to_unicode("/".join((URL_PREFIX, user.get("screen_name", "")))) or None,
+ "is_me": user.get("screen_name", None) == self.account["username"],
+ }
+
+@@ -146,14 +147,14 @@ class Client:
+ if data["in_reply_to_status_id"]:
+ m["reply"] = {}
+ m["reply"]["id"] = data["in_reply_to_status_id"]
+- m["reply"]["nick"] = data["in_reply_to_screen_name"]
++ m["reply"]["nick"] = to_unicode(data["in_reply_to_screen_name"])
+ if m["reply"]["id"] and m["reply"]["nick"]:
+- m["reply"]["url"] = "/".join((URL_PREFIX, m["reply"]["nick"], "statuses", str(m["reply"]["id"])))
++ m["reply"]["url"] = to_unicode("/".join((URL_PREFIX, m["reply"]["nick"], "statuses", str(m["reply"]["id"]))))
+ else:
+ m["reply"]["url"] = None
+
+ m["sender"] = self._user(data["user"] if "user" in data else data["sender"])
+- m["url"] = "/".join((m["sender"]["url"], "statuses", str(m["mid"])))
++ m["url"] = to_unicode("/".join((m["sender"]["url"], "statuses", str(m["mid"]))))
+
+ return m
+
+@@ -162,12 +163,12 @@ class Client:
+ m["private"] = True
+
+ m["recipient"] = {}
+- m["recipient"]["name"] = data["recipient"]["name"]
+- m["recipient"]["nick"] = data["recipient"]["screen_name"]
++ m["recipient"]["name"] = to_unicode(data["recipient"]["name"])
++ m["recipient"]["nick"] = to_unicode(data["recipient"]["screen_name"])
+ m["recipient"]["id"] = data["recipient"]["id"]
+- m["recipient"]["image"] = data["recipient"]["profile_image_url"]
+- m["recipient"]["location"] = data["recipient"]["location"]
+- m["recipient"]["url"] = "/".join((URL_PREFIX, m["recipient"]["nick"]))
++ m["recipient"]["image"] = to_unicode(data["recipient"]["profile_image_url"])
++ m["recipient"]["location"] = to_unicode(data["recipient"]["location"])
++ m["recipient"]["url"] = to_unicode("/".join((URL_PREFIX, m["recipient"]["nick"])))
+ m["recipient"]["is_me"] = m["recipient"]["nick"] == self.account["username"]
+ m["to_me"] = m["recipient"]["is_me"]
+
+@@ -179,15 +180,15 @@ class Client:
+ if data["to_user_id"]:
+ m["reply"] = {}
+ m["reply"]["id"] = data["to_user_id"]
+- m["reply"]["nick"] = data["to_user"]
++ m["reply"]["nick"] = to_unicode(data["to_user"])
+
+ m["sender"] = {}
+- m["sender"]["nick"] = data["from_user"]
++ m["sender"]["nick"] = to_unicode(data["from_user"])
+ m["sender"]["id"] = data["from_user_id"]
+- m["sender"]["image"] = data["profile_image_url"]
+- m["sender"]["url"] = "/".join((URL_PREFIX, m["sender"]["nick"]))
++ m["sender"]["image"] = to_unicode(data["profile_image_url"])
++ m["sender"]["url"] = to_unicode("/".join((URL_PREFIX, m["sender"]["nick"])))
+ m["sender"]["is_me"] = m["sender"]["nick"] == self.account["username"]
+- m["url"] = "/".join((m["sender"]["url"], "statuses", str(m["mid"])))
++ m["url"] = to_unicode("/".join((m["sender"]["url"], "statuses", str(m["mid"]))))
+ return m
+
+ def _profile(self, data):
+@@ -197,20 +198,20 @@ class Client:
+ }
+ return {
+ "name": data.get("name", data["screen_name"]),
+- "service": "twitter",
++ "service": u"twitter",
+ "stream": "profile",
+ "account": self.account["id"],
+ "mid": data["id"],
+- "text": data.get("description", ""),
+- "nick": data["screen_name"],
+- "url": data.get("url", ""),
++ "text": to_unicode(data.get("description", "")),
++ "nick": to_unicode(data["screen_name"]),
++ "url": to_unicode(data.get("url", "")),
+ "protected": data.get("protected", False),
+ "statuses": data.get("statuses_count", 0),
+ "followers": data.get("followers_count", 0),
+ "friends": data.get("friends_count", 0),
+ "following": data.get("following", 0),
+ "favourites": data.get("favourites_count", 0),
+- "image": data["profile_image_url"],
++ "image": to_unicode(data["profile_image_url"]),
+ "utc_offset": data.get("utc_offset", 0),
+ "id": data["id"],
+ "lang": data.get("lang", "en"),
+@@ -222,23 +223,23 @@ class Client:
+ def _list(self, data):
+ return {
+ "mid": data["id"],
+- "service": "twitter",
++ "service": u"twitter",
+ "account": self.account["id"],
+ "time": 0,
+- "text": data["description"],
+- "html": data["description"],
+- "content": data["description"],
+- "url": "/".join((URL_PREFIX, data["uri"])),
+- "sender": self._user(data["user"]),
+- "name": data["name"],
+- "nick": data["slug"],
++ "text": to_unicode(data["description"]),
++ "html": to_unicode(data["description"]),
++ "content": to_unicode(data["description"]),
++ "url": to_unicode("/".join((URL_PREFIX, data["uri"]))),
++ "sender": to_unicode(self._user(data["user"])),
++ "name": to_unicode(data["name"]),
++ "nick": to_unicode(data["slug"]),
+ "key": data["slug"],
+- "full": data["full_name"],
+- "uri": data["uri"],
++ "full": to_unicode(data["full_name"]),
++ "uri": to_unicode(data["uri"]),
+ "mode": data["mode"],
+ "members": data["member_count"],
+ "followers": data["subscriber_count"],
+- "kind": "list",
++ "kind": u"list",
+ }
+
+ def _get(self, path, parse="message", post=False, single=False, **args):
diff --git a/gwibber-3.3.1.1-no-gtkspell.patch b/gwibber-3.3.1.1-no-gtkspell.patch
new file mode 100644
index 0000000..1464c77
--- /dev/null
+++ b/gwibber-3.3.1.1-no-gtkspell.patch
@@ -0,0 +1,14 @@
+diff -up gwibber-3.3.1.1/vapi/Makefile.am.no-gtkspell gwibber-3.3.1.1/vapi/Makefile.am
+--- gwibber-3.3.1.1/vapi/Makefile.am.no-gtkspell 2011-10-25 12:25:06.000000000 -0400
++++ gwibber-3.3.1.1/vapi/Makefile.am 2011-11-30 15:02:18.115657117 -0500
+@@ -1,5 +1,8 @@
+ EXTRA_DIST = \
+ config.vapi \
+ libnotify.deps \
+- libnotify.vapi \
+- gtkspell-2.0.vapi
++ libnotify.vapi
++
++if HAVE_GTKSPELL
++EXTRA_DIST += gtkspell-2.0.vapi
++endif
diff --git a/gwibber-3.3.1.1-no-unity.patch b/gwibber-3.3.1.1-no-unity.patch
new file mode 100644
index 0000000..22e0ffd
--- /dev/null
+++ b/gwibber-3.3.1.1-no-unity.patch
@@ -0,0 +1,18 @@
+diff -up gwibber-3.3.1.1/client/Makefile.am.no-unity gwibber-3.3.1.1/client/Makefile.am
+--- gwibber-3.3.1.1/client/Makefile.am.no-unity 2011-08-30 15:54:40.000000000 -0400
++++ gwibber-3.3.1.1/client/Makefile.am 2011-11-30 14:46:30.054431541 -0500
+@@ -69,10 +69,13 @@ gwibber_preferences_VALAFLAGS = \
+ --pkg atk \
+ --pkg glib-2.0 \
+ --pkg gmodule-2.0 \
+- --pkg Dbusmenu-0.4 \
+ $(MAINTAINER_VALAFLAGS) \
+ $(NULL)
+
++if HAVE_UNITY
++gwibber_preferences_VALAFLAGS += --pkg Dbusmenu-0.4
++endif
++
+ gwibber_preferences_LDADD = \
+ $(BASE_LIBS)
+
diff --git a/gwibber-3.3.1.1-sina.patch b/gwibber-3.3.1.1-sina.patch
new file mode 100644
index 0000000..bc9e716
--- /dev/null
+++ b/gwibber-3.3.1.1-sina.patch
@@ -0,0 +1,817 @@
+diff -up gwibber-3.3.1.1/configure.ac.sina gwibber-3.3.1.1/configure.ac
+--- gwibber-3.3.1.1/configure.ac.sina 2011-11-21 16:35:35.000000000 -0500
++++ gwibber-3.3.1.1/configure.ac 2011-11-30 14:37:56.447866098 -0500
+@@ -281,6 +281,10 @@ AC_CONFIG_FILES([
+ gwibber/microblog/plugins/qaiku/gtk/Makefile
+ gwibber/microblog/plugins/qaiku/gtk/qaiku/Makefile
+ gwibber/microblog/plugins/qaiku/ui/Makefile
++ gwibber/microblog/plugins/sina/Makefile
++ gwibber/microblog/plugins/sina/gtk/Makefile
++ gwibber/microblog/plugins/sina/gtk/sina/Makefile
++ gwibber/microblog/plugins/sina/ui/Makefile
+ gwibber/microblog/plugins/statusnet/Makefile
+ gwibber/microblog/plugins/statusnet/gtk/Makefile
+ gwibber/microblog/plugins/statusnet/gtk/statusnet/Makefile
+diff -up gwibber-3.3.1.1/data/icons/breakdance/16x16/Makefile.am.sina gwibber-3.3.1.1/data/icons/breakdance/16x16/Makefile.am
+--- gwibber-3.3.1.1/data/icons/breakdance/16x16/Makefile.am.sina 2011-06-23 16:39:09.000000000 -0400
++++ gwibber-3.3.1.1/data/icons/breakdance/16x16/Makefile.am 2011-11-30 14:37:56.447866098 -0500
+@@ -9,6 +9,7 @@ icon_DATA = brightkite.png \
+ openid.png \
+ pingfm.png \
+ qaiku.png \
++ sina.png \
+ statusnet.png \
+ stumbleupon.png \
+ twitter.png
+diff -up gwibber-3.3.1.1/data/icons/breakdance/22x22/Makefile.am.sina gwibber-3.3.1.1/data/icons/breakdance/22x22/Makefile.am
+--- gwibber-3.3.1.1/data/icons/breakdance/22x22/Makefile.am.sina 2011-06-23 16:39:09.000000000 -0400
++++ gwibber-3.3.1.1/data/icons/breakdance/22x22/Makefile.am 2011-11-30 14:37:56.447866098 -0500
+@@ -9,6 +9,7 @@ icon_DATA = brightkite.png \
+ openid.png \
+ pingfm.png \
+ qaiku.png \
++ sina.png \
+ statusnet.png \
+ stumbleupon.png \
+ twitter.png
+diff -up gwibber-3.3.1.1/data/icons/breakdance/32x32/Makefile.am.sina gwibber-3.3.1.1/data/icons/breakdance/32x32/Makefile.am
+--- gwibber-3.3.1.1/data/icons/breakdance/32x32/Makefile.am.sina 2011-06-23 16:39:09.000000000 -0400
++++ gwibber-3.3.1.1/data/icons/breakdance/32x32/Makefile.am 2011-11-30 14:37:56.447866098 -0500
+@@ -9,6 +9,7 @@ icon_DATA = brightkite.png \
+ openid.png \
+ pingfm.png \
+ qaiku.png \
++ sina.png \
+ statusnet.png \
+ stumbleupon.png \
+ twitter.png
+diff -up gwibber-3.3.1.1/data/icons/breakdance/scalable/Makefile.am.sina gwibber-3.3.1.1/data/icons/breakdance/scalable/Makefile.am
+--- gwibber-3.3.1.1/data/icons/breakdance/scalable/Makefile.am.sina 2011-06-23 16:39:09.000000000 -0400
++++ gwibber-3.3.1.1/data/icons/breakdance/scalable/Makefile.am 2011-11-30 14:37:56.448866086 -0500
+@@ -9,6 +9,7 @@ icon_DATA = brightkite.svg \
+ openid.svg \
+ pingfm.svg \
+ qaiku.svg \
++ sina.svg \
+ statusnet.svg \
+ stumbleupon.svg \
+ twitter.svg
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/Makefile.am.sina gwibber-3.3.1.1/gwibber/microblog/plugins/Makefile.am
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/Makefile.am.sina 2011-06-23 16:39:09.000000000 -0400
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/Makefile.am 2011-11-30 14:38:25.091505943 -0500
+@@ -1,4 +1,4 @@
+-SUBDIRS = buzz digg facebook flickr foursquare friendfeed identica pingfm qaiku statusnet twitter
++SUBDIRS = buzz digg facebook flickr foursquare friendfeed identica pingfm qaiku sina statusnet twitter
+
+ plugindir = $(datadir)/gwibber/plugins/
+ plugin_PYTHON = \
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/__init__.py.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/__init__.py.sina 2011-11-30 14:37:56.448866086 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/__init__.py 2011-11-30 14:37:56.448866086 -0500
+@@ -0,0 +1 @@
++
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/Makefile.am.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/Makefile.am
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/Makefile.am.sina 2011-11-30 14:37:56.449866074 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/Makefile.am 2011-11-30 14:37:56.449866074 -0500
+@@ -0,0 +1,7 @@
++SUBDIRS = sina
++plugindir = $(datadir)/gwibber/plugins/sina/gtk
++plugin_PYTHON = \
++ __init__.py
++
++uninstall-hook:
++ rm -fr $(plugindir)
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py.sina 2011-11-30 14:37:56.449866074 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/__init__.py 2011-11-30 14:37:56.449866074 -0500
+@@ -0,0 +1,174 @@
++import gtk, pango, webkit, gnomekeyring
++import urllib, urllib2, json, urlparse, uuid
++from oauth import oauth
++
++from gtk import Builder
++from gwibber.microblog.util import resources
++import gettext
++from gettext import gettext as _
++if hasattr(gettext, 'bind_textdomain_codeset'):
++ gettext.bind_textdomain_codeset('gwibber','UTF-8')
++gettext.textdomain('gwibber')
++
++gtk.gdk.threads_init()
++
++sigmeth = oauth.OAuthSignatureMethod_HMAC_SHA1()
++
++class AccountWidget(gtk.VBox):
++ """AccountWidget: A widget that provides a user interface for configuring sina accounts in Gwibber
++ """
++
++ def __init__(self, account=None, dialog=None):
++ """Creates the account pane for configuring Sina accounts"""
++ gtk.VBox.__init__( self, False, 20 )
++ self.ui = gtk.Builder()
++ self.ui.set_translation_domain ("gwibber")
++ self.ui.add_from_file (resources.get_ui_asset("gwibber-accounts-sina.ui"))
++ self.ui.connect_signals(self)
++ self.vbox_settings = self.ui.get_object("vbox_settings")
++ self.pack_start(self.vbox_settings, False, False)
++ self.show_all()
++
++ self.account = account or {}
++ self.dialog = dialog
++ has_secret_key = True
++ if self.account.has_key("id"):
++ try:
++ value = gnomekeyring.find_items_sync(gnomekeyring.ITEM_GENERIC_SECRET, {"id": str("%s/%s" % (self.account["id"], "secret_token"))})[0].secret
++ except gnomekeyring.NoMatchError:
++ has_secret_key = False
++
++ try:
++ if self.account.has_key("access_token") and self.account.has_key("secret_token") and self.account.has_key("username") and has_secret_key and not self.dialog.condition:
++ self.ui.get_object("hbox_sina_auth").hide()
++ self.ui.get_object("sina_auth_done_label").set_label(_("%s has been authorized by Sina") % self.account["username"])
++ self.ui.get_object("hbox_sina_auth_done").show()
++ else:
++ self.ui.get_object("hbox_sina_auth_done").hide()
++ if self.dialog.ui:
++ self.dialog.ui.get_object('vbox_create').hide()
++ except:
++ self.ui.get_object("hbox_sina_auth_done").hide()
++ if self.dialog.ui:
++ self.dialog.ui.get_object("vbox_create").hide()
++
++
++ def on_sina_auth_clicked(self, widget, data=None):
++ self.winsize = self.window.get_size()
++
++ web = webkit.WebView()
++ web.get_settings().set_property("enable-plugins", False)
++ web.load_html_string(_("<p>Please wait...</p>"), "file:///")
++
++ self.consumer = oauth.OAuthConsumer(*resources.get_sina_keys())
++
++ request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_method="POST",
++ http_url="http://api.t.sina.com.cn/oauth/request_token")
++
++ request.sign_request(sigmeth, self.consumer, token=None)
++
++ tokendata = urllib2.urlopen(request.http_url, request.to_postdata()).read()
++ self.token = oauth.OAuthToken.from_string(tokendata)
++
++ url = "http://api.t.sina.com.cn/oauth/authorize?oauth_token=%s&oauth_callback=%s&display=popup" % ( self.token.key, "http://gwibber.com/0/auth.html" )
++
++ web.load_uri(url)
++ web.set_size_request(550, 400)
++ web.connect("title-changed", self.on_sina_auth_title_change)
++
++ self.scroll = gtk.ScrolledWindow()
++ self.scroll.add(web)
++
++ self.pack_start(self.scroll, True, True, 0)
++ self.show_all()
++
++ self.ui.get_object("vbox1").hide()
++ self.ui.get_object("vbox_advanced").hide()
++ self.dialog.infobar.set_message_type(gtk.MESSAGE_INFO)
++
++ def on_sina_auth_title_change(self, web=None, title=None, data=None):
++ saved = False
++ if title.get_title() == "Success":
++
++ if hasattr(self.dialog, "infobar_content_area"):
++ for child in self.dialog.infobar_content_area.get_children(): child.destroy()
++ self.dialog.infobar_content_area = self.dialog.infobar.get_content_area()
++ self.dialog.infobar_content_area.show()
++ self.dialog.infobar.show()
++
++ message_label = gtk.Label(_("Verifying"))
++ message_label.set_use_markup(True)
++ message_label.set_ellipsize(pango.ELLIPSIZE_END)
++ self.dialog.infobar_content_area.add(message_label)
++ self.dialog.infobar.show_all()
++ self.scroll.hide()
++ url = web.get_main_frame().get_uri()
++ data = urlparse.parse_qs(url.split("?", 1)[1])
++
++ self.ui.get_object("vbox1").show()
++ self.ui.get_object("vbox_advanced").show()
++
++ token = data["oauth_token"][0]
++ verifier = data["oauth_verifier"][0]
++
++ request = oauth.OAuthRequest.from_consumer_and_token(
++ self.consumer, self.token,
++ http_url="http://api.t.sina.com.cn/oauth/access_token",
++ parameters={"oauth_verifier": str(verifier)})
++ request.sign_request(sigmeth, self.consumer, self.token)
++
++ tokendata = urllib2.urlopen(request.http_url, request.to_postdata()).read()
++ data = urlparse.parse_qs(tokendata)
++
++ atok = oauth.OAuthToken.from_string(tokendata)
++
++ self.account["access_token"] = data["oauth_token"][0]
++ self.account["secret_token"] = data["oauth_token_secret"][0]
++ self.account["username"] = data["screen_name"][0]
++ self.account["user_id"] = data["user_id"][0]
++
++ apireq = oauth.OAuthRequest.from_consumer_and_token(
++ self.consumer, atok,
++ http_method="GET",
++ http_url="http://api.t.sina.com.cn/account/verify_credentials.json", parameters=None)
++
++ apireq.sign_request(sigmeth, self.consumer, atok)
++
++ account_data = json.loads(urllib2.urlopen(apireq.to_url()).read())
++
++ if isinstance(account_data, dict):
++ if account_data.has_key("id"):
++ saved = self.dialog.on_edit_account_save()
++ else:
++ print "Failed"
++ self.dialog.infobar.set_message_type(gtk.MESSAGE_ERROR)
++ message_label.set_text(_("Authorization failed. Please try again."))
++ else:
++ print "Failed"
++ self.dialog.infobar.set_message_type(gtk.MESSAGE_ERROR)
++ message_label.set_text(_("Authorization failed. Please try again."))
++
++ if saved:
++ message_label.set_text(_("Successful"))
++ self.dialog.infobar.set_message_type(gtk.MESSAGE_INFO)
++ #self.dialog.infobar.hide()
++
++ self.ui.get_object("hbox_sina_auth").hide()
++ self.ui.get_object("sina_auth_done_label").set_label(_("%s has been authorized by Sina") % str(self.account["username"]))
++ self.ui.get_object("hbox_sina_auth_done").show()
++ if self.dialog.ui and self.account.has_key("id") and not saved:
++ self.dialog.ui.get_object("vbox_save").show()
++ elif self.dialog.ui and not saved:
++ self.dialog.ui.get_object("vbox_create").show()
++
++ self.window.resize(*self.winsize)
++
++ if title.get_title() == "Failure":
++ web.hide()
++ self.dialog.infobar.set_message_type(gtk.MESSAGE_ERROR)
++ message_label.set_text(_("Authorization failed. Please try again."))
++ self.dialog.infobar.show_all()
++
++ self.ui.get_object("vbox1").show()
++ self.ui.get_object("vbox_advanced").show()
++ self.window.resize(*self.winsize)
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/Makefile.am.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/Makefile.am
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/Makefile.am.sina 2011-11-30 14:37:56.449866074 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/gtk/sina/Makefile.am 2011-11-30 14:37:56.449866074 -0500
+@@ -0,0 +1,6 @@
++plugindir = $(datadir)/gwibber/plugins/sina/gtk/sina
++plugin_PYTHON = \
++ __init__.py
++
++uninstall-hook:
++ rm -fr $(plugindir)
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/__init__.py.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/__init__.py
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/__init__.py.sina 2011-11-30 14:37:56.449866074 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/__init__.py 2011-11-30 14:37:56.449866074 -0500
+@@ -0,0 +1,293 @@
++from gwibber.microblog import network, util
++from htmlentitydefs import name2codepoint
++import re
++import gnomekeyring
++from oauth import oauth
++from gwibber.microblog.util import log, resources
++from gettext import lgettext as _
++log.logger.name = "Sina"
++
++PROTOCOL_INFO = {
++ "name": "Sina",
++ "version": "1.0",
++
++ "config": [
++ "private:secret_token",
++ "access_token",
++ "username",
++ "color",
++ "receive_enabled",
++ "send_enabled",
++ ],
++
++ "authtype": "oauth1a",
++ "color": "#E61217",
++
++ "features": [
++ "send",
++ "receive",
++ "search",
++ "tag",
++ "reply",
++ "responses",
++ "private",
++ "public",
++ "delete",
++ "retweet",
++ "like",
++ "send_thread",
++ "send_private",
++ "user_messages",
++ "sinceid",
++ "lists",
++ "list",
++ ],
++
++ "default_streams": [
++ "receive",
++ "images",
++ "responses",
++ "private",
++ "lists",
++ ],
++}
++
++URL_PREFIX = "http://t.sina.com.cn"
++API_PREFIX = "http://api.t.sina.com.cn"
++
++def unescape(s):
++ return re.sub('&(%s);' % '|'.join(name2codepoint),
++ lambda m: unichr(name2codepoint[m.group(1)]), s)
++
++class Client:
++ def __init__(self, acct):
++ self.service = util.getbus("Service")
++ if acct.has_key("secret_token") and acct.has_key("password"): acct.pop("password")
++ self.account = acct
++
++ if not acct.has_key("access_token") and not acct.has_key("secret_token"):
++ return [{"error": {"type": "auth", "account": self.account, "message": _("Failed to find credentials")}}]
++
++ self.sigmethod = oauth.OAuthSignatureMethod_HMAC_SHA1()
++ self.consumer = oauth.OAuthConsumer(*util.resources.get_sina_keys())
++ self.token = oauth.OAuthToken(acct["access_token"], acct["secret_token"])
++
++ def _common(self, data):
++ m = {};
++ try:
++ m["mid"] = str(data["id"])
++ m["service"] = "sina"
++ m["account"] = self.account["id"]
++ m["time"] = util.parsetime(data["created_at"])
++ m["text"] = unescape(data["text"])
++ m["to_me"] = ("@%s" % self.account["username"]) in data["text"]
++
++ m["html"] = util.linkify(data["text"],
++ ((util.PARSE_HASH, '#<a class="hash" href="%s#search?q=\\1">\\1</a>' % URL_PREFIX),
++ (util.PARSE_NICK, '@<a class="nick" href="%s/\\1">\\1</a>' % URL_PREFIX)), escape=False)
++
++ m["content"] = util.linkify(data["text"],
++ ((util.PARSE_HASH, '#<a class="hash" href="gwibber:/tag?acct=%s&query=\\1">\\1</a>' % m["account"]),
++ (util.PARSE_NICK, '@<a class="nick" href="gwibber:/user?acct=%s&name=\\1">\\1</a>' % m["account"])), escape=False)
++
++ if data.has_key("retweeted_status"):
++ m["retweeted_status"] = data["retweeted_status"]
++ else:
++ m["retweeted_status"] = None
++
++ images = util.imgpreview(m["text"])
++ if images:
++ m["images"] = images
++ m["type"] = "photo"
++ except:
++ log.logger.error("%s failure - %s", PROTOCOL_INFO["name"], data)
++ return {}
++
++ return m
++
++ def _user(self, user):
++ return {
++ "name": user["name"],
++ "nick": user["screen_name"],
++ "id": user["id"],
++ "location": user["location"],
++ "followers": user.get("followers", None),
++ "image": user["profile_image_url"],
++ "url": "/".join((URL_PREFIX, user["screen_name"])),
++ "is_me": user["screen_name"] == self.account["username"],
++ }
++
++ def _message(self, data):
++ if type(data) == type(None):
++ return []
++
++ m = self._common(data)
++ m["source"] = data.get("source", False)
++
++ if data.has_key("in_reply_to_status_id"):
++ if data["in_reply_to_status_id"]:
++ m["reply"] = {}
++ m["reply"]["id"] = data["in_reply_to_status_id"]
++ m["reply"]["nick"] = data["in_reply_to_screen_name"]
++ if m["reply"]["id"] and m["reply"]["nick"]:
++ m["reply"]["url"] = "/".join((URL_PREFIX, m["reply"]["nick"], "statuses", str(m["reply"]["id"])))
++ else:
++ m["reply"]["url"] = None
++
++ m["sender"] = self._user(data["user"] if "user" in data else data["sender"])
++ m["url"] = "/".join((m["sender"]["url"], "statuses", str(m["mid"])))
++
++ return m
++
++ def _private(self, data):
++ m = self._message(data)
++ m["private"] = True
++
++ m["recipient"] = {}
++ m["recipient"]["name"] = data["recipient"]["name"]
++ m["recipient"]["nick"] = data["recipient"]["screen_name"]
++ m["recipient"]["id"] = data["recipient"]["id"]
++ m["recipient"]["image"] = data["recipient"]["profile_image_url"]
++ m["recipient"]["location"] = data["recipient"]["location"]
++ m["recipient"]["url"] = "/".join((URL_PREFIX, m["recipient"]["nick"]))
++ m["recipient"]["is_me"] = m["recipient"]["nick"] == self.account["username"]
++ m["to_me"] = m["recipient"]["is_me"]
++
++ return m
++
++ def _result(self, data):
++ m = self._common(data)
++
++ if data["to_user_id"]:
++ m["reply"] = {}
++ m["reply"]["id"] = data["to_user_id"]
++ m["reply"]["nick"] = data["to_user"]
++
++ m["sender"] = {}
++ m["sender"]["nick"] = data["from_user"]
++ m["sender"]["id"] = data["from_user_id"]
++ m["sender"]["image"] = data["profile_image_url"]
++ m["sender"]["url"] = "/".join((URL_PREFIX, m["sender"]["nick"]))
++ m["sender"]["is_me"] = m["sender"]["nick"] == self.account["username"]
++ m["url"] = "/".join((m["sender"]["url"], "statuses", str(m["mid"])))
++ return m
++
++ def _list(self, data):
++ return {
++ "mid": data["id"],
++ "service": "sina",
++ "account": self.account["id"],
++ "time": 0,
++ "text": data["description"],
++ "html": data["description"],
++ "content": data["description"],
++ "url": "/".join((URL_PREFIX, data["uri"])),
++ "sender": self._user(data["user"]),
++ "name": data["name"],
++ "nick": data["slug"],
++ "key": data["slug"],
++ "full": data["full_name"],
++ "uri": data["uri"],
++ "mode": data["mode"],
++ "members": data["member_count"],
++ "followers": data["subscriber_count"],
++ "kind": "list",
++ }
++
++ def _get(self, path, parse="message", post=False, single=False, **args):
++ url = "/".join((API_PREFIX, path))
++
++ request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, self.token,
++ http_method="POST" if post else "GET", http_url=url, parameters=util.compact(args))
++ request.sign_request(self.sigmethod, self.consumer, self.token)
++
++ if post:
++ data = network.Download(request.http_url, None, post, body=request.to_postdata()).get_json()
++ #data = network.Download(request.to_url(), util.compact(args), post).get_json()
++ else:
++ data = network.Download(request.to_url(), None, post).get_json()
++
++ resources.dump(self.account["service"], self.account["id"], data)
++
++ if isinstance(data, dict) and data.get("errors", 0):
++ if "authenticate" in data["errors"][0]["message"]:
++ logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Authentication failed"), error["message"])
++ log.logger.error("%s", logstr)
++ return [{"error": {"type": "auth", "account": self.account, "message": data["errors"][0]["message"]}}]
++ else:
++ for error in data["errors"]:
++ logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Unknown failure"), error["message"])
++ return [{"error": {"type": "unknown", "account": self.account, "message": error["message"]}}]
++ elif isinstance(data, dict) and data.get("error", 0):
++ if "Incorrect signature" in data["error"]:
++ logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Request failed"), data["error"])
++ log.logger.error("%s", logstr)
++ return [{"error": {"type": "auth", "account": self.account, "message": data["error"]}}]
++ elif isinstance(data, str):
++ logstr = """%s: %s - %s""" % (PROTOCOL_INFO["name"], _("Request failed"), data)
++ log.logger.error("%s", logstr)
++ return [{"error": {"type": "request", "account": self.account, "message": data}}]
++
++ if parse == "list":
++ return [self._list(l) for l in data["lists"]]
++ if single: return [getattr(self, "_%s" % parse)(data)]
++ if parse: return [getattr(self, "_%s" % parse)(m) for m in data]
++ else: return []
++
++ def _search(self, **args):
++ data = network.Download("http://api.t.sina.com.cn/search.json", util.compact(args))
++ data = data.get_json()["results"]
++
++ return [self._result(m) for m in data]
++
++ def __call__(self, opname, **args):
++ return getattr(self, opname)(**args)
++
++ def receive(self, count=util.COUNT, since=None):
++ return self._get("statuses/home_timeline.json", count=count, since_id=since)
++
++ def user_messages(self, id=None, count=util.COUNT, since=None):
++ return self._get("statuses/user_timeline.json", id=id, count=count, since_id=since)
++
++ def responses(self, count=util.COUNT, since=None):
++ return self._get("statuses/mentions.json", count=count, since_id=since)
++
++ def private(self, count=util.COUNT, since=None):
++ private = self._get("direct_messages.json", "private", count=count, since_id=since) or []
++ private_sent = self._get("direct_messages/sent.json", "private", count=count, since_id=since) or []
++ return private + private_sent
++
++ def public(self):
++ return self._get("statuses/public_timeline.json")
++
++ def lists(self, **args):
++ following = self._get("%s/lists/subscriptions.json" % self.account["username"], "list") or []
++ lists = self._get("%s/lists.json" % self.account["username"], "list") or []
++ return following + lists
++
++ def list(self, user, id, count=util.COUNT, since=None):
++ return self._get("%s/lists/%s/statuses.json" % (user, id), per_page=count, since_id=since)
++
++ def search(self, query, count=util.COUNT, since=None):
++ return self._search(q=query, rpp=count, since_id=since)
++
++ def tag(self, query, count=util.COUNT, since=None):
++ return self._search(q="#%s" % query, count=count, since_id=since)
++
++ def delete(self, message):
++ return self._get("statuses/destroy/%s.json" % message["mid"], None, post=True, do=1)
++
++ def like(self, message):
++ return self._get("favorites/create/%s.json" % message["mid"], None, post=True, do=1)
++
++ def send(self, message):
++ return self._get("statuses/update.json", post=True, single=True,
++ status=message)
++
++ def send_private(self, message, private):
++ return self._get("direct_messages/new.json", "private", post=True, single=True,
++ text=message, screen_name=private["sender"]["nick"])
++
++ def send_thread(self, message, target):
++ return self._get("statuses/update.json", post=True, single=True,
++ status=message, in_reply_to_status_id=target["mid"])
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/Makefile.am.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/Makefile.am
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/Makefile.am.sina 2011-11-30 14:37:56.449866074 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/Makefile.am 2011-11-30 14:37:56.449866074 -0500
+@@ -0,0 +1,7 @@
++SUBDIRS = gtk ui
++plugindir = $(datadir)/gwibber/plugins/sina
++plugin_PYTHON = \
++ __init__.py
++
++uninstall-hook:
++ rm -fr $(plugindir)
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui.sina 2011-11-30 14:37:56.450866062 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui 2011-11-30 14:37:56.450866062 -0500
+@@ -0,0 +1,177 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<interface>
++ <requires lib="gtk+" version="2.16"/>
++ <!-- interface-naming-policy toplevel-contextual -->
++ <object class="GtkVBox" id="vbox_settings">
++ <property name="visible">True</property>
++ <property name="spacing">6</property>
++ <child>
++ <object class="GtkVBox" id="vbox1">
++ <property name="visible">True</property>
++ <child>
++ <object class="GtkHBox" id="hbox_sina_auth">
++ <property name="visible">True</property>
++ <child>
++ <object class="GtkButton" id="sina_auth_button">
++ <property name="label" translatable="yes">_Authorize</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">True</property>
++ <property name="use_underline">True</property>
++ <signal name="clicked" handler="on_sina_auth_clicked"/>
++ </object>
++ <packing>
++ <property name="fill">False</property>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkLabel" id="sina_auth_label">
++ <property name="visible">True</property>
++ <property name="label" translatable="yes">Authorize with sina</property>
++ </object>
++ <packing>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkHBox" id="hbox_sina_auth_done">
++ <property name="visible">True</property>
++ <child>
++ <object class="GtkLabel" id="sina_auth_done_label">
++ <property name="visible">True</property>
++ <property name="label" translatable="yes">Sina authorized</property>
++ </object>
++ <packing>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkHSeparator" id="hseparator1">
++ <property name="visible">True</property>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkVBox" id="vbox_advanced">
++ <property name="visible">True</property>
++ <property name="spacing">6</property>
++ <child>
++ <object class="GtkLabel" id="label3">
++ <property name="visible">True</property>
++ <property name="xalign">0</property>
++ <property name="label" translatable="yes">Account Settings:</property>
++ <attributes>
++ <attribute name="weight" value="bold"/>
++ </attributes>
++ </object>
++ <packing>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkTable" id="table_advanced_settings">
++ <property name="visible">True</property>
++ <property name="n_rows">2</property>
++ <property name="n_columns">3</property>
++ <property name="column_spacing">12</property>
++ <property name="row_spacing">6</property>
++ <child>
++ <object class="GtkCheckButton" id="send_enabled">
++ <property name="label" translatable="yes">_Send Messages</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="tooltip_text" translatable="yes">Allow sending posts to this account</property>
++ <property name="use_underline">True</property>
++ <property name="active">True</property>
++ <property name="draw_indicator">True</property>
++ </object>
++ <packing>
++ <property name="right_attach">3</property>
++ <property name="top_attach">1</property>
++ <property name="bottom_attach">2</property>
++ <property name="x_options">GTK_FILL</property>
++ <property name="y_options"></property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkCheckButton" id="receive_enabled">
++ <property name="label" translatable="yes">_Receive Messages</property>
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">False</property>
++ <property name="tooltip_text" translatable="yes">Include this account when downloading messages</property>
++ <property name="use_underline">True</property>
++ <property name="active">True</property>
++ <property name="draw_indicator">True</property>
++ </object>
++ <packing>
++ <property name="right_attach">3</property>
++ <property name="x_options">GTK_FILL</property>
++ <property name="y_options"></property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkHBox" id="hbox1">
++ <property name="visible">True</property>
++ <property name="homogeneous">True</property>
++ <child>
++ <object class="GtkLabel" id="label4">
++ <property name="visible">True</property>
++ <property name="tooltip_text" translatable="yes">Color used to help distinguish accounts</property>
++ <property name="xalign">0</property>
++ <property name="label" translatable="yes">Account Color:</property>
++ </object>
++ <packing>
++ <property name="position">0</property>
++ </packing>
++ </child>
++ <child>
++ <object class="GtkColorButton" id="color">
++ <property name="visible">True</property>
++ <property name="can_focus">True</property>
++ <property name="receives_default">True</property>
++ <property name="tooltip_text" translatable="yes">Color used to help distinguish accounts</property>
++ <property name="color">#000000000000</property>
++ </object>
++ <packing>
++ <property name="expand">False</property>
++ <property name="position">1</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">2</property>
++ </packing>
++ </child>
++ </object>
++ <packing>
++ <property name="position">2</property>
++ </packing>
++ </child>
++ </object>
++</interface>
+diff -up gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/Makefile.am.sina gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/Makefile.am
+--- gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/Makefile.am.sina 2011-11-30 14:37:56.450866062 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/plugins/sina/ui/Makefile.am 2011-11-30 14:37:56.450866062 -0500
+@@ -0,0 +1,8 @@
++plugindir = $(datadir)/gwibber/plugins/sina/ui
++plugin_DATA = \
++ gwibber-accounts-sina.ui
++
++uninstall-hook:
++ rm -fr $(plugindir)
++
++EXTRA_DIST = $(plugin_DATA)
+diff -up gwibber-3.3.1.1/gwibber/microblog/util/const.py.in.sina gwibber-3.3.1.1/gwibber/microblog/util/const.py.in
+--- gwibber-3.3.1.1/gwibber/microblog/util/const.py.in.sina 2011-11-30 14:37:56.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/util/const.py.in 2011-11-30 14:39:05.793994449 -0500
+@@ -15,6 +15,9 @@ else:
+ TWITTER_OAUTH_KEY = "qMBra1U4bpNYvDz947M5Q"
+ TWITTER_OAUTH_SECRET = "Lzdkhg0WvGYFzD9tnsuwC0zYmpJ4z7HrZl3yOxU1g"
+
++SINA_OAUTH_KEY = "4014350411"
++SINA_OAUTH_SECRET = "92e7877ad12d59a8410d850c19787701"
++
+ # Gwibber
+ MAX_MESSAGE_LENGTH = 140
+ MAX_MESSAGE_COUNT = 20000
+diff -up gwibber-3.3.1.1/gwibber/microblog/util/resources.py.sina gwibber-3.3.1.1/gwibber/microblog/util/resources.py
+--- gwibber-3.3.1.1/gwibber/microblog/util/resources.py.sina 2011-11-10 09:40:58.000000000 -0500
++++ gwibber-3.3.1.1/gwibber/microblog/util/resources.py 2011-11-30 14:37:56.450866062 -0500
+@@ -83,6 +83,10 @@ def get_twitter_keys():
+ # Distros should register their own keys and not rely on the defaults
+ return TWITTER_OAUTH_KEY, TWITTER_OAUTH_SECRET
+
++def get_sina_keys():
++ # Distros should register their own keys and not rely on the defaults
++ return SINA_OAUTH_KEY, SINA_OAUTH_SECRET
++
+ def get_avatar_path(url):
+ avatar_cache_dir = realpath(join(CACHE_BASE_DIR, "gwibber", "avatars"))
+ if not isdir(avatar_cache_dir):
+diff -up gwibber-3.3.1.1/po/POTFILES.in.sina gwibber-3.3.1.1/po/POTFILES.in
+--- gwibber-3.3.1.1/po/POTFILES.in.sina 2011-11-21 09:24:35.000000000 -0500
++++ gwibber-3.3.1.1/po/POTFILES.in 2011-11-30 14:39:54.159387082 -0500
+@@ -50,6 +50,8 @@ gwibber/microblog/plugins/pingfm/gtk/pin
+ gwibber/microblog/plugins/qaiku/gtk/qaiku/__init__.py
+ gwibber/microblog/plugins/foursquare/__init__.py
+ gwibber/microblog/plugins/foursquare/gtk/foursquare/__init__.py
++gwibber/microblog/plugins/sina/__init__.py
++gwibber/microblog/plugins/sina/gtk/sina/__init__.py
+ gwibber/util.py
+ [type: gettext/glade] data/gwibber-accounts-dialog.ui
+ [type: gettext/glade] data/gwibber-preferences-dialog.ui
+@@ -64,4 +66,5 @@ gwibber/util.py
+ [type: gettext/glade] gwibber/microblog/plugins/twitter/ui/gwibber-accounts-twitter.ui
+ [type: gettext/glade] gwibber/microblog/plugins/qaiku/ui/gwibber-accounts-qaiku.ui
+ [type: gettext/glade] gwibber/microblog/plugins/foursquare/ui/gwibber-accounts-foursquare.ui
++[type: gettext/glade] gwibber/microblog/plugins/sina/ui/gwibber-accounts-sina.ui
+ [type: gettext/ini] lens/data/gwibber.lens.in.in
diff --git a/gwibber-3.3.1.1-sqlite-catch_error.patch b/gwibber-3.3.1.1-sqlite-catch_error.patch
new file mode 100644
index 0000000..7287cb5
--- /dev/null
+++ b/gwibber-3.3.1.1-sqlite-catch_error.patch
@@ -0,0 +1,89 @@
+diff -up gwibber-3.3.1.1/gwibber/microblog/storage.py.catch_sqlite_error gwibber-3.3.1.1/gwibber/microblog/storage.py
+--- gwibber-3.3.1.1/gwibber/microblog/storage.py.catch_sqlite_error 2011-10-24 11:32:05.000000000 -0400
++++ gwibber-3.3.1.1/gwibber/microblog/storage.py 2011-11-30 14:41:50.687925563 -0500
+@@ -44,7 +44,10 @@ class MessageManager(dbus.service.Object
+ def setup_table(self):
+ with self.db:
+ schema = "rowid integer primary key autoincrement," + self.schema
+- self.db.execute("CREATE TABLE messages (%s)" % schema)
++ try:
++ self.db.execute("CREATE TABLE messages (%s)" % schema)
++ except sqlite3.OperationalError, msg:
++ log.logger.info("SQLite threw an error trying to setup the messages table: %s", msg)
+ self.db.execute("create unique index idx1 on messages (mid, account, operation, transient)")
+ self.db.execute("create index IF NOT EXISTS idx2 on messages (stream, time, transient)")
+
+@@ -63,10 +66,16 @@ class MessageManager(dbus.service.Object
+ log.logger.info("Found %d records in the messages stream for account %s", count, acct[0])
+ if count > 2000:
+ log.logger.info("Purging old data for %s", acct[0])
+- self.db.execute("DELETE FROM messages WHERE account = ? AND operation = 'receive' AND stream = 'messages' AND time IN (SELECT CAST (time AS int) FROM (SELECT time FROM messages WHERE account = ? AND operation = 'receive' AND stream = 'messages' AND time != 0 ORDER BY time ASC LIMIT (SELECT COUNT(time) FROM messages WHERE operation = 'receive' AND stream = 'messages' AND account = ? AND time != 0) - 2000) ORDER BY time ASC)", (acct[0],acct[0],acct[0]))
++ try:
++ self.db.execute("DELETE FROM messages WHERE account = ? AND operation = 'receive' AND stream = 'messages' AND time IN (SELECT CAST (time AS int) FROM (SELECT time FROM messages WHERE account = ? AND operation = 'receive' AND stream = 'messages' AND time != 0 ORDER BY time ASC LIMIT (SELECT COUNT(time) FROM messages WHERE operation = 'receive' AND stream = 'messages' AND account = ? AND time != 0) - 2000) ORDER BY time ASC)", (acct[0],acct[0],acct[0]))
++ except sqlite3.OperationalError, msg:
++ log.logger.info("DB Maintenance: SQLite threw an error: %s", msg)
+ except:
+ pass
+- self.db.execute("VACUUM")
++ try:
++ self.db.execute("VACUUM")
++ except sqlite3.OperationalError, msg:
++ log.logger.info("DB Maintenance: SQLite threw an error: %s", msg)
+ return
+
+
+@@ -158,14 +167,17 @@ class StreamManager(dbus.service.Object)
+
+ def setup_table(self):
+ with self.db:
+- self.db.execute("""
+- CREATE TABLE streams (
+- id text,
+- name text,
+- account text,
+- operation text,
+- data text)
+- """)
++ try:
++ self.db.execute("""
++ CREATE TABLE streams (
++ id text,
++ name text,
++ account text,
++ operation text,
++ data text)
++ """)
++ except sqlite3.OperationalError, msg:
++ log.logger.info("SQLite threw an error trying to setup the streams table: %s", msg)
+
+ @dbus.service.signal("com.Gwibber.Streams", signature="s")
+ def Updated(self, data):
+@@ -269,15 +281,19 @@ class AccountManager(dbus.service.Object
+
+ def setup_table(self):
+ with self.db:
+- self.db.execute("""
+- CREATE TABLE accounts (
+- id text,
+- service text,
+- username text,
+- color text,
+- send integer,
+- receive integer,
+- data text)""")
++ try:
++ self.db.execute("""
++ CREATE TABLE accounts (
++ id text,
++ service text,
++ username text,
++ color text,
++ send integer,
++ receive integer,
++ data text)""")
++ except sqlite3.OperationalError, msg:
++ log.logger.info("SQLite threw an error trying to setup the accounts table: %s", msg)
++
+
+ def refresh_password_cache(self):
+ for acct in json.loads(self.List()):
diff --git a/gwibber.spec b/gwibber.spec
index aa65506..dc69aa1 100644
--- a/gwibber.spec
+++ b/gwibber.spec
@@ -1,10 +1,8 @@
- %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
-
-%global bzr_rev 894
+%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
Name: gwibber
-Version: 3.1.0
-Release: 2%{?dist}
+Version: 3.3.1.1
+Release: 1%{?dist}
Epoch: 1
Summary: An open source microblogging client for GNOME developed with Python and GTK
Group: Applications/Internet
@@ -15,7 +13,7 @@ URL: https://launchpad.net/gwibber
# following command to generate the tarball:
# bzr export -r %{bzr_rev} gwibber-%{bzr_rev}bzr.tar.gz lp:gwibber
# Source0: %{name}-%{bzr_rev}bzr.tar.gz
-Source0: http://launchpad.net/gwibber/3.2/3.1.0/+download/%{name}-%{version}.tar.gz
+Source0: http://launchpad.net/gwibber/3.4/3.3.1.1/+download/%{name}-%{version}.tar.gz
# Fix situation where pango_overlay can be undefined
# https://bugs.launchpad.net/gwibber/+bug/577050
@@ -33,33 +31,21 @@ Patch3: gwibber-2.91.92-handle-x00-in-content.patch
# Oauth bits
Patch6: gwibber-832bzr-twitter-oauth.patch
-# Minimize characters with unicode equivs
-Patch10: gwibber-3.1.0-minimize-chars.patch
-
# Fix iter call to non-existent "mark", use "mark_start"
# bz635345 bz623754
Patch11: gwibber-3.1.0-undefined-mark.patch
# Use kitchen to force all strings generated by microblog inputs to unicode
-Patch13: gwibber-3.1.0-kitchen-unicode.patch
-
-# Right click on a stream in the navigation bar to close it.
-Patch14: gwibber-875bzr-right-click-to-close-stream.patch
+Patch13: gwibber-3.3.1.1-kitchen-unicode.patch
# Facebook Fedora key
-Patch15: gwibber-886bzr-facebook-fedora.patch
-
-# Fix hash tag parsing for unicode
-Patch19: gwibber-2.91.92-kitchen-unicode-hash.patch
-
-# Gowalla support
-Patch40: gwibber-3.0.0-gowalla.patch
-Patch41: gwibber-2.91.92-gowalla-icons.patch
-Patch42: gwibber-2.91.92-gowalla-kitchen.patch
+Patch15: gwibber-3.3.1.1-facebook-fedora.patch
# Sina support
-Patch43: gwibber-3.0.0.1-sina.patch
-Patch44: gwibber-3.0.0.1-sina-icons.patch
+Patch43: gwibber-3.3.1.1-sina.patch
+Patch44: gwibber-3.1.2-sina-icons.patch
+# TODO
+# Kitchen patch
# Catch errors trying to get Twitter Oauth token
Patch45: gwibber-3.0.0.1-twitter-catch_error.patch
@@ -71,10 +57,16 @@ Patch46: gwibber-3.0.0.1-empty_msg.patch
Patch47: gwibber-3.0.0.1-improve-about-dialog-handling.patch
# Catch sqlite errors (bz 702992 678015 699139 698074 700966)
-Patch48: gwibber-3.0.0.1-sqlite-catch_error.patch
+Patch48: gwibber-3.3.1.1-sqlite-catch_error.patch
# Overwrite default oauth key
-Patch49: gwibber-3.1.0-fix-twitter-oauth.patch
+Patch49: gwibber-3.3.1.1-fix-twitter-oauth.patch
+
+# No unity here, sorry.
+Patch51: gwibber-3.3.1.1-no-unity.patch
+
+# No gtkspell right now, still only gtk2 in Fedora
+Patch52: gwibber-3.3.1.1-no-gtkspell.patch
Requires: libsoup, python-pycurl
Requires: python
@@ -96,12 +88,25 @@ Requires: gnome-python2-libwnck
Requires: gnome-python2-gtkspell
Requires: python-kitchen
BuildRequires: python-devel, desktop-file-utils, python-distutils-extra, intltool, gettext
-BuildArch: noarch
+BuildRequires: gsettings-desktop-schemas-devel, json-glib-devel, libnotify-devel, dee-devel >= 0.5.19
+BuildRequires: libgee-devel, pango-devel, glib2-devel, gtk3-devel, libsoup-devel
+BuildRequires: libtool, autoconf, automake
+# This needs to be a gtk3 version.
+# BuildRequires: gtkspell-devel
%description
Gwibber is an open source microblogging client for GNOME developed with Python
-and GTK. It supports Twitter, Jaiku, Identi.ca, Facebook, and Digg.
+and GTK. It supports Twitter, Jaiku, Identi.ca, Facebook, Sina, Buzz
+and Digg.
+
+%package devel
+Summary: Development files for gwibber's libraries
+Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release}
+Requires: pkgconfig
+Group: Development/Libraries
+%description devel
+Development files for gwibber's libraries.
%prep
%setup -q
@@ -112,22 +117,12 @@ and GTK. It supports Twitter, Jaiku, Identi.ca, Facebook, and Digg.
%patch6 -p1 -b .oauth
-%patch10 -p1 -b .minimize
%patch11 -p1 -b .missing-mark
%patch13 -p1 -b .kitchen
-%patch14 -p1 -b .right-click
-
%patch15 -p1 -b .fedora
-%patch19 -p1 -b .kitchen-hash
-
-# Gowalla
-%patch40 -p1 -b .gowalla
-%patch41 -p1 -b .gowalla-icons
-%patch42 -p1 -b .gowalla-kitchen
-
# Sina
%patch43 -p1 -b .sina
%patch44 -p1 -b .sina-icons
@@ -145,48 +140,65 @@ and GTK. It supports Twitter, Jaiku, Identi.ca, Facebook, and Digg.
%patch48 -p1 -b .catch_sqlite_error
# Overwrite default oauth key for twitter
-%patch49 -p1
+%patch49 -p1 -b .fedora-twitter
-# sed -i -e '/^#! \?\//, 1d' $(find %{name} | grep "\.py$")
+# No unity here, sorry.
+%patch51 -p1 -b .no-unity
-%build
-%{__python} setup.py build
+# No gtkspell at the moment
+%patch52 -p1 -b .no-gtkspell
+%build
+NOCONFIGURE=true ./autogen.sh
+%configure --disable-unity --disable-static --disable-spell
+pushd libgwibber-gtk
+make clean
+popd
+make
%install
-%{__python} setup.py install --prefix %{_prefix} -O1 --skip-build --root %{buildroot}
-
-# Clean up patchfiles
-for i in `find %{buildroot} |grep "\.gowalla"`; do
- rm -f $i
-done
-for i in `find %{buildroot} |grep "\.sina"`; do
- rm -f $i
-done
-for i in `find %{buildroot} |grep "\.catch_error"`; do
- rm -f $i
-done
+make DESTDIR=%{buildroot} install
## Reinstall .desktop file
rm -rf %{buildroot}%{_datadir}/applications
-desktop-file-install --vendor="fedora" --dir=%{buildroot}%{_datadir}/applications build/share/applications/%{name}.desktop
+desktop-file-install --vendor="fedora" --dir=%{buildroot}%{_datadir}/applications data/%{name}.desktop
+
+rm -rf %{buildroot}%{_libdir}/*.la
## Install i18n data (THIS MUST COME LAST)
-cp -a build/mo %{buildroot}%{_datadir}/locale
+# cp -a build/mo %{buildroot}%{_datadir}/locale
%find_lang %{name}
+%post
+/sbin/ldconfig
+/bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :
+
+%postun
+if [ $1 -eq 0 ] ; then
+ /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
+ /bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null
+ /usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
+fi
+
+%posttrans
+/usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
+/usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
%files -f %{name}.lang
%doc AUTHORS COPYING README
%{python_sitelib}/%{name}
-%{python_sitelib}/%{name}-*.egg-info
%{_bindir}/%{name}
%{_bindir}/%{name}-accounts
%{_bindir}/%{name}-poster
%{_bindir}/%{name}-preferences
%{_bindir}/%{name}-service
%{_datadir}/%{name}
-%{_datadir}/pixmaps/%{name}.svg
+%{_datadir}/icons/hicolor/16x16/apps/%{name}.png
+%{_datadir}/icons/hicolor/24x24/apps/%{name}.png
+%{_datadir}/icons/hicolor/32x32/apps/%{name}.png
+%{_datadir}/icons/hicolor/48x48/apps/%{name}.png
+%{_datadir}/icons/hicolor/256x256/apps/%{name}.png
+%{_datadir}/icons/hicolor/scalable/apps/%{name}.svg
%{_datadir}/applications/fedora-%{name}.desktop
%{_datadir}/dbus-1/services/com.Gwibber.Accounts.service
%{_datadir}/dbus-1/services/com.Gwibber.Connection.service
@@ -195,10 +207,46 @@ cp -a build/mo %{buildroot}%{_datadir}/locale
%{_datadir}/dbus-1/services/com.Gwibber.Service.service
%{_datadir}/dbus-1/services/com.Gwibber.Streams.service
%{_datadir}/dbus-1/services/com.Gwibber.URLShorten.service
-%{_datadir}/dbus-1/services/com.GwibberClient.service
-%{_datadir}/indicators/messages/applications/gwibber
+%{_datadir}/GConf/gsettings/gwibber.convert
+%{_datadir}/glib-2.0/schemas/org.gwibber.gschema.xml
+%{_datadir}/indicators/messages/applications/gwibber.indicator
+%{_libdir}/libgwibber*.so.*
+%{_libdir}/girepository-1.0/Gwibber*
+%{_mandir}/man1/gwibber*
+
+%files devel
+%{_includedir}/libgwibber-*/
+%{_libdir}/libgwibber*.so
+%{_libdir}/pkgconfig/gwibber*.pc
+%{_datadir}/gir-1.0/Gwibber*
+%{_datadir}/vala/vapi/gwibber*
%changelog
+* Wed Nov 30 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.3.1.1-1
+- 3.3.1.1
+
+* Tue Aug 2 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.1.5-0.1.20110802bzr1086
+- bzr1086
+
+* Tue Jul 26 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.1.5-0.1.20110726bzr1079
+- bzr1079
+
+* Wed Jul 20 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.1.2-1.20110720bzr1057
+- bzr1057
+
+* Mon Jul 18 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.1.2-1.20110718bzr1053
+- update versioning (no epoch bump because previous builds with 3.2 tag were never released)
+- update to bzr1053
+
+* Fri Jul 15 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.2-0.1.20110715bzr1042
+- 1042
+
+* Thu Jul 14 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.2-0.1.20110713bzr1039
+- 1039bzr
+
+* Thu Jul 14 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.2-0.1.20110713bzr1037
+- rebase to trunk code
+
* Thu Jul 7 2011 Tom Callaway <spot at fedoraproject.org> - 1:3.1.0-2
- really use our oauth key
diff --git a/sources b/sources
index 5afb9d0..f4db20b 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-e022ad230bd7ef0c735069ba6407df0d gwibber-3.1.0.tar.gz
+484bf327257698a267ffc6684bed253b gwibber-3.3.1.1.tar.gz
More information about the scm-commits
mailing list