# Copyright 2006-2007 Nanorex, Inc. See LICENSE file for details. """ Sponsors.py - sponsors system, exporting PermissionDialog and SponsorableMixin @author: Will @version: $Id$ @copyright: 2006-2007 Nanorex, Inc. See LICENSE file for details. Motivation and design rationale: We want to recoup some of the costs of developing NanoEngineer-1 in a way consistent with its GPL licensing. One way to do that is to have sponsors, and to offer the sponsors advertising space in a way that doesn't bother the user. Some UI dialogs will have buttons with sponsor logos on them, and if you click on a sponsor logo button, you'll get more information and maybe a link to their website. There are no unsolicited pop-ups in this system. We want to be able to update sponsor information without asking the user to download a new version. So we have the program fetch recent sponsor information from our server. We don't want this to annoy the user, in terms of either network bandwidth or privacy concerns, so we have a permission dialog that explains what we're doing and asks the user for permission to do it. Module classification: Contains many levels of code, but exports only a widget and a widget-helper. Still, functionally it may belong in its own toplevel package. [bruce 071217] """ import base64 import md5 import os import random import re import socket import string import threading import types import urllib from xml.dom.minidom import parseString from PyQt4.Qt import QDialog from PyQt4.Qt import QImage from PyQt4.Qt import QPixmap from PyQt4.Qt import QSize from PyQt4.Qt import QIcon from PyQt4.Qt import QGridLayout from PyQt4.Qt import QTextBrowser from PyQt4.Qt import QPushButton from PyQt4.Qt import SIGNAL import foundation.env as env from utilities import debug_flags from platform_dependent.PlatformDependent import find_or_make_Nanorex_subdir from foundation.wiki_help import WikiHelpBrowser from utilities.debug import print_compact_stack, print_compact_traceback from utilities.qt4transition import qt4todo from utilities.prefs_constants import sponsor_download_permission_prefs_key from utilities.prefs_constants import sponsor_permanent_permission_prefs_key from utilities.prefs_constants import sponsor_md5_mismatch_flag_key from utilities.Log import redmsg, orangemsg, greenmsg from utilities.icon_utilities import geticon _sponsordir = find_or_make_Nanorex_subdir('Sponsors') _sponsors = { } # Include a trailing slash in the following sponsor server URLs. _sponsor_servers = \ ['http://nanoengineer-1.com/NE1_Sponsors/', #'file:///transfers/', ] def _fixHtml(rc): #bruce 071217 renamed this to be private startUrl=re.compile('\[') middleUrl=re.compile(' ') finishUrl=re.compile('\]') rc = string.replace(rc, '[P]', '

') rc = string.replace(rc, '[p]', '

') rc = string.replace(rc, '[ul]', '

') rc = string.replace(rc, '[li]', '
  • ') while True: m = startUrl.search(rc) if m == None: return rc s, e = m.start(), m.end() m = middleUrl.search(rc[e:]) s2, e2 = m.start() + e, m.end() + e m = finishUrl.search(rc[e2:]) s3, e3 = m.start() + e2, m.end() + e2 mid = "%s" % (rc[e:s2], rc[e2:s3]) rc = rc[:s] + mid + rc[e3:] class _Sponsor: #bruce 071217 renamed this to be private """ """ def __init__(self, name, text, imgfile): self.name = name self.text = text self.imgfile = imgfile def __repr__(self): return '<' + self.name + '>' def configureSponsorButton(self, btn): """ Load the image in the Sponsor button I{btn} that is displayed at the top of the Property Manager. @param btn: The sponsor button. @type btn: QToolButton """ qimg = QImage(self.imgfile) pixmap = QPixmap.fromImage(qimg) size = QSize(pixmap.width(), pixmap.height()) btn.setIconSize(size) btn.setIcon(QIcon(pixmap)) def wikiHelp(self): parent = env.mainwindow() w = WikiHelpBrowser(self.text,parent,caption=self.name) w.show() def _get_remote_file(filename, prefix): """ Get file I{filename} from the sponsor server. @param filename: the name of the file to get @type filename: string @param prefix: a short string that is expected at the beginning of the file for the retrieval to be denoted as successful @type prefix: string @return: gotContents, fileContents @rtype: gotContents - True if the file contents were successfully retrieved, False otherwise fileContents - the contents of the requested file """ # Try to connect for up to five seconds per host socket.setdefaulttimeout(5) fileContents = "" gotContents = False for host in _sponsor_servers: url = host + filename try: fileHandle = urllib.urlopen(url) fileContents = fileHandle.read() fileHandle.close() if fileContents.startswith(prefix): gotContents = True break except IOError: pass # Fail silently return gotContents, fileContents def _download_xml_file(xmlfile): (gotSponsorsFile, fileContents) = _get_remote_file("sponsors.xml", " See the NanoEngineer-1 wiki for tutorials and other information.

    See www.nanorex.com for more information about Nanorex.

    """ from utilities.icon_utilities import image_directory _sponsorImagePath = os.path.join( image_directory(), "ui/sponsors/nanorex.png") _defaultSponsor = _Sponsor('Nanorex', _fixHtml(_nanorexText), _sponsorImagePath) ############################################### class SponsorableMixin: """ To use this mixin class, instances of a main class which inherits it (which is typically a QDialog or PM_Dialog) should provide: - an attribute sponsor_keyword, which can be None, or a keyword string, or a list or tuple of sponsor keyword strings. - an attribute sponsor_btn, which must be a QPushButton object whose pixmap will be replaced with a sponsor logo during this mixin's __init__ or setSponsor methods. This button must already exist when our __init__ method is called. This mixin class then provides: - an __init__ method (which should only be called when the above attributes are ready) - a setSponsor method which may be called at any time after that, to change sponsors (might be useful whether or not sponsor_keyword has changed, since sponsors are sometimes chosen at random based on it, and the info used to choose them might have been updated) - an action method meant to be used by the caller as Qt slot methods, which can be named either 'sponsor_btn_clicked' or 'open_sponsor_homepage'. """ sponsor_keyword = None # Nanorex is the default sponsor. def __init__(self): self.setSponsor() def setSponsor(self): keyword = self.sponsor_keyword if type(keyword) in (types.ListType, types.TupleType): keyword = random.choice(keyword) try: sponsor = random.choice(_sponsors[keyword]) except KeyError: sponsor = _defaultSponsor self.__sponsor = sponsor sponsor.configureSponsorButton(self.sponsor_btn) def sponsor_btn_clicked(self): self.__sponsor.wikiHelp() def open_sponsor_homepage(self): self.__sponsor.wikiHelp() pass # end