Beispiel #1
0
def action(req, fields):
    global MaxNewsItems

    req.write(InstDB.header("Menu"))

    box = InstDB.html_box()
    req.write(box.start("About"))
    req.write(InstDB.IncFile("about.inc"))
    req.write(box.end())

    req.write("<p>\n")

    InstDB.Cur.execute(
        """\
SELECT DATE_FORMAT(N.Date,'%%a, %%Y-%%m-%%d %%H:%%i:%%s') AS Date,
N.UserId, U.UserName, N.Title, N.Content FROM News AS N, Users AS U
WHERE N.UserId=U.UserId ORDER BY N.Date DESC LIMIT %s""", MaxNewsItems)

    for entry in InstDB.Cur.fetchall():
        req.write("""\
<p>
<font class=newstitle>%s</font><br>
<table width="100%%" border=0 cellpadding=4 cellspacing=0><tr class=lightbg><td>
Submitted by <a href="patches.py?Action=User&amp;UserId=%s">%s</a> on %s
<p>
%s
</td></tr></table>
""" % (entry["Title"], entry["UserId"], entry["UserName"], entry["Date"],
        entry["Content"]))

    req.write(InstDB.footer())
Beispiel #2
0
def action (req, fields):
  req.write (InstDB.header ("Initialize database"))

  if InstDB.Cur.execute ("SHOW tables") == 0:
    for fname in os.listdir (InstDB.BasePath + os.sep + "sql"):
      if fname[-4:] != ".sql": continue

      req.write ("Executing SQL in '%s'<br>\n" % fname)

      f = open (InstDB.BasePath + os.sep + "sql" + os.sep + fname, "r")
      sql = f.read ()

      # Split text file into individual queries
      for query in string.split (sql, ';')[:-1]:
        InstDB.Cur.execute (query)

    # Import categories file
    f = open (InstDB.BasePath + os.sep + "sql" + os.sep + "categories.txt", "r")
    for line in f.readlines():
      fields = string.split (line, "\t")
      InstDB.Cur.execute ("INSERT INTO Category (CategoryName, CategoryDescr)"
                          " VALUES (%s, %s)", (fields[0], fields[1]))

    # Import licenses file
    f = open (InstDB.BasePath + os.sep + "sql" + os.sep + "licenses.txt", "r")
    for line in f.readlines():
      fields = string.split (line, "\t")
      InstDB.Cur.execute ("INSERT INTO Licenses (LicenseName, LicenseDescr,"
                          " LicenseURL) VALUES (%s, %s, %s)",
                          (fields[0], fields[1], fields[2]))
  else:
    req.write ("Database already contains tables, nothing done")

  req.write (InstDB.footer ())
Beispiel #3
0
def action(req, fields):
    req.write(InstDB.header("Software/Credits"))

    box = InstDB.html_box()
    req.write(box.start("Software/Credits"))

    req.write(InstDB.IncFile("software.inc"))

    req.write(box.end())
    req.write(InstDB.footer())
Beispiel #4
0
def action(req, fields):
    Type = fields.get("Type", "Patch")
    if Type == "Inst": s = "Instruments"
    elif Type == "Sample": s = "Samples"
    else: s = "Patch Files"

    req.write(InstDB.header("List " + s))

    if Type == "Inst": list_insts(req, fields)
    elif Type == "Sample": list_samples(req, fields)
    else: list_patches(req, fields)

    req.write(InstDB.footer())
Beispiel #5
0
    def html_val(self, field, data):
        if self.fieldinfo.get(field, None): pass
        elif PatchHelper.fieldinfo.get(field, None):
            return PatchHelper.html_val(field, data)
        else:
            return None

        if field == "FormatStr":
            format = "%.1fkHz" % (int(data["SampleRate"]) / 1000.0)

            chan = int(data["Channels"])
            if chan == 1: format += " Mono"
            elif chan == 2: format += " Stereo"
            else: format += " %d-channel" % chan

            format += " " + data["Format"]

            return format

        elif field == "SampleNameLink":
            return "<a href=\"patches.py?Action=item&amp;ItemId=%d\">%s</a>" \
                   % (data["SampleId"], cgi.escape (data["SampleName"]))
        elif field == "SampleSize":
            return InstDB.pretty_size(data["SampleSize"])
        else:
            return ItemHelper.html_val(self, field, data)
Beispiel #6
0
    def html_val(self, field, data):
        if not self.fieldinfo.get(field, None): return None

        if field == "PropValue":
            return cgi.escape(InstDB.emailmunge(data["PropValue"]))
        else:
            return ItemHelper.html_val(self, field, data)
Beispiel #7
0
def init(req):
    InstDB.Req = req
    InstDB.Fields = util.FieldStorage(req)

    InstDB.connect()

    InstDB.User = None
    InstDB.Sess = Session.Session(req)
    if InstDB.Sess.is_new():
        login = InstDB.Fields.get("Username", None)
        passwd = InstDB.Fields.get("Pass", None)

        if login and passwd:  # lookup user by login name and password
            InstDB.Cur.execute(
                "SELECT * FROM Users WHERE Login=%s"
                " && Password=SHA1(%s)"
                " && !FIND_IN_SET('NoLogin', Flags)"
                " && !FIND_IN_SET('Confirm', Flags)",
                (login, passwd + InstDB.FuKraXor))
            InstDB.User = InstDB.Cur.fetchone()
            if InstDB.User:
                InstDB.Cur.execute("UPDATE Users SET LastLogin=NULL"
                                   " WHERE UserId=%s" % InstDB.User["UserId"])

                InstDB.Sess["UserId"] = InstDB.User["UserId"]
                InstDB.Sess.set_timeout(InstDB.SessTimeout)
                InstDB.Sess.save()

                InstDB.LoginError = ""
            else:
                InstDB.LoginError = "Invalid login"

        if not InstDB.Sess.has_key("UserId"): InstDB.Sess["UserId"] = 0
    else:  # Sess is not new
        InstDB.Sess.load()  # Load session values
        InstDB.Cur.execute(
            "SELECT * FROM Users WHERE UserId=%s"
            " && !FIND_IN_SET('NoLogin', Flags)", (InstDB.Sess["UserId"], ))
        InstDB.User = InstDB.Cur.fetchone()
        if InstDB.User:
            del InstDB.User["Password"]  # no need to keep it around
            InstDB.LoginError = ""
        else:
            InstDB.Sess["UserId"] = 0
            InstDB.Sess.delete()
Beispiel #8
0
def query_matches(query, matches, matchstr):
    left = max_sub_matches - len(matches)
    if left <= 0: return matches

    count = InstDB.Cur.execute(query)
    if count == 0: return matches
    if count > left: count = left

    for i in range(0, count):
        row = InstDB.Cur.fetchone()
        matches += ((cgi.escape(row['type']), cgi.escape(row['name']),
                     mark_match(InstDB.emailmunge(row['value']), matchstr)), )
    return matches
Beispiel #9
0
def add_extra_props(req, props):
    tab = ()
    if props: tab += (('<center>Additional Properties</center>', ), )

    # Add all the properties to a hash
    propvals = {}
    for prop in props:
        propvals[string.capitalize (prop["PropName"])] \
            = cgi.escape (InstDB.emailmunge (prop["PropValue"]))

    # Sort the property name keys
    sortkeys = propvals.keys()
    sortkeys.sort()

    # Add all the properties to the table
    for key in sortkeys:
        tab += ((key, propvals[key]), )

    return tab
Beispiel #10
0
def action(req, fields):
    id = int(fields.get("ItemId", 0))

    req.write(InstDB.header("Patch file details"))

    InstDB.Cur.execute("SELECT State,UserId FROM PatchInfo WHERE PatchId=%s",
                       id)
    row = InstDB.Cur.fetchone()
    if not row:
        InstDB.error(req, "Patch file with ID '%d' not found" % id)
        req.write(InstDB.footer())
        return

    # See if we are allowed to view this patch
    if row["State"] != "Active" \
            and (not InstDB.User or not (row["UserId"] == InstDB.User["UserId"]
                                         or "Admin" in InstDB.User["Flags"])):
        InstDB.error(req, "You don't have permission to view that info")
        req.write(InstDB.footer())
        return

    show_patch_item(id, fields, req)

    req.write(InstDB.footer())
Beispiel #11
0
def action (req, fields):
    topic = fields.get ("Topic", None)

    mini = fields.get ("Mini", '0')
    if mini != '0': mini = True
    else: mini = False

    title = topic_titles.get (topic, "Help")

    req.write (InstDB.header (title, mini))

    if not topic_titles.get (topic, None):
        InstDB.error (req, "Help topic not found!")
        req.write (InstDB.footer (mini))
        return

    box = InstDB.html_box ()
    req.write (box.start (title))
    req.write (InstDB.IncFile ("%s.inc" % topic))
    req.write (box.end ())

    req.write (InstDB.footer (mini))
Beispiel #12
0
def edit_patch_item(id, fields, req):
    itemCells = PatchHelper.edit_cell_format
    itemFields = InstDB.cells_to_fields(itemCells)

    InstDB.Cur.execute("SELECT PropId, PropName FROM Props ORDER BY PropName")
    propnames = InstDB.Cur.fetchall()

    # Was Save button clicked? - Save field values
    if fields.get("Save", None):
        update = PatchHelper.sql_update(itemFields, fields)
        update.where = ("PatchId=%d" % id, )
        InstDB.Cur.execute(update.query())

        props_update(id, fields)

    sel = PatchHelper.sql_select(itemFields)
    sel.where.insert(0, "PatchInfo.PatchId=%d" % id)
    InstDB.Cur.execute(sel.query())

    row = InstDB.Cur.fetchone()
    if not row:  # Shouldn't happen, thats why we raise an exception
        raise LookupError, "Patch file with ID '%d' not found" % id

    sel = props_select(id)
    InstDB.Cur.execute(sel.query())
    props = InstDB.Cur.fetchall()

    # Get count of Instruments in this patch
    InstDB.Cur.execute("SELECT COUNT(*) AS Count FROM InstInfo"
                       " WHERE PatchId=%s" % id)
    instcount = InstDB.Cur.fetchone()["Count"]

    # Get count of samples in this patch
    InstDB.Cur.execute("SELECT COUNT(*) AS Count FROM SampleInfo"
                       " WHERE PatchId=%s" % id)
    samplecount = InstDB.Cur.fetchone()["Count"]

    tab = PatchHelper.form_cell_vals(itemCells, row)

    if instcount > 0:
        trow = ('<a href="patches.py?Action=list&amp;Type=Inst&amp;PatchId=%s'
                '&amp;OrderBy=Bank">Instruments</a>' % id, instcount)
    else:
        trow = ("Instruments", "None")

    if samplecount > 0:
        trow += (
            '<a href="patches.py?Action=list&amp;Type=Sample&amp;PatchId=%s'
            '&amp;OrderBy=SampleNameLink">Samples</a>' % id, samplecount)
    else:
        trow += ("Samples", "None")

    tab += ((trow, ))
    tab += props_form_cells(props)

    req.write('<form action="" method=POST>\n')

    # Display patch item edit form
    box = InstDB.html_box()

    box.titlehtml = '\n&nbsp;&nbsp;&nbsp;<a href="patches.py?Action=item&amp;ItemId=%d">' \
                '<img src="images/view.png" alt="View Mode" width=15 height=15 border=0>' \
                '&nbsp;<font class="boxtitle">View</font></a>\n' % id   # Show edit icon

    req.write(box.start("Patch File"))

    split = InstDB.tabsplits(tab)
    req.write(split.render())

    req.write("<center><input type=submit name=Save value=\"Save\">"
              "</center>\n")

    req.write(box.end())

    req.write("</form>\n<p>\n")
Beispiel #13
0
def action(req, fields):
    req.write(InstDB.header("Reset password"))

    login = fields.get("Login", None)
    email = fields.get("Email", None)

    if login or email:
        if login:
            InstDB.Cur.execute(
                "SELECT UserId, Login, UserEmail FROM Users"
                " WHERE Login=%s", login)
        else:
            InstDB.Cur.execute(
                "SELECT UserId, Login, UserEmail FROM Users"
                " WHERE UserEmail=%s", email)

        row = InstDB.Cur.fetchone()
        if not row:
            InstDB.error(req, "Sorry, no account matches the given input.")
            req.write(InstDB.footer())
            return

        userid = row["UserId"]
        emailaddr = row["UserEmail"]
        login = row["Login"]

        InstDB.Cur.execute(
            "SELECT COUNT(*) AS count FROM Confirm"
            " WHERE UserId=%s && ConfirmAction='ResetPass'", userid)

        if InstDB.Cur.fetchone()["count"]:
            InstDB.error(req, "Password reset confirmation already sent.")
            req.write(
                '<p>\nIf you do not receive it soon, feel free to'
                ' email the <a href="mailto:[email protected]">admin</a>'
                ', or wait a day for this confirmation to expire.')
            req.write(InstDB.footer())
            return

        confirm.new('ResetPass', userid, login)

        req.write('<font class="title">An email will be sent soon'
                  ' to the email address you registered with, containing'
                  ' directions on how to reset your password.</font>\n')

        req.write(InstDB.footer())
        return

    box = InstDB.html_box()
    req.write(box.start("Reset password"))

    req.write("""
<form action="patches.py?Action=resetpass" method=post>
<table border=0 cellpadding=8>
<tr>
<td align="center" bgcolor="#4070b0">
<b>Login</b><input type=text name=Login size=16><br>
or<br>
<b>Email</b><input type=text name=Email size=16>
<p>
<input type=submit value="Reset password">
<p>
</td>
<td>
Enter your account login <b>OR</b> email address. A confirmation email
will be sent to your registered email address with directions
for resetting your password.
</td>
</tr>
</table>
</form>
""")

    req.write(box.end())
    req.write(InstDB.footer())
Beispiel #14
0
def action(req, fields):
    id = int(fields["ItemId"])

    req.write(InstDB.header("Edit patch file details"))

    InstDB.Cur.execute("SELECT UserId FROM PatchInfo WHERE PatchId=%s", id)
    row = InstDB.Cur.fetchone()
    if not row:
        InstDB.error(req, "Patch file with ID '%d' not found" % id)
        req.write(InstDB.footer())
        return

    # Do some security checks
    if not InstDB.User:
        InstDB.error(req, "Must login to edit patch information")
        req.write(InstDB.footer())
        return

    if not "Admin" in InstDB.User["Flags"] \
       and row["UserId"] != InstDB.User["UserId"]:
        InstDB.error(req, "You don't have permission to edit that info")
        req.write(InstDB.footer())
        return

    edit_patch_item(id, fields, req)

    req.write(InstDB.footer())
Beispiel #15
0
    def html_val(self, field, data):
        global StarWidths

        if field == "Author":
            if "Author" in data["PatchFlags"]:
                return cgi.escape(InstDB.emailmunge(data["UserName"]))
            else:
                return cgi.escape(InstDB.emailmunge(data["PatchAuthor"]))
        elif field == "CategoryName2":
            return self.category_name(int(data["CategoryId2"]))
        elif field == "CategoryName3":
            return self.category_name(int(data["CategoryId3"]))
        elif field == "CategoryLong":
            cats = data["CategoryName"]
            if data["CategoryId"] != 1:
                s = self.category_name(int(data["CategoryId2"]))
                if s:
                    cats = cats + ", " + s
                    s = self.category_name(int(data["CategoryId3"]))
                    if s:
                        cats = cats + ", " + s
            return cats
        elif field == "CramSize":
            return InstDB.pretty_size(data["CramSize"])
        elif field == "CramSizeDetail":
            return InstDB.pretty_size (data["CramSize"]) \
                   + " (%d)" % data["CramSize"]
        elif field == "DownloadLink":
            return "<a href=\"download.py?PatchId=%d&amp;Type=zip\">" \
                   "<img src=\"images/dnld.png\" alt=\"Download\" width=14 height=14 border=0>" \
                   "</a> (%d)" % (data["PatchId"], data["DownloadClicks"])
        elif field == "Email":
            return cgi.escape(InstDB.emailmunge(data["Email"]))
        elif field == "FileName":
            return cgi.escape(data["FileName"]) + "." + data["PatchType"]
        elif field == "FileNameLink":
            return '<a href="patches.py?Action=item&amp;ItemId=%d">%s.%s</a>' \
                   % (data["PatchId"], cgi.escape (data["FileName"]),
                      data["PatchType"])
        elif field == "FileSize":
            return InstDB.pretty_size(data["FileSize"])
        elif field == "LicenseLink":
            if data["LicenseURL"]:
                return '<a href="%s">%s</a>' % (data["LicenseURL"],
                                                data["LicenseName"])
            else:
                return data["LicenseName"]
        elif field == "Rating":
            if data["RateCount"] > 0:
                f = float(data["RatingValue"])
                if f < 0.5: f = 0.5
                elif f > 5.0: f = 5.0

                fract = f - int(f)
                if fract < 0.5: ifract = 0
                else: ifract = 5

                width = StarWidths[int(f) * 2 + ifract / 5 - 1]

                return (
                    '<img src="images/stars_%d_%d.png" alt="%0.1f stars" width=%d height=11>'
                    '(%d)' % (int(f), ifract, f, width, data["RateCount"]))
            else:
                return "None"
        elif field == "UserFileSize":
            return InstDB.pretty_size(data["FileSize"])
        elif field == "UserLink":
            return "<a href=\"patches.py?Action=user&amp;UserId=%d\">%s</a>" \
                   % (data["UserId"], cgi.escape (data["UserName"]))
        elif field == "ZipSize":
            return InstDB.pretty_size(data["ZipSize"])
        elif field == "ZipSizeDetail":
            return InstDB.pretty_size (data["ZipSize"]) \
                   + " (%d)" % data["ZipSize"]
        elif field == "WebSiteLink":
            escurl = cgi.escape(data["WebSite"])
            return '<a href="%s">%s</a>' % (escurl, escurl)
        else:
            return ItemHelper.html_val(self, field, data)
Beispiel #16
0
def action(req, fields):
    req.write(InstDB.header("Patch submission"))

    if not InstDB.AssertLogin(req):
        req.write(InstDB.footer())
        return

    req.write("""
Upload files to <a href="ftp://sounds.resonance.org/incoming/">ftp://sounds.resonance.org/incoming/</a>.
It is recommended that you read the <a href="patches.py?Action=help&amp;Topic=submit">Content Submission Help</a>.
<p>
""")

    # Check if any incoming files have been selected
    Files = fields.getlist("Files")
    for fname in Files:
        (evil, fname) = os.path.split(fname)  # Make sure no path of evil

        if not os.path.isfile(InstDB.IncomingPath + os.sep + fname):
            InstDB.error(req, "File not found '%s'" % cgi.escape(fname))
        else:
            # Create new import queue task (don't activate yet)
            InstDB.Cur.execute(
                "INSERT INTO Queue"
                " (Type, Status, FileName, UserId)"
                " VALUES ('Import', 'Queued', %s, %s)",
                (fname, InstDB.User["UserId"]))

    # Get list of files in incoming directory
    file_list = os.listdir(InstDB.IncomingPath)
    file_list.sort()

    box = InstDB.html_box()
    box.tableattr = 'width="100%"'
    req.write(box.start("Incoming files"))

    req.write("<form action=\"patches.py?Action=submit\" method=POST>\n")

    if file_list:
        table = InstDB.tabular(("Import", "File", "Size", "Date"))
        table.tableattr = 'width="100%"'

        for file in file_list:
            check = "<input type=checkbox name=Files value=\"%s\">" \
                    % cgi.escape (file)
            stats = os.stat(InstDB.IncomingPath + "/" + file)
            req.write(
                table.addrow((check, cgi.escape(file),
                              InstDB.pretty_size(stats.st_size),
                              time.ctime(stats.st_mtime))))
        req.write(table.end())

    else:
        req.write("<b>No files in incoming directory</b><br>\n")

    req.write(box.end())

    req.write("<center><input type=submit name=FileSelect"
              " value=\"Import and/or Refresh\"></center>\n")
    req.write("</form>\n")

    req.write("<p><p>\n")

    # Get queued tasks

    box = InstDB.html_box()
    box.tableattr = 'width="100%"'
    req.write(box.start("Your queued tasks"))

    sel = SqlHelpers.Select()
    sel.fields = ("QueueId", "Type", "Status", "UserId", "FileName", "Date")
    sel.tables = ("Queue", )
    sel.where = ("Type in ('Import','Activate')", )
    sel.orderby = "QueueId"

    InstDB.Cur.execute(sel.query())

    table = InstDB.tabular(
        ("Position", "File Name", "Action", "Status", "Start Time"))
    table.tableattr = 'width="100%"'

    index = 0
    any = False
    for row in InstDB.Cur.fetchall():
        if row["UserId"] == InstDB.User["UserId"]:
            any = True

            if row["Status"] != "Error":
                ndxval = index
                status = row["Status"]
            else:
                ndxval = "N/A"
                status = '<font class="Error">' + row["Status"] + '</font>'

            req.write(
                table.addrow((ndxval, cgi.escape(row["FileName"]), row["Type"],
                              status, row["Date"])))
        if row["Status"] != "Error":
            index += 1

    if any: req.write(table.end())
    else: req.write("<b>No queued tasks</b><br>\n")

    req.write(box.end())
    req.write("<p><p>\n")

    # Any activation form data submitted?  Group fields by patch ID.
    patchFields = {}
    if fields.get("Activate", None):
        for field in fields.list:
            # Fields have "P<PatchId>_" prefix
            match = re.match("P([0-9]{1,10})_(.+)", field.name)
            if match:
                patchId = match.group(1)
                if not patchFields.get(patchId, None):
                    patchFields[patchId] = {}

                patchFields[patchId][match.group(2)] = fields[field.name]

    # Files pending activation

    box = InstDB.html_box()
    box.tableattr = 'width="100%"'
    req.write(box.start("Files pending activation"))

    InstDB.Cur.execute(
        "SELECT PatchId, PatchType FROM PatchInfo"
        " WHERE UserId=%s && State='Imported'", InstDB.User["UserId"])
    pendRows = InstDB.Cur.fetchall()

    if pendRows:
        activateCells = PatchHelper.edit_cell_format
        activateFields = InstDB.cells_to_fields(activateCells)
    displayedOne = False

    for pendRow in pendRows:
        pFields = patchFields.get(str(pendRow["PatchId"]), None)
        patchId = int(pendRow["PatchId"])
        activate = pFields and pFields.get("Activate", None)
        errorMsg = ""

        if pFields:  # Form data submitted for this patch?
            pFields["FileName"] = pFields["FileName"].strip()
            if not pFields["FileName"]:
                errorMsg = "File name is required"

            # Check if filename already used
            if not errorMsg:
                InstDB.Cur.execute(
                    "SELECT COUNT(*) AS count FROM PatchInfo"
                    " WHERE PatchId != %s && FileName=%s"
                    " && PatchType=%s",
                    (patchId, pFields["FileName"], pendRow["PatchType"]))
                if int(InstDB.Cur.fetchone()["count"]) > 0:
                    errorMsg = "File name already in use"

        if pFields:
            update = PatchHelper.sql_update(activateFields, pFields)
            update.set["FileName"] = pFields["FileName"]
            update.where = ("PatchInfo.PatchId=%d" % patchId, )
            if activate and not errorMsg: update.set["State"] = "Activating"

            InstDB.Cur.execute(update.query())
            props_update(patchId, pFields)  # Update extra properties

            # Queue activation task
            if activate and not errorMsg:
                InstDB.Cur.execute(
                    "INSERT INTO Queue"
                    " (Type, Status, UserId, ItemId, FileName)"
                    " VALUES ('Activate', 'Queued', %s, %s, %s)",
                    (InstDB.User["UserId"], patchId, "%s.%s" %
                     (pFields["FileName"], pendRow["PatchType"])))

        if (not activate or errorMsg) and not displayedOne:
            req.write(
                "<form action=\"patches.py?Action=submit\" method=POST>\n")
            displayedOne = True

        if errorMsg:
            req.write('<font class="error">%s</font><br>\n' %
                      cgi.escape(errorMsg))

        if not activate or errorMsg:
            sel = PatchHelper.sql_select(activateFields)
            sel.fields.insert(0, "PatchInfo.PatchId")
            sel.where.insert(0, "PatchInfo.PatchId=%d" % patchId)
            InstDB.Cur.execute(sel.query())
            row = InstDB.Cur.fetchone()
            display_patch(req, row, pFields)

    if displayedOne:
        req.write("<input type=submit name=Activate" " value=\"Update\">\n")
        req.write("</form>\n")
    else:
        req.write("<b>No files pending activation</b><br>\n")

    req.write(box.end())
    req.write(InstDB.footer())
Beispiel #17
0
def action(req, fields):
    id = int(fields.get("PatchId", 0))

    filetype = fields.get("Type", None)
    if filetype != "zip": filetype = "CRAM"

    itemFields = ("FileName", "PatchType", "ZipSize", "CramSize")
    sel = PatchHelper.sql_select(itemFields)

    sel.fields.append("DATEDIFF(NOW(), PatchInfo.DateImported) AS ImportDays"
                      )  # Days since imported
    sel.where.insert(0, "PatchInfo.State = 'Active'")
    sel.where.insert(0, "PatchInfo.PatchId=%d" % id)

    InstDB.Cur.execute(sel.query())

    PatchRow = InstDB.Cur.fetchone()
    if not PatchRow:
        req.write(InstDB.header("Download Error"))
        InstDB.error(req, "Instrument file with ID '%d' not found" % id)
        req.write(InstDB.footer())
        return

    # For now, we just select a random mirror, unless imported less than 2 days ago, then we use default mirror
    if (PatchRow["ImportDays"] > 2):
        InstDB.Cur.execute(
            "SELECT MirrorId, BaseURL FROM Mirrors"
            " WHERE FIND_IN_SET('Active', Flags) && FIND_IN_SET(%s, Flags)"
            " ORDER BY RAND() LIMIT 1", filetype)
    else:  # Just use first active mirror with file type available (usually Mirror 1, unless its inactive)
        InstDB.Cur.execute(
            "SELECT MirrorId, BaseURL FROM Mirrors WHERE"
            " FIND_IN_SET('Active', Flags) && FIND_IN_SET(%s, Flags)"
            " ORDER BY MirrorId LIMIT 1", filetype)

    mirror = InstDB.Cur.fetchone()
    if not mirror:
        req.write(InstDB.header("Download Error"))
        InstDB.error(req,
                     "No active mirror found for requested instrument file")
        req.write(InstDB.footer())
        return

    # Update PatchInfo file download count for file type and set some vars
    if filetype == "zip":
        filesize = PatchRow["ZipSize"]
        fileext = "zip"
        InstDB.Cur.execute(
            "UPDATE PatchInfo SET ZipClicks=ZipClicks+1"
            " WHERE PatchId=%s", id)
    else:
        filesize = PatchRow["CramSize"]
        fileext = "cram"
        InstDB.Cur.execute(
            "UPDATE PatchInfo SET CramClicks=CramClicks+1"
            " WHERE PatchId=%s", id)

    # Update Mirror download count and download size counters
    InstDB.Cur.execute(
        "UPDATE Mirrors SET DownloadClicks=DownloadClicks+1,"
        "DownloadSize=DownloadSize+%s,DownloadTotal=DownloadTotal+%s"
        " WHERE MirrorId=%s", (filesize, filesize, mirror["MirrorId"]))

    # If user logged in, mark file as downloaded
    if InstDB.User:
        InstDB.Cur.execute(
            "SELECT COUNT(*) AS count FROM FileMarks"
            " WHERE UserId=%s && PatchId=%s", (InstDB.User['UserId'], id))
        if InstDB.Cur.fetchone()["count"] == 0:
            InstDB.Cur.execute(
                "INSERT FileMarks (UserId, PatchId, MarkFlags)"
                " VALUES(%s,%s,'Downloaded')", (InstDB.User['UserId'], id))
        else:
            InstDB.Cur.execute(
                "UPDATE FileMarks SET MarkFlags=CONCAT(MarkFlags, \",Downloaded\"),"
                "MarkTime=Null WHERE UserId=%s && PatchId=%s",
                (InstDB.User['UserId'], id))

    # Redirect to file to download
    if req.proto_num >= 1001:
        req.status = apache.HTTP_TEMPORARY_REDIRECT
    else:
        req.status = apache.HTTP_MOVED_TEMPORARILY

    # FIXME - Does redirect Location: URL need to be escaped???
    req.headers_out["Location"] = "%s/%s.%s.%s" \
      % (mirror["BaseURL"], PatchRow["FileName"], PatchRow["PatchType"], fileext)
    req.send_http_header()
Beispiel #18
0
def action(req, fields):
    req.write(InstDB.header("Edit profile"))

    if not InstDB.AssertLogin(req):
        req.write(InstDB.footer())
        return

    itemCells = UsersHelper.edit_cell_format
    itemFields = InstDB.cells_to_fields(itemCells)

    errmsg = None

    # Was Save button clicked? - Update field values
    if fields.get("Save", None):
        data = fields

        password = fields.get("Password", "")
        verify = fields.get("PassVerify", "")
        email = fields.get("UserEmail", "")

        if len(password) > 0 and len(password) < 5:
            errmsg = "Passwords must be 5-32 characters, leave blank for no change"

        if not errmsg and password != verify:
            errmsg = "Password and Verify don't match"

        if not errmsg:
            n = string.find(email, "@")
            n2 = string.find(email, " ")
            if n <= 0 or n == len(email) - 1 or n2 >= 0:
                errmsg = "A valid email address is required"

        if not errmsg:
            update = UsersHelper.sql_update(itemFields, fields)

            if password:                update.setnesc["Password"] = '******' \
       % MySQLdb.escape_string (password + InstDB.FuKraXor)

            update.where = ("UserId=%d" % InstDB.User["UserId"], )

            # Use login if UserName not set
            if not fields.get("UserName", None):
                update.setnesc["UserName"] = "******"
                if update.set.get("UserName"): del update.set["UserName"]

            InstDB.Cur.execute(update.query())
            req.write("<font size=+1>User profile updated</font>\n<p>\n")
        else:
            req.write('<font class="error">%s</font><p>\n' % errmsg)
    else:  # First page load?
        sel = UsersHelper.sql_select(itemFields)
        sel.where.insert(0, "UserId=%d" % InstDB.User["UserId"])
        InstDB.Cur.execute(sel.query())
        data = InstDB.Cur.fetchone()

    tab = (("Password", "<input type=password name=Password>"),
           ("Verify", "<input type=password name=PassVerify>"))
    tab += tuple(UsersHelper.form_cell_vals(itemCells, data))
    tab += (("<input type=submit name=Save value=\"Save\">\n", ), )

    # Display form
    box = InstDB.html_box()
    req.write(box.start("User Profile"))
    req.write(
        "<table border=0 cellpadding=8>\n<tr>\n<td valign=top bgcolor=#408ee6>\n"
    )
    req.write("<form action=\"patches.py?Action=profile\" method=POST>\n")

    split = InstDB.tabsplits(tab)
    req.write(split.render())

    req.write("</form>\n")
    req.write("</td>\n<td valign=top>\n")

    req.write("<vr>\n</td>\n<td valign=top>\n")

    req.write(InstDB.IncFile("profile.inc"))

    req.write("</td>\n</tr>\n</table>\n")
    req.write(box.end())

    req.write(InstDB.footer())
Beispiel #19
0
def action(req, fields):
    req.write(InstDB.header("Registration"))

    itemCells = UsersHelper.edit_cell_format

    errmsg = None
    recaptcha_error = None

    # Was Register button clicked? - Save field values
    if fields.get("Register", None):
        data = fields

        login = fields.get("Login", "")
        password = fields.get("Password", "")
        verify = fields.get("PassVerify", "")
        email = fields.get("UserEmail", "")
        captcha_challenge = fields.get("recaptcha_challenge_field", "")
        captcha_response = fields.get("recaptcha_response_field", "")

        if not login: errmsg = "Login name is required"
        elif not verify_chars(login, InstDB.LoginAllowChars):
            errmsg = "Only these characters are allowed in login: '******'" \
                % InstDB.LoginAllowChars
        elif login and InstDB.Cur.execute \
                 ("SELECT 1 FROM Users WHERE Login=%s", login) == 1:
            errmsg = "The login '%s' is already used" % (cgi.escape(login))

        if not errmsg and len(password) < 5:
            errmsg = "A password with 5-32 characters is required"

        if not errmsg and password != verify:
            errmsg = "Password and Verify don't match"

        if not errmsg:
            n = string.find(email, "@")
            n2 = string.find(email, " ")
            if n <= 0 or n == len(email) - 1 or n2 >= 0:
                errmsg = "A valid email address is required"

        if not errmsg and not verify_chars(email, InstDB.EmailAllowChars):
            errmsg = "Only these characters are allowed in email address: '%s'" \
                % InstDB.EmailAllowChars

        if not errmsg and InstDB.Cur.execute \
                ("SELECT Login FROM Users WHERE UserEmail=%s", email) == 1:
            existLogin = InstDB.Cur.fetchone()["Login"]
            errmsg = "Login '%s' is already registered with that email address." \
                % existLogin

        if not errmsg:
            resp = recaptcha_submit(captcha_challenge, captcha_response,
                                    InstDB.ReCaptchaPrvKey,
                                    req.connection.remote_ip)
            if not resp.is_valid:
                errmsg = "Are you human? reCAPTCHA response incorrect."
                recaptcha_error = resp.error_code

        if not errmsg:
            itemFields = InstDB.cells_to_fields(
                InstDB.delete_cells(itemCells, "UserName"))

            insert = UsersHelper.sql_insert(itemFields, fields)
            insert.set["Login"] = login
            insert.set["Flags"] = "Confirm"
            insert.setnesc["Password"] = '******' \
                % MySQLdb.escape_string (password + InstDB.FuKraXor)
            insert.setnesc["DateCreated"] = "NULL"
            insert.set["LastLogin"] = 0

            # Use login if UserName not set
            username = fields.get("UserName", None)
            if not username: username = login
            insert.set["UserName"] = username

            InstDB.Cur.execute(insert.query())
            userid = InstDB.DB.insert_id()

            confirm.new("Register", userid, login)

            # Add new account for phpbb forums
            if InstDB.EnableForums:
                retval = phpbb.add_user(login, password, email)

                if retval != 0:
                    raise RuntimeWarning, "Failed to add user '%s' to PHPBB forums (retval=%d)" % (
                        login, retval)

            req.write('<font class="title">Registration successful, an email'
                      ' will be sent shortly with directions on activating'
                      ' your account.</font>\n')
            req.write(InstDB.footer())
            return
        else:
            InstDB.error(req, errmsg)
            req.write("<p>\n")
    else:
        data = {"UserFlags": ('EmailNews', 'NotifyMessage', 'NotifyRatings')}

    tab = (("Login", '<input type=text name=Login value="%s">' %
            cgi.escape(data.get("Login", ""))),
           ("Password", "<input type=password name=Password>"),
           ("Verify", "<input type=password name=PassVerify>"))
    tab += tuple(UsersHelper.form_cell_vals(itemCells, data))

    tab += ((recaptcha_display(InstDB.ReCaptchaPubKey, False,
                               recaptcha_error), ), )

    tab += (('<input type=submit name=Register value="Register">\n'
             '<a href="patches.py?Action=help&amp;Topic=privacy&amp;Mini=1"'
             ' target="_blank">Privacy Policy</a>', ), )

    # Display register form
    box = InstDB.html_box()
    req.write(box.start("User Registration"))
    req.write("<table border=0 cellpadding=8>\n<tr>\n<td valign=top>\n")
    req.write("<form action=\"patches.py?Action=register\" method=POST>\n")

    split = InstDB.tabsplits(tab)
    split.tableattr = 'bgcolor="#408ee6"'
    split.cellspacing = 4
    req.write(split.render())

    req.write("</form>\n")
    req.write("</td>\n<td valign=top>\n")

    req.write(InstDB.IncFile("register.inc"))

    req.write("</td>\n</tr>\n</table>\n")
    req.write(box.end())

    req.write(InstDB.footer())
Beispiel #20
0
def action (req, fields):
    msg = fields.get ("Msg", None)

    if msg == "Registered":
        req.write (InstDB.header ("Email confirmation"))
        req.write (InstDB.IncFile ("activated.inc"))
        req.write (InstDB.footer ())
        return
    elif msg == "Reset":
        req.write (InstDB.header ("Email confirmation"))
        req.write ('<font class="title">Password has been reset and will be'
                   ' emailed to you shortly.</font>\n')
        req.write (InstDB.footer ())
        return

    login = fields.get ("Login", None)
    hashkey = fields.get ("HashKey", None)

    if not login or not hashkey:
        req.write (InstDB.header ("Email confirmation"))
        InstDB.error (req, "Invalid confirmation data")
        req.write (InstDB.footer ())
        return

    InstDB.Cur.execute ("SELECT UserId FROM Users WHERE Login=%s", login)
    row = InstDB.Cur.fetchone ()
    if not row:
        req.write (InstDB.header ("Email confirmation"))
        InstDB.error (req, "Invalid confirmation data")
        req.write (InstDB.footer ())
        return

    userid = row["UserId"]

    InstDB.Cur.execute ("SELECT ConfirmAction FROM Confirm"
                        " WHERE UserId=%s && HashKey=%s", (userid, hashkey))

    row = InstDB.Cur.fetchone ()
    if not row:
        req.write (InstDB.header ("Email confirmation"))
        InstDB.error (req, "Email confirmation not found, perhaps it expired?")
        req.write (InstDB.footer ())
        return

    if row["ConfirmAction"] == "Register":
        InstDB.Cur.execute ("UPDATE Users SET Flags='' WHERE UserId=%s", userid)

        if InstDB.EnableForums:
          retval = phpbb.activate_user (login)  # Activate the forum account

          if retval != 0:
            raise RuntimeWarning, "Failed to activate PHPBB user '%s' (retval=%d)" % (login, retval)

        # Redirect to confirm page with Msg=Registered parameter
        if req.proto_num >= 1001:
            req.status = apache.HTTP_TEMPORARY_REDIRECT
        else: req.status = apache.HTTP_MOVED_TEMPORARILY

        req.headers_out["Location"] = "patches.py?Action=confirm&amp;Msg=Registered"
        req.send_http_header()

    elif row["ConfirmAction"] == "ResetPass":
        newpass = ""
        for i in range (0, 8):
            newpass += random.choice ("ABCDEFGHIJKLMNPQRSTUVWXYZ"
                                      "abcdefghjkmnpqrstuvwxyz23456789")

        InstDB.Cur.execute ("UPDATE Users SET Password=SHA1(%s)"
                            " WHERE UserId=%s", (newpass + InstDB.FuKraXor, userid))

        f = open (InstDB.IncludePath + os.sep + "newpass.msg")
        email = f.read () % {
            "Login" : login,
            "Password" : newpass }

        # Queue email task
        InstDB.Cur.execute ("INSERT INTO Queue (Type, Status, UserId, Content)"
                            " VALUES ('Email', 'Queued', %s, %s)",
                            (userid, email))

        # Redirect to confirm page with Msg=Reset parameter
        if req.proto_num >= 1001:
            req.status = apache.HTTP_TEMPORARY_REDIRECT
        else: req.status = apache.HTTP_MOVED_TEMPORARILY

        req.headers_out["Location"] = "patches.py?Action=confirm&amp;Msg=Reset"
        req.send_http_header()


    # Remove the confirmation, we done with it
    InstDB.Cur.execute ("DELETE FROM Confirm WHERE UserId=%s && HashKey=%s",
                        (userid, hashkey))
Beispiel #21
0
def display_patch(req, row, fields):
    itemCells = InstDB.delete_cells(PatchHelper.edit_cell_format, "FileName")
    prefix = "P%d_" % row["PatchId"]  # Prefix field names with patch ID

    tab = [["Activate",
            "<input type=checkbox name=%sActivate>" % prefix],
           [
               "File Name",
               '<input type=text name=%sFileName value="%s" size=48>'
               '.%s' % (prefix, row["FileName"], row["PatchType"])
           ]]

    tab += PatchHelper.form_cell_vals(itemCells, row, prefix)

    patchid = row["PatchId"]

    # Get patch property values
    sel = props_select(patchid)
    InstDB.Cur.execute(sel.query())
    props = InstDB.Cur.fetchall()

    # Get count of Instruments in this patch
    InstDB.Cur.execute(
        "SELECT COUNT(*) AS Count FROM InstInfo"
        " WHERE PatchId=%s", patchid)
    instcount = InstDB.Cur.fetchone()["Count"]

    # Get count of samples in this patch
    InstDB.Cur.execute(
        "SELECT COUNT(*) AS Count FROM SampleInfo"
        " WHERE PatchId=%s", patchid)
    samplecount = InstDB.Cur.fetchone()["Count"]

    if instcount > 0:
        trow = ('<a href="patches.py?Action=list&amp;Type=Inst&amp;PatchId=%s'
                '&amp;OrderBy=Bank">Instruments</a>' % patchid, instcount)
    else:
        trow = ("Instruments", "None")

    if samplecount > 0:
        trow += (
            '<a href="patches.py?Action=list&amp;Type=Sample&amp;PatchId=%s'
            '&amp;OrderBy=Name">Samples</a>' % patchid, samplecount)
    else:
        trow += ("Samples", "None")

    tab += ((trow, ))

    tab += props_form_cells(props, prefix)

    # Get list of extracted files
    patchFilePath = InstDB.ActivatePath + os.sep + str(
        row["PatchId"]) + "-files"
    file_list = os.listdir(patchFilePath)
    file_list.sort()

    table = InstDB.tabular(("File name", "Size"))
    table.tableattr = 'width="100%"'
    table.ttlrowattr = 'bgcolor="#110044"'

    tableStr = ""
    for fname in file_list:
        if fields: newname = fields.get("File_%s" % fname, None)
        else: newname = None

        if newname and newname != fname:  # File rename requested?
            (evil,
             newname) = os.path.split(newname)  # Make sure no path of evil
            try:
                os.rename(patchFilePath + os.sep + fname,
                          patchFilePath + os.sep + newname)
                fname = newname
            except:
                InstDB.error(
                    req,
                    "Failed to rename file '%s' to '%s'!" % (fname, newname))

        namefield = '<input type=text name="%sFile_%s" value="%s" size=60>' \
            % (prefix, cgi.escape (fname), cgi.escape (fname))
        stats = os.stat(patchFilePath + os.sep + fname)
        tableStr += table.addrow(
            (namefield, InstDB.pretty_size(stats.st_size)))
    tableStr += table.end()

    tab += (("<center><b>Archive files</b></center>\n", ), )
    tab += ((tableStr, ), )

    split = InstDB.tabsplits(tab)
    req.write(split.render())

    req.write("<p>\n")
Beispiel #22
0
def show_patch_item(id, fields, req):
    RateOrder = fields.get("RateOrder", "Date")

    OrderDir = int(fields.get("OrderDir", 0))
    if OrderDir < 0 or OrderDir > 2: OrderDir = 0

    recaptcha_error = None
    comment = ""

    # Check for comment/rating submission
    if fields.get("submit", None):
        comment = fields.get("comment", "")
        captcha_challenge = fields.get("recaptcha_challenge_field", "")
        captcha_response = fields.get("recaptcha_response_field", "")

        if InstDB.User:  # User is logged in?
            rating = int(fields.get("rating", 0))
            if rating not in range(0, 6): rating = 0

            # Select existing comment/rating if any
            InstDB.Cur.execute(
                "SELECT Rating, Comment FROM Ratings"
                " WHERE PatchId=%s && UserId=%s", (id, InstDB.User["UserId"]))
            row = InstDB.Cur.fetchone()
            if row:  # Existing comment/rating?
                InstDB.Cur.execute(
                    "UPDATE Ratings SET Rating=%s, Comment=%s"
                    " WHERE PatchId=%s && UserId=%s",
                    (rating, comment, id, InstDB.User["UserId"]))

                # Calculate increment value for RateCount (user count)
                if rating != 0 and row["Rating"] == 0: countinc = 1  # inc
                elif rating == 0 and row["Rating"] != 0: countinc = -1  # dec
                else: countinc = 0  # Don't change

                # Update RateTotal (+ new rating - old) and RateCount
                InstDB.Cur.execute(
                    "UPDATE PatchInfo SET RateTotal=RateTotal"
                    " + %s - %s, RateCount=RateCount + %s"
                    " WHERE PatchId=%s", (rating, row["Rating"], countinc, id))

                InstDB.show_status(req, "Updated comment/rating data")

            else:  # Insert new comment/rating
                InstDB.Cur.execute(
                    "INSERT INTO Ratings (PatchId, UserId,"
                    " Rating, Comment) VALUES(%s,%s,%s,%s)",
                    (id, InstDB.User["UserId"], rating, comment))
                if rating != 0:  # Update RateTotal and RateCount for patch file
                    InstDB.Cur.execute(
                        "UPDATE PatchInfo SET RateTotal="
                        "RateTotal+%s, RateCount=RateCount+1"
                        " WHERE PatchId=%s", (rating, id))

                InstDB.show_status(req, "Added new comment/rating")

        elif comment:  # User not logged in - add an anonymous comment
            resp = recaptcha_submit(captcha_challenge, captcha_response,
                                    InstDB.ReCaptchaPrvKey,
                                    req.connection.remote_ip)
            if resp.is_valid:
                InstDB.Cur.execute(
                    "INSERT INTO Ratings"
                    " (PatchId, UserId, Comment) VALUES(%s,1,%s)",
                    (id, comment))
                InstDB.show_status(req, "Anonymous comment added")
                comment = ""
            else:
                InstDB.error(
                    req, "reCAPTCHA response incorrect. Comment not added.")
                recaptcha_error = resp.error_code

    itemCells = PatchHelper.cell_format
    itemFields = InstDB.cells_to_fields(itemCells)

    sel = PatchHelper.sql_select(itemFields)

    # Add UserId field if not already in select query
    if not "PatchInfo.UserId" in sel.fields:
        sel.fields += ("PatchInfo.UserId", )

    sel.where.insert(0, "PatchInfo.State = 'Active'")
    sel.where.insert(0, "PatchInfo.PatchId=%d" % id)

    InstDB.Cur.execute(sel.query())

    patchRow = InstDB.Cur.fetchone()
    if not patchRow:  # Shouldn't happen, thats why we raise an exception
        raise LookupError, "Patch file with ID '%d' not found" % id

    fields = InstDB.cells_to_fields(PropHelper.cell_format)
    sel = PropHelper.sql_select(fields)
    sel.where.insert(0, "ItemProps.ItemId=%d" % id)

    InstDB.Cur.execute(sel.query())
    props = InstDB.Cur.fetchall()

    # Get count of Instruments in this patch
    InstDB.Cur.execute(
        "SELECT COUNT(*) AS Count FROM InstInfo"
        " WHERE PatchId=%s", id)
    instcount = InstDB.Cur.fetchone()["Count"]

    # Get count of samples in this patch
    InstDB.Cur.execute(
        "SELECT COUNT(*) AS Count FROM SampleInfo"
        " WHERE PatchId=%s", id)
    samplecount = InstDB.Cur.fetchone()["Count"]

    # If user is logged in - see if they have rated this file
    UserRating = None
    if InstDB.User:
        InstDB.Cur.execute(
            "SELECT Date, Rating, Comment FROM Ratings"
            " WHERE PatchId=%s && UserId=%s", (id, InstDB.User["UserId"]))
        UserRating = InstDB.Cur.fetchone()

    # Get ratings/comments
    RatingFields = RatingHelper.list_format

    sel = RatingHelper.sql_select(RatingFields)
    sel.where.insert(0, "PatchId=%d" % id)
    sel.orderby = RatingHelper.sql_order_field(RateOrder, OrderDir)
    sel.limit = str(InstDB.MaxRatings)
    sel.calc_found_rows = True

    InstDB.Cur.execute(sel.query())
    RatingRows = InstDB.Cur.fetchall()

    # Get total number of comments/ratings
    InstDB.Cur.execute("SELECT FOUND_ROWS() AS Total")
    RatingRowsTotal = InstDB.Cur.fetchone()["Total"]

    tab = PatchHelper.cell_vals(itemCells, patchRow)

    if instcount > 0:
        tmptab = (
            '<a href="patches.py?Action=list&amp;Type=Inst&amp;PatchId=%s'
            '&amp;OrderBy=Bank">Instruments</a>' % id, instcount)
    else:
        tmptab = ("Instruments", "None")

    if samplecount > 0:
        tmptab += (
            '<a href="patches.py?Action=list&amp;Type=Sample&amp;PatchId=%s'
            '&amp;OrderBy=SampleName">Samples</a>' % id, samplecount)
    else:
        tmptab += ("Samples", "None")

    tab += (tmptab, )

    tab += add_extra_props(req, props)

    tab += (("<center>Rating &amp; Comment</center>", ), )

    if InstDB.User:  # User logged in?
        if UserRating: tab += (("Modified", UserRating["Date"]), )

        # selected item
        if UserRating: s = str(UserRating["Rating"])
        else: s = "0"

        tab += (("Rating", InstDB.form_radio("rating", InstDB.RatingRadio,
                                             s)), )
        captcha_code = ""
    else:
        tab += (("Rating", "Login to rate this file"), )
        captcha_code = recaptcha_display(InstDB.ReCaptchaPubKey, False,
                                         recaptcha_error)

    if UserRating:
        comment = cgi.escape(UserRating["Comment"])
        submit = "Update"
    else:
        submit = "Submit"


    tab += (("Comment",
             "<textarea name=comment rows=5 cols=40>%s</textarea>\n" % comment
             + captcha_code),
            ("<center>\n<input name=submit type=submit value=%s>\n</center>\n" \
             % submit,))

    # Display patch item info
    box = InstDB.html_box()

    # User is admin or owns this patch file? - show edit icon
    if InstDB.User and ("Admin" in InstDB.User["Flags"] \
           or patchRow["UserId"] == InstDB.User["UserId"]):
        box.titlehtml = '\n&nbsp;&nbsp;&nbsp;<a href="patches.py?Action=edit&amp;ItemId=%d">' \
                    '<img src="images/edit.png" alt="Edit Mode" width=10 height=15 border=0>' \
                    '&nbsp;<font class="boxtitle">Edit</font></a>\n' % id

    req.write(box.start("Patch File"))
    req.write('<form action="" method=POST>\n')

    split = InstDB.tabsplits(tab)
    req.write(split.render())

    req.write("</form>\n")
    req.write(box.end())

    req.write("<p><p>\n")

    # Show ratings/comments table
    if RatingRows:
        box = InstDB.html_box()
        req.write(box.start("Comments"))

        sortList = []
        for field in RatingFields:
            if RatingHelper.field_searchable(field):
                sortList += ((field, RatingHelper.field_title(field)), )
            else:
                sortList += (RatingHelper.field_title(field), )

        urlstr = urlencode((("Action", "item"), ("ItemId", str(id))))
        urlstr = cgi.escape(urlstr)

        titles = sort_titles(sortList, urlstr, RateOrder, OrderDir)
        table = InstDB.tabular(titles)
        table.cellpadding = "4"
        table.tableattr = 'width="100%"'

        # Display comments
        for row in RatingRows:
            req.write(table.addrow(RatingHelper.html_vals(RatingFields, row)))

        req.write(table.end())

        req.write(box.end())
Beispiel #23
0
def list_samples(req, fields):
    OrderBy = fields.get("OrderBy", None)

    OrderDir = int(fields.get("OrderDir", 0))
    if OrderDir < 0 or OrderDir > 2: OrderDir = 0

    Offset = int(fields.get("Offset", 0))
    Count = int(fields.get("Count", 50))
    PatchId = int(fields.get("PatchId", 0))

    if Count <= 5: Count = 5
    elif Count > InstDB.MaxListItems: Count = InstDB.MaxListItems

    InstDB.Cur.execute("SELECT State, UserId FROM PatchInfo WHERE PatchId=%s",
                       PatchId)

    row = InstDB.Cur.fetchone()
    if not row:
        InstDB.error(req, "Specified patch file not found")
        return

    # Only allow list if patch is active, user is admin or user owns imported file
    if row["State"] != "Active":
        if not InstDB.User or ("Admin" not in InstDB.User["Flags"] and \
              (row["State"] != 'Imported' or InstDB.User["UserId"] != row["UserId"])):
            InstDB.error(req, "Permission denied to specified patch file")
            return

    itemFields = SampleHelper.list_format
    extraFields = ("State", )
    if PatchId: extraFields += ("PatchId", )

    # Create SQL query
    sel = SampleHelper.sql_select(itemFields + extraFields)

    # Make sure order by field is valid
    if OrderBy and not SampleHelper.field_searchable(OrderBy): OrderBy = None
    if not OrderBy: OrderBy = "SampleName"

    sel.orderby = SampleHelper.sql_order_field(OrderBy, OrderDir)
    sel.limit = "%d,%d" % (Offset, Count)
    if PatchId: sel.where.insert(0, "PatchInfo.PatchId=%d" % PatchId)
    sel.calc_found_rows = True

    InstDB.Cur.execute(sel.query())
    rows = InstDB.Cur.fetchall()

    # Get total number of matching items
    InstDB.Cur.execute("SELECT FOUND_ROWS() AS Total")
    row = InstDB.Cur.fetchone()
    total = int(row["Total"])

    # Re URL-ify the input fields
    pager_urlstr = urlencode(
        (("Action", "list"), ("Type", "Sample"), ("OrderBy", OrderBy),
         ("OrderDir", OrderDir), ("Count", Count), ("PatchId", PatchId)))
    pager_urlstr = cgi.escape(pager_urlstr)

    # Display the pager bar
    InstDB.show_pager(req, pager_urlstr, Offset, Count, total)

    # Create clickable titles that sort output
    urlstr = urlencode((("Action", "list"), ("Type", "Sample"),
                        ("Count", Count), ("PatchId", PatchId)))
    urlstr = cgi.escape(urlstr)

    sortList = []
    for field in itemFields:
        if SampleHelper.field_searchable(field):
            sortList += ((field, SampleHelper.field_title(field)), )
        else:
            sortList += (SampleHelper.field_title(field), )

    titles = sort_titles(sortList, urlstr, OrderBy, OrderDir, SampleHelper)

    table = InstDB.tabular(titles)
    table.tableattr = 'width="100%"'

    for row in rows:
        req.write(table.addrow(SampleHelper.html_vals(itemFields, row)))

    req.write(table.end())

    # Display the pager bar
    InstDB.show_pager(req, pager_urlstr, Offset, Count, total)
Beispiel #24
0
def list_patches(req, fields):
    OrderBy = fields.get("OrderBy", None)
    Category = int(fields.get("Category", 0))
    CategoryExact = int(fields.get("CategoryExact", 0))

    OrderDir = int(fields.get("OrderDir", 0))
    if OrderDir < 0 or OrderDir > 2: OrderDir = 0

    Offset = int(fields.get("Offset", 0))
    Count = int(fields.get("Count", 50))

    if Count <= 5: Count = 5
    elif Count > InstDB.MaxListItems: Count = InstDB.MaxListItems

    itemFields = list(PatchHelper.list_format)

    # Category specified?
    if Category:
        CategoryIds = (str(Category), )
        if not CategoryExact:  # Exact category NOT specified?
            InstDB.Cur.execute("SELECT CategoryName FROM Category"
                               " WHERE CategoryId=%s", Category)  # Name
            res = InstDB.Cur.fetchone()
            if res:  # Get list of sub categories (if any)
                InstDB.Cur.execute(
                    "SELECT CategoryId FROM Category"
                    " WHERE LEFT(CategoryName, %s) = %s",
                    (len(res["CategoryName"]) + 1, res["CategoryName"] + ':'))
                for row in InstDB.Cur.fetchall():
                    CategoryIds += (str(row["CategoryId"]), )

        # Remove category from item fields
        if "CategoryName" in itemFields:
            itemFields.remove("CategoryName")

    # Create SQL query
    sel = PatchHelper.sql_select(itemFields)

    # Make sure order by field is valid
    if OrderBy and not PatchHelper.field_searchable(OrderBy): OrderBy = None
    if not OrderBy: OrderBy = "FileName"

    sel.orderby = PatchHelper.sql_order_field(OrderBy, OrderDir)
    sel.limit = "%d,%d" % (Offset, Count)
    sel.where.append("PatchInfo.State = 'Active'")
    sel.calc_found_rows = True

    if Category:
        # Match categories (1st, 2nd or 3rd)
        s = string.join(CategoryIds, ',')
        sel.where.insert(
            0, "(PatchInfo.CategoryId IN (%s)"
            " || PatchInfo.CategoryId2 IN (%s)"
            " || PatchInfo.CategoryId3 IN (%s))" % (s, s, s))
        if not "PatchInfo.CategoryId" in sel.fields:
            sel.fields += ("PatchInfo.CategoryId", )

    InstDB.Cur.execute(sel.query())
    rows = InstDB.Cur.fetchall()

    # Get total number of matching items
    InstDB.Cur.execute("SELECT FOUND_ROWS() AS Total")
    row = InstDB.Cur.fetchone()
    total = int(row["Total"])

    # Re URL-ify the input fields
    pager_urlstr = urlencode(
        (("Action", "list"), ("Type", "Patch"), ("Category", Category),
         ("OrderBy", OrderBy), ("OrderDir", OrderDir), ("Count", Count)))
    pager_urlstr = cgi.escape(pager_urlstr)

    # Display the pager bar
    InstDB.show_pager(req, pager_urlstr, Offset, Count, total)

    # Create clickable titles that sort output
    urlstr = urlencode((("Action", "list"), ("Type", "Patch"),
                        ("Category", Category), ("Count", Count)))
    urlstr = cgi.escape(urlstr)

    sortList = []
    for field in itemFields:
        if PatchHelper.field_searchable(field):
            sortList += ((field, PatchHelper.field_title(field)), )
        else:
            sortList += (PatchHelper.field_title(field), )

    titles = sort_titles(sortList, urlstr, OrderBy, OrderDir, PatchHelper)

    if rows:
        table = InstDB.tabular(titles)
        table.tableattr = 'width="100%"'

        for row in rows:
            req.write(table.addrow(PatchHelper.html_vals(itemFields, row)))

        req.write(table.end())
    else:
        req.write("<b>No files match criteria</b>\n")

    # Display the pager bar
    InstDB.show_pager(req, pager_urlstr, Offset, Count, total)
Beispiel #25
0
        os.rmdir(extractDir)
        raise InstDB.ImportError, "Unknown file type"

    # Import the instrument information
    ItemId = PatchImport.import_patch(extractDir, UserId)

    # Move original file to original/<ItemId> (purged after some time)
    os.rename(importFileName, InstDB.OriginalPath + os.sep + str(ItemId))

    return ItemId


# Main outer loop (persistently tries to connect to database)
while True:
    try:
        InstDB.connect()
    except MySQLdb.Error, detail:
        log("Failed to connect to database: %s" % detail)
        time.sleep(CONNECT_RETRY_INTERVAL)
        continue

    # Inner loop polls for active tasks and executes them
    while True:
        try:
            InstDB.Cur.execute("SELECT * FROM Queue"
                               " WHERE Status='Queued' && Type IN (%s)"
                               " ORDER BY QueueId" % taskTypes)
            QueueId = 0

            for row in InstDB.Cur.fetchall():  # For each queued task
                QueueId = row["QueueId"]
Beispiel #26
0
def action(req, fields):
    text = fields.get("Text", "")
    offset = int(fields.get("Offset", 0))
    count = int(fields.get("Count", 20))

    exceeded = 0  # set to 1 if max_results is exceeded

    try:
        InstDB.Cur.execute("DROP TABLE _scores")
        InstDB.Cur.execute("DROP TABLE _total_scores")
    except MySQLdb.OperationalError:
        pass

    # Create temporary scoring table
    InstDB.Cur.execute("""\
CREATE TEMPORARY TABLE _scores (
pid INT UNSIGNED NOT NULL,
rating INT UNSIGNED NOT NULL,
flag SMALLINT UNSIGNED NOT NULL,
INDEX (pid))""")

    # Create temporary total scores table
    InstDB.Cur.execute("""\
CREATE TEMPORARY TABLE _total_scores (
pid INT UNSIGNED NOT NULL,
rating INT UNSIGNED NOT NULL,
flags SMALLINT UNSIGNED NOT NULL,
INDEX (pid),
INDEX (rating))""")

    # Insert scores for matches to Patch Name or FileName
    rows = InstDB.Cur.execute(
        """\
INSERT INTO _scores
SELECT PatchId, %s, %s FROM PatchInfo
WHERE INSTR(PatchName, %s) || INSTR(FileName, %s)
LIMIT %s""", (patch_name_score, patch_name_score, text, text, max_results))
    if rows == max_results: exceeded = 1

    # Insert scores for matches to Patch properties (each property ++)
    rows = InstDB.Cur.execute(
        """\
INSERT INTO _scores
SELECT ItemProps.ItemId, COUNT(*) * %s, %s FROM ItemProps, Items
WHERE ItemProps.ItemId=Items.ItemId && Items.ItemType='Patch'
&& INSTR(PropValue, %s) GROUP BY ItemProps.ItemId LIMIT %s""",
        (patch_prop_score, patch_prop_score, text, max_results))
    if rows == max_results: exceeded = 1

    # Insert scores for matches to instrument names (each instrument ++)
    rows = InstDB.Cur.execute(
        """\
INSERT INTO _scores
SELECT PatchId, COUNT(*) * %s, %s FROM InstInfo
WHERE INSTR(InstName, %s)
GROUP BY PatchId LIMIT %s""",
        (inst_name_score, inst_name_score, text, max_results))
    if rows == max_results: exceeded = 1

    # Insert scores for matches to instrument properties (each property ++)
    rows = InstDB.Cur.execute(
        """\
INSERT INTO _scores
SELECT InstInfo.PatchId, COUNT(*) * %s, %s FROM ItemProps, Items, InstInfo
WHERE ItemProps.ItemId=Items.ItemId && Items.ItemType='Inst'
&& INSTR(PropValue, %s) && ItemProps.ItemId=InstInfo.InstId
GROUP BY InstInfo.PatchId LIMIT %s""",
        (inst_prop_score, inst_prop_score, text, max_results))
    if rows == max_results: exceeded = 1

    # Insert scores for matches to sample names (each sample ++)
    rows = InstDB.Cur.execute(
        """\
INSERT INTO _scores
SELECT PatchId, COUNT(*) * %s, %s FROM SampleInfo
WHERE INSTR(SampleName, %s)
GROUP BY PatchId LIMIT %s""",
        (sample_name_score, sample_name_score, text, max_results))
    if rows == max_results: exceeded = 1

    # Insert scores for matches to sample properties (each property ++)
    rows = InstDB.Cur.execute(
        """\
INSERT INTO _scores
SELECT SampleInfo.PatchId, COUNT(*) * %s, %s FROM ItemProps, Items, SampleInfo
WHERE ItemProps.ItemId=Items.ItemId && Items.ItemType='Sample'
&& INSTR(PropValue, %s) && ItemProps.ItemId=SampleInfo.SampleId
GROUP BY SampleInfo.PatchId LIMIT %s""",
        (sample_prop_score, sample_prop_score, text, max_results))
    if rows == max_results: exceeded = 1

    # Sum up all the ratings into temp total ratings table
    InstDB.Cur.execute("""\
INSERT INTO _total_scores
SELECT pid, SUM(rating), BIT_OR(flag) FROM _scores, PatchInfo
WHERE pid = PatchInfo.PatchId && PatchInfo.State='Active'
GROUP BY pid""")

    # Drop temp ratings table
    InstDB.Cur.execute("DROP TABLE _scores")

    # Get max rating
    InstDB.Cur.execute("SELECT MAX(rating) AS max_rating,"
                       " COUNT(*) AS total_count FROM _total_scores")
    row = InstDB.Cur.fetchone()
    max_rating = row["max_rating"]
    total_count = row["total_count"]

    itemCells = (("FileNameLink", ), ("PatchName", ),
                 ("DownloadLink", "PatchType", "FileSize"),
                 ("Rating", "DateImported"), ("CategoryName", "LicenseName"),
                 ("Version", "WebSiteLink", "Email"))

    # convert sequence of sequence of strings to list of string fields
    itemFields = []
    for cell in itemCells:
        for field in cell:
            itemFields += (field, )

    sel = PatchHelper.sql_select(itemFields)  # Create SELECT query

    sel.where.insert(0, "PatchInfo.State = 'Active'")
    sel.where.insert(0, "_total_scores.pid=PatchInfo.PatchId")
    sel.tables += ("_total_scores", )
    sel.orderby = "rating DESC"
    sel.fields += ("pid", "rating", "flags")
    sel.limit = "%d, %d" % (offset, count)

    InstDB.Cur.execute(sel.query())

    req.write(InstDB.header("Search results"))

    if total_count == 0:
        req.write("<b>No matches to '%s'</b>\n" % (cgi.escape(text)))
        req.write(InstDB.footer())
        InstDB.Cur.execute("DROP TABLE _total_scores")
        return

    if exceeded: warn = " (results truncated due to excessive matches)"
    else: warn = ""

    if total_count == 1:
        matchstr = "Found 1 match to '%s'%s\n" % (cgi.escape(text), warn)
    else:        matchstr = "Found %d matches to '%s'%s\n" \
% (total_count, cgi.escape (text), warn)

    req.write("<b>" + matchstr + "</b>\n")

    # Re URL-ify the input fields
    urlstr = urlencode(
        (("Action", "search"), ("Text", text), ("Count", count)))
    urlstr = cgi.escape(urlstr)

    # Display the result pager bar
    InstDB.show_pager(req, urlstr, offset, count, total_count)

    req.write("<p>\n")

    # loop over matches
    for row in InstDB.Cur.fetchall():

        score = int(float(row["rating"]) / float(max_rating) * 100.0)
        if score == 0: score = 1
        scoreStr = "Score %d (%s/%s)" % (score, row["rating"], max_rating)

        # box start for result item
        box = InstDB.html_box()
        box.fontsize = "+0"
        box.tableattr = 'width="640"'
        req.write(box.start(scoreStr))

        tab = PatchHelper.cell_vals(itemCells, row)

        split = InstDB.tabsplits(tab)
        split.tableattr = 'width="100%"'
        req.write(split.render())

        # Create array of matching info to display (Type, Name, Value)
        matches = ()
        flags = int(row["flags"])

        esctext = InstDB.sqlescape(text)

        if flags & patch_prop_score:  # Patch property matches?
            query = """\
SELECT '%s' AS type, P.PropName AS name, I.PropValue AS value
FROM ItemProps AS I, Props AS P
WHERE I.ItemId=%d && INSTR(I.PropValue, '%s') && I.PropId=P.PropId""" \
            % ("Patch Property", int (row["pid"]), esctext)
            matches = query_matches(query, matches, text)

        if flags & inst_name_score:  # Instrument name matches?
            query = "SELECT '%s' AS type, '%s' AS name, InstName AS value" \
                    " FROM InstInfo WHERE PatchId=%d && INSTR(InstName, '%s')" \
                    % ("Instrument", "Name", int (row["pid"]), esctext)
            matches = query_matches(query, matches, text)

        if flags & inst_prop_score:  # Instrument property matches?
            query = """\
SELECT '%s' AS type, Props.PropName AS name, ItemProps.PropValue AS value
FROM ItemProps, Props, Items, InstInfo
WHERE Items.ItemType='Inst' && INSTR(ItemProps.PropValue, '%s')
&& ItemProps.ItemId=InstInfo.InstId && InstInfo.PatchId=%d
&& ItemProps.ItemId=Items.ItemId && ItemProps.PropId=Props.PropId""" \
            % ("Instrument Property", esctext, int (row["pid"]))
            matches = query_matches(query, matches, text)

        if flags & sample_name_score:  # Sample name matches?
            query = "SELECT '%s' AS type, '%s' AS name, SampleName AS value" \
                    " FROM SampleInfo WHERE PatchId=%d" \
                    " && INSTR(SampleName, '%s')" \
                    % ("Sample", "Name", int (row["pid"]), esctext)
            matches = query_matches(query, matches, text)

        if flags & sample_prop_score:  # Sample property matches?
            query = """\
SELECT '%s' AS type, Props.PropName AS name, ItemProps.PropValue AS value
FROM ItemProps, Props, Items, SampleInfo
WHERE Items.ItemType='Sample' && INSTR(ItemProps.PropValue, '%s')
&& ItemProps.ItemId=SampleInfo.SampleId && SampleInfo.PatchId=%d
&& ItemProps.ItemId=Items.ItemId && ItemProps.PropId=Props.PropId""" \
            % ("Sample Property", esctext, int (row["pid"]))
            matches = query_matches(query, matches, text)

        if matches:
            table = InstDB.tabular(("Item", "Field", "Value"))
            table.tableattr = 'width="100%"'
            table.cellspacing = 2
            for m in matches:
                req.write(table.addrow(m))
            req.write(table.end())

        req.write(box.end())
        req.write("<p>\n")

    req.write("<p>\n")

    InstDB.show_pager(req, urlstr, offset, count, total_count)

    req.write(InstDB.footer())

    InstDB.Cur.execute("DROP TABLE _total_scores")
Beispiel #27
0
def action(req, fields):

    if not InstDB.User or "Admin" not in InstDB.User["Flags"]:
        req.write(InstDB.header("Admin only"))
        InstDB.error(req,
                     "Administrator privileges required for this function")
        req.write(InstDB.footer())
        return

    req.write(InstDB.header("Email news"))

    subject = fields.get("Subject", None)
    msg = fields.get("Message", None)

    # Message submitted?
    if subject and msg:
        message = "Subject: " + subject + "\r\n\r\n" + msg

        query = "SELECT UserId FROM Users WHERE !FIND_IN_SET('NoLogin', Flags)"

        allUsers = fields.get("AllUsers", None)

        if not allUsers:
            query += " && FIND_IN_SET('EmailNews', UserFlags)"

        InstDB.Cur.execute(query)

        for row in InstDB.Cur.fetchall():
            # Queue email task
            InstDB.Cur.execute(
                "INSERT INTO Queue (Type, Status, UserId, Content)"
                " VALUES ('Email', 'Queued', %s, %s)",
                (row["UserId"], message))

        if allUsers: allstr = "ALL"
        else: allstr = "EmailNews"

        req.write('<font class="title">Sent message to %s users.</font>\n' %
                  allstr)
        req.write(InstDB.footer())
        return

    box = InstDB.html_box()
    req.write(box.start("Email news"))

    req.write("""
<form action="patches.py?Action=emailnews" method=post>
<table border=0 cellpadding=8>
<tr>
<td><b>Subject:</b></td>
<td><input type=text name=Subject size=64></td>
</tr>
<tr>
<td><b>All users:</b></td>
<td><input type=checkbox name=AllUsers> Email <b>ALL</b> users?</td>
</tr>
<tr>
<td colspan=2>
<textarea name=Message rows=16 cols=80>
</textarea>
</td>
</tr>
<tr>
<td colspan=2>
<input type=submit name=Submit value=Submit>
</td>
</tr>
</table>
</form>
""")

    req.write(box.end())
    req.write(InstDB.footer())
Beispiel #28
0
def action(req, fields):
    sums = fields.get("Md5Sums", None)

    req.write(InstDB.header("MD5 Find"))

    # Form submitted?
    if sums:
        table = InstDB.tabular(["MD5", "Label", "FileName"])
        table.tableattr = 'width="100%"'

        reObj = re.compile("[0-9a-fA-F]{32}")

        for line in sums.split('\n'):
            if not reObj.match(line): continue

            md5 = line[:32]
            label = line[32:].strip()

            InstDB.Cur.execute(
                "SELECT PatchId, FileName FROM Files WHERE Md5=%s", md5)
            result = InstDB.Cur.fetchone()

            if result:
                filelink = '<a href="patches.py?Action=item&amp;ItemId=%d">%s</a>' \
                    % (result["PatchId"], result["FileName"])
            else:
                filelink = "Not found"

            req.write(table.addrow([md5, label, filelink]))

        req.write(table.end())
        req.write(InstDB.footer())
        return

    box = InstDB.html_box()
    req.write(box.start("MD5 Find"))

    req.write("""
<form action="" method=post>
<table border=0 cellpadding=8>
<tr>
<td>
Enter one MD5 signature per line optionally followed by white space and a
file name (or any other text).  The MD5 signatures will be searched for in the
database.  The correct text output can be obtained from programs like <b>md5sum</b> and
others (see Wikipedia <a href="http://en.wikipedia.org/wiki/Md5sum">Md5sum</a>).<br>
Example: <i>752091450e308127a4d70c7623dd4e37  Fluid.sf2</i><br>
</td>
</tr>
<tr>
<td>
<textarea name=Md5Sums rows=16 cols=80>
</textarea>
</td>
</tr>
<tr>
<td>
<input type=submit name=Submit value=Submit>
</td>
</tr>
</table>
</form>
""")

    req.write(box.end())
    req.write(InstDB.footer())
Beispiel #29
0
def action(req, fields):
    req.write(InstDB.header("Browse by Category"))

    # Get count of active patch files for primary category
    InstDB.Cur.execute(
        "SELECT CategoryId, COUNT(*) AS UseCount FROM PatchInfo" +
        " WHERE State='Active' GROUP BY CategoryId")
    UseCounts = {}
    for row in InstDB.Cur.fetchall():
        UseCounts[row["CategoryId"]] = row["UseCount"]

    # Get count of active patch files for secondary category
    InstDB.Cur.execute("SELECT CategoryId2, COUNT(*) AS UseCount"
                       " FROM PatchInfo WHERE CategoryId2 != 0"
                       " && State='Active' GROUP BY CategoryId2")
    for row in InstDB.Cur.fetchall():
        oldcount = UseCounts.get(row["CategoryId2"], 0)
        UseCounts[row["CategoryId2"]] = oldcount + row["UseCount"]

    # Get count of active patch files for 3rd category
    InstDB.Cur.execute("SELECT CategoryId3, COUNT(*) AS UseCount"
                       " FROM PatchInfo WHERE CategoryId3 != 0"
                       " && State='Active' GROUP BY CategoryId3")
    for row in InstDB.Cur.fetchall():
        oldcount = UseCounts.get(row["CategoryId3"], 0)
        UseCounts[row["CategoryId3"]] = oldcount + row["UseCount"]

    # Select all categories (including sub categories)
    InstDB.Cur.execute("SELECT CategoryId, CategoryName FROM Category"
                       " ORDER BY CategoryName")

    req.write("<table border=0 cellpadding=4 cellspacing=0>\n")

    # Loop over categories, displaying only master categories and summing
    # sub category use counts
    count = 0
    dispcount = 0
    rows = InstDB.Cur.fetchall()
    for i in range(0, len(rows)):
        row = rows[i]

        # Next row or None if last
        if i + 1 < len(rows): nextRow = rows[i + 1]
        else: nextRow = None

        # Store ID and name of master category
        if string.find(row["CategoryName"], ':') == -1:
            id = row["CategoryId"]
            name = row["CategoryName"]

        count += UseCounts.get(row["CategoryId"], 0)

        # Next row is a sub category? - yes: still processing a master category
        if nextRow and string.find(nextRow["CategoryName"], ':') != -1:
            continue

        if dispcount % 4 == 0:
            if dispcount != 0: req.write("</tr>\n")
            req.write("<tr>\n")

        (width, height) = image_sizes.get(name, [100, 100])

        req.write("""\
<td align="center">
<a href="patches.py?Action=list&amp;Category=%s">
<img src="images/category/%s.png" alt="Category Image" border=0 width=%d height=%d><br>
<b>%s</b> (%d)
</a>
</td>
""" % (id, name, width, height, name, count))

        count = 0
        dispcount += 1

    if dispcount % 4 != 1: req.write("</tr>\n")

    req.write("</table>")

    req.write(InstDB.footer())