Logo Search packages:      
Sourcecode: webboard version File versions

webboard.py

#!/usr/bin/env python
# WebBoard allows you to publish text on a public pastebin on the net
# This applications is based on the command line tool paste of 
# Dennis Kaarsemaker
#
#   (c) 2005 - Dennis Kaarsemaker <dennis@kaarsemaker.net>
#   (c) 2006 - Sebastian Heinlein <sebastian.heinlein@web.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


import sys
import os
import thread
import time
import datetime
import subprocess
import urllib
import xmlrpclib

try:
    import pygtk
    pygtk.require('2.0')
    import gtk
    import gtk.glade
    import pango
    import gtksourceview
    from re import compile
    import gettext
    import gobject
    from gettext import gettext as _
    gettext.bindtextdomain('webboard')
    gettext.textdomain('webboard')
    gtk.glade.bindtextdomain('webboard')
    gtk.glade.textdomain('webboard')
except:
    print "faild to import all modules"
    sys.exit(1)

try:
    import wbconfig
    from wbconfig import WebBoardConfig
except:
    import webboard.wbconfig
    from webboard.wbconfig import WebBoardConfig

class WebBoard:
    def __init__(self, config, history, clip=False, file=None):
        """
        Initialize the main window of webboard
        """
        icons = gtk.icon_theme_get_default()
        self.logo_pixbuf=icons.load_icon("gtk-paste", 32, 0)
        gtk.window_set_default_icon_list(self.logo_pixbuf)

        self.format = "text"

        self.glade = gtk.glade.XML(os.path.join(wbconfig.glade_dir,
                "webboard.glade"))
        self.glade.signal_autoconnect(self)

        # setup the gtksourceview
        self.lm = gtksourceview.SourceLanguagesManager()
        buffer = gtksourceview.SourceBuffer()
        buffer.set_data('languages-manager', self.lm)
        self.textview = gtksourceview.SourceView(buffer)
        gconf_mono_font = config.gconf.get_string("/desktop/gnome/interface/monospace_font_name")
        if gconf_mono_font :
            font = pango.FontDescription(gconf_mono_font)
            self.textview.modify_font(font)
        scroller = self.glade.get_widget("scrolledwindow_code")
        scroller.add(self.textview)

        self.language = "none"

        # The key used here is the format name as know by paste.debian.net
        # Fixme Not all format supported by paste.debian.net are listed
        self.langs = {
            "Plain"         : (_("Plain text"), "None"),
            "sh"            : ("Bash", "text/x-sh"),
            "python"        : ("Python", "text/x-python"),
            "c"             : ("C", "text/x-c"),
            "cpp"           : ("C++", "text/x-cpp"),
            "html4"         : ("HTML (4.0.1)", "text/html"),
            "java"          : ("Java", "text/x-java"),
            "javascript"    : ("Javascript", "text/x-javascript"),
            "perl"          : ("Perl", "text/x-perl"),
            "php"           : ("PHP", "text/x-php"),
            "sql"           : ("SQL", "text/x-sql"),
            "ada"           : ("Ada", "text/x-ada"),
            "apache"        : (_("Apache log file"), "None"),
            "asm"           : (_("ASM (NASM based)"), "None"),
            "aspvbs"        : ("Active Server Page", "None"),
            "dcl"           : ("CAD DCL", "None"),
            "lisp"          : ("CAD Lisp", "None"),
            "cs"            : ("C#", "text/x-csharp"),
            "css"           : ("CSS", "text/css"),
            "lisp"          : ("Lisp", "None"),
            "lua"           : ("Lua", "None"),
            "nsis"          : (_("NullSoft Installer"), "None"),
            "objc"          : (_("Objective C"), "text/x-c"),
            "oracle8"       : ("Oracle 8", "None"),
            "pascal"        : ("Pascal", "text/x-pascal"),
            "smarty"        : ("Smarty", "None"),
            "vb"            : ("Visual Basic", "text/x-vb"),
            "foxpro"        : ("Visual Fox Pro", "none"),
            "xml"           : ("XML", "text/xml")
        }

        lang_store = gtk.ListStore(gobject.TYPE_STRING,
                                   gobject.TYPE_STRING,
                                   gobject.TYPE_STRING)
        lang_store.set_sort_column_id(1, gtk.SORT_ASCENDING)
        for format in self.langs.keys():
            lang_store.append([format, self.langs[format][0],
                              self.langs[format][1]])

        self.combobox_syntax = self.glade.get_widget("combobox_syntax")
        cell = gtk.CellRendererText()
        self.combobox_syntax.pack_start(cell, True)
        self.combobox_syntax.add_attribute(cell, 'text', 1)

        self.combobox_syntax.set_model(lang_store)
        self.combobox_syntax.set_active(21)
        self.combobox_syntax.connect("changed", self.on_combobox_syntax_changed)
        
        # setup drag'n'drop
        self.textview.drag_dest_set(gtk.DEST_DEFAULT_ALL, \
                                    [('text/uri-list',0 , 0)], \
                                    gtk.gdk.ACTION_COPY)
        self.textview.connect("drag_motion", \
                              self.on_textview_drag_motion)
        self.textview.connect("drag_data_received", \
                              self.on_textview_drag_data_received)
        self.window_main = self.glade.get_widget("window_main")
        self.toolbutton_open = self.glade.get_widget("toolbutton_open")
        self.toolbutton_copy = self.glade.get_widget("toolbutton_copy")
        self.toolbutton_send = self.glade.get_widget("toolbutton_send")
        self.combobox_pastebin = self.glade.get_widget("combobox_pastebin")
        self.statusbar = self.glade.get_widget("statusbar")

        self.context = self.statusbar.get_context_id("context_webboard")

        self.clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)

        self.config = config
        self.config.add_notifier(self.on_config_changed)
        self.history = history

        self.tooltips = gtk.Tooltips()

        self.on_config_changed()

        self.buffer = self.textview.get_buffer()
        text = self.clipboard.wait_for_text()
        if (clip is False) or (text is None):
            self.buffer.set_text(self.welcome)
        else:
            self.buffer.set_text(text)
        self.buffer.select_range(self.buffer.get_start_iter(),
                                 self.buffer.get_end_iter())

        if file != None:
            self.open_file(file)

        #self.toolbutton_send.grab_default()
        self.textview.show()
        self.window_main.show()

        gtk.main()
        return

    def on_combobox_syntax_changed(self, combobox):
        """
        Set the selected synatx highlightenging for
        the source view
        """
        lang_store = combobox.get_model()
        iter = combobox.get_active_iter()
        mime = lang_store.get_value(iter, 2)
        self.language = lang_store.get_value(iter, 0).lower()
        buffer = self.textview.get_buffer()
        if mime == "None":
            buffer.set_highlight(False)
        else:
            try:
                language = self.lm.get_language_from_mime_type(mime)
                buffer.set_language(language)
                buffer.set_highlight(True)
            except:
                buffer.set_highlight(False)

    def _get_file_path_from_dnd_dropped_uri(self, uri):
        """
        Guess the correct file path of the dropped file
        """
        path = urllib.url2pathname(uri) # escape special chars
        path = path.strip('\r\n\x00') # remove \r\n and NULL
        # get the path to file
        if path.startswith('file:\\\\\\'): # windows
            path = path[8:] # 8 is len('file:///')
        elif path.startswith('file://'): # nautilus, rox
            path = path[7:] # 7 is len('file://')
        elif path.startswith('file:'): # xffm
            path = path[5:] # 5 is len('file:')
        return path
    
    def on_textview_drag_motion(self, widget, content, x, y, time):
        """
        Deselect all text in the text view, so that the drag'n'drop
        doesn't replace the selection
        """
        self.buffer.select_range(self.buffer.get_start_iter(),
                                 self.buffer.get_start_iter())

    def open_file(self, path):
        """
        Read the content of the specified file and write it to
        the text view
        """
        #FIXME: Should make use of async gnome vfs
        if os.path.exists(path):
            self.textview.get_buffer().set_text(file(path).read())

    def on_textview_drag_data_received(self, widget, context, x, y,
                                       selection, target_type, timestamp):
        uri = selection.data.strip()
        uri_splitted = uri.split()
        for uri in uri_splitted:
            path = self._get_file_path_from_dnd_dropped_uri(uri)
            self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
            while gtk.events_pending(): gtk.main_iteration()
            try:
                self.open_file(path)
            except IOError, e:
                pass
            self.window_main.window.set_cursor(None)

    def on_history_changed(self):
        self.history.update()

    def on_config_changed(self):
        # TRANSLATORS: tooltip of the button "publish"
        #              %s is the URL of the pastebin server
        self.tooltips.set_tip(self.toolbutton_send,
                              _("Publish the text on %s and copy the link "\
                                "to the relating website into the clipboard") \
                                % self.config.pastebin)
        # TRANSLATORS: default welcome text
        #              %s is the URL of the pastebin server
        self.welcome = _("Enter, copy or drag and drop source code and text "
                         "notes for publishing on %s") % self.config.pastebin
        # TRANSLATORS: The Window title - %s is the URL of the server
        self.window_main.set_title(_("WebBoard - %s") % self.config.pastebin)


    def on_window_main_destroy(self, widget):
        self.clipboard.store()
        gtk.main_quit()
        return

    def on_history_activate(self, widget, data):
        self.clipboard.set_text(data, len=-1)

    def on_button_preferences_clicked(self, widget=None):
        """
        Show the preferences window
        """
        self.config.preferences()

    def on_button_send_clicked(self, widget=None):
        """
        Lock the interface and run the send function
        in the background
        """
        self.statusbar.push(self.context,_("Publishing..."))
        self.window_main.set_sensitive(False)
        self.textview.set_sensitive(False)
        self.textview.set_editable(False)
        self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
        self.post = self.buffer.get_text(self.buffer.get_start_iter(),
                                             self.buffer.get_end_iter(), False)
        if self.post is "":
            self.statusbar.push(self.context,_("No text for publishing"))
        else:
            lock = thread.allocate_lock()
            lock.acquire()
            thread.start_new_thread(self.send, (self.post, lock))
            while lock.locked():
                time.sleep(0.05)
                while gtk.events_pending():
                    gtk.main_iteration()

            if self.ret is False:
                self.statusbar.push(self.context,_("Could not publish the text"\
                                                   " at %s") % self.config.pastebin)
                self.toolbutton_copy.set_sensitive(False)
                self.toolbutton_open.set_sensitive(False)
            else:
                self.url = "%s/%s" % (self.config.pastebin, self.ret)
                if not (self.url.startswith("http://") or
                        self.url.startswith("https://")):
                    self.url = "http://" + self.url
                # Write URL to the clipboard for easy paste
                self.statusbar.push(self.context,
                        _("Published at %s") % self.url)
                # set a nice title for the link
                text = self.post[:].lstrip(" \n\t").rstrip(" \n\t")
                text.replace("\n", "")
                if len(text) > 26:
                    text = "%s..." % text[0:20]
                # add to history
                now = datetime.datetime.now()
                stamp = time.mktime(now.timetuple())
                self.history.add(stamp , text, self.url)
                # copy link to the clipboard
                self.clipboard.set_text(self.url, len=-1)
                self.toolbutton_copy.set_sensitive(True)
                self.toolbutton_open.set_sensitive(True)

        self.window_main.window.set_cursor(None)
        self.window_main.set_sensitive(True)
        self.textview.set_editable(True)
        self.textview.set_sensitive(True)

    def on_menu_quit_activate(self, widget):
        self.window_main.destroy()

    def on_menu_info_activate(self, widget):
        wbconfig.about_info(self)

    def on_button_copy_clicked(self, widget):
        """
        Copy the current URL to the clipboard
        """
        self.clipboard.set_text(self.url, len=-1)

    def on_button_clear_clicked(self, widget):
        """
        Clear the textview
        """
        self.buffer.set_text("")

    def on_button_open_clicked(self, widget=None):
        """
        Open the URL of the latest pastebin publication in a browser window
        """
        if os.path.exists('/usr/bin/gnome-open'):
            command = ['gnome-open', self.url]
        else:
            command = ['x-www-browser', self.url]
        p = subprocess.Popen(command, close_fds=True, stdin=subprocess.PIPE,\
                             stdout=subprocess.PIPE)

    def send(self, post, lock, *args):
        """
        Send the post to the pastebin
        """
        self.ret = False
        pastebin = self.config.pastebin

        if not (pastebin[:7] == 'http://'):
            pastebin = 'http://' + pastebin
        
        rpcpath = '/server.pl' #FIXME Should be configurable
        server = xmlrpclib.Server(pastebin + rpcpath)
        
        # paste.debian.net limit name to 10 chars
        user = self.config.user[:10]
        print user

        # TODO : Guess format or allow user to choose it
        format = self.language

        try:
            #Handle error better
            rep = server.paste.addPaste(post, user, '', format)
            if rep['rc'] != 0 :
                print 'An error occured : %s ' % (rep['statusmessage'])
                self.ret = False
            else:
                #Some how display this message to the user
                print rep['statusmessage']
                print 'Code pasted at %s/%i' % (pastebin, rep['id'])
                self.ret =  str(rep['id'])
        except xmlrpclib.ProtocolError, err:
            print "A protocol error occurred"
            print "URL: %s" % err.url
            print "HTTP/HTTPS headers: %s" % err.headers
            print "Error code: %d" % err.errcode
            print "Error message: %s" % err.errmsg
            self.ret = False
        #except Exception, err:
            #print type(err)   
            #print err
            #self.ret = False
        finally:
            lock.release()


    def urlencode(self, data):
        """Encode all non-alphanumeric characters as hexadecimal codes"""
        out = ""
        for char in data:
            if char.isalnum() or char in ['-','_']:
                out += char
            else:
                char = hex(ord(char))[2:]
                if len(char) == 1:
                    char = "0" + char
                out += "%" + char
        return out

def main():
    config = WebBoardConfig()
    wb = WebBoard(config, clip=True)
    config.add_notifier(wb.on_config_changed)
    del wb
    del config

if __name__ == '__main__':
    main()


Generated by  Doxygen 1.6.0   Back to index