another day

This commit is contained in:
j 2007-04-05 18:37:32 +00:00
parent 04b0a2bae9
commit f7f3753cbf
76 changed files with 683 additions and 297 deletions

View file

@ -21,10 +21,14 @@ from sortname import sortname
def httpExpires(sec):
return cherrypy.lib.httptools.HTTPDate(time.gmtime(time.mktime(time.gmtime()) + sec))
def _default_search_values():
return dict(q = '', f = 'all', s = 'title', o = 0, n = 60, l = 'all', v = 'icon', length = 0)
class View:
@expose(template=".templates.view")
def view(self, item):
return dict(item = item)
search = cherrypy.session.get('search', _default_search_values())
return dict(item = item, search = search)
def icon(self, item):
response.headerMap['Content-Type'] = "image/png"
@ -48,6 +52,9 @@ class View:
return self.icon(item)
elif args[0].startswith('icon_reflection.'):
return self.icon_reflection(item)
elif args[0] == 'update':
item.archive.updateItem(item.archiveItemId)
raise redirect("/view/%s" % item.hashId)
elif args[0] == 'json':
return item.json
@ -69,11 +76,33 @@ class Admin:
@validate(form = forms.add_archive)
@error_handler(archives)
def archives_add(self, **data):
print data
try:
url = "%s?metadata=1" % data['archiveUrl']
result = simplejson.loads(read_url(url))
print result
except:
result = None
if result and result.has_key('name') \
and result.has_key('id') \
and result.has_key('ttl'):
archiveId = result['id']
archiveName = result['name']
ttl = int(result['ttl'])
if result.has_key('icon'):
icon = result['icon']
else:
icon = ''
else:
tg_errors ="Not a valid JSON Backend"
return "FIXME"
new = Archive(
archiveName = data['archiveName'],
archiveType = data['archiveType'],
archiveName = archiveName,
archiveId = archiveId,
archiveUrl = data['archiveUrl'],
ttl = int(data['ttl']),
icon = icon,
ttl = ttl,
hashId = "%s" % time.mktime(time.localtime()),
)
new.setHashId()
raise redirect('archives')
@ -125,13 +154,27 @@ class ArchiveStyleSheet:
cherrypy.response.headerMap["Expires"] = httpExpires(60) #(60*60*24*30)
return archive.css
class Root(controllers.RootController):
class ArchiveIcon:
@expose()
def default(self, name):
name = name.split('.')[0]
archive = Archive.byHashId(name)
response.headerMap['Content-Type'] = "image/png"
cherrypy.response.headerMap["Expires"] = httpExpires(60*60*24*30)
return oilcache.loadArchiveIcon(archive)
class Root(controllers.RootController):
view = View()
admin = Admin()
api = Api()
js = ArchiveJavascript()
css = ArchiveStyleSheet()
icon = ArchiveIcon()
@expose()
def redirect(self, *kw, **args):
url = "?".join(cherrypy.request.browserUrl.split('?')[1:])
raise redirect(url)
@expose(template=".templates.login")
def login(self, forward_url=None, previous_url=None, *args, **kw):
@ -163,8 +206,6 @@ class Root(controllers.RootController):
identity.current.logout()
raise redirect("/")
def default_search_values(self):
return dict(q = '', f = 'all', s = 'title', o = 0, n = 60, l = 'all', v = 'icon', length = 0)
_sort_map = {
'id': 'hashId',
@ -184,6 +225,7 @@ class Root(controllers.RootController):
'title': ArchiveItem.q.title,
'author': ArchiveItem.q.author,
'genre': ArchiveItem.q.genre,
'date': ArchiveItem.q.relDate,
}
_search_map = {
@ -192,7 +234,7 @@ class Root(controllers.RootController):
@expose(template=".templates.iconview")
def search(self, q = '', f = None, s = None, o = -1, n = None, l = None, v = None):
search = cherrypy.session.get('search', self.default_search_values())
search = cherrypy.session.get('search', _default_search_values())
if not v:
v = search['v']
if not l:
@ -219,10 +261,10 @@ class Root(controllers.RootController):
orderBy = [self.get_sort(s), 'title_sort', 'rel_date']
if q:
q = q.encode('utf-8')
if f=='all':
items = queryArchive(q, s)
elif f in ('title', 'author', 'genre'):
q = q.encode('utf-8')
elif f in ('title', 'author', 'genre', 'date'):
items = ArchiveItem.select(LIKE(self._field_map[f], '%'+q+'%') , orderBy=orderBy)
else:
items = ArchiveItem.select(orderBy = orderBy)

View file

@ -20,12 +20,12 @@ def updateSortAuthorNames():
'''
def spiderArchives():
for archive in Archive.select(Archive.q.initialized == True):
if archive.modDate - datetime.now() < timedelta(minutes = archive.ttl):
if archive.modDate - datetime.now() < timedelta(seconds = archive.ttl):
print "updating", archive.archiveName
archive.update()
else:
print "skipping", archive.archiveName
def runCron():
spiderArchives()
#spiderArchives()
updateSortAuthorNames()

View file

@ -14,11 +14,7 @@ class forms:
add_archive = widgets.TableForm(
fields=[
widgets.TextField(name="archiveName",label="Name"),
widgets.TextField(name="archiveType",label="Type"),
widgets.TextField(name="archiveUrl",label="JSON url"),
widgets.TextField(name="ttl",label="Update Interval(Minutes)"),
],
submit_text="Save"
)

View file

@ -29,14 +29,17 @@ def queryArchive(query, orderBy="score", offset = 0, count = 100):
orderBy = orderBy.encode('utf-8')
print orderBy
if orderBy not in ('score', 'size', 'title', 'description'):
orderBy = 'score'
orderBy = 'score DESC, title'
if orderBy == 'size':
orderBy = "size DESC"
match = "MATCH (title, description, text) AGAINST ('%s')" % query
sql = """SELECT id, %s AS score, title, size, description FROM archive_item
match = '''MATCH (title, description, text) AGAINST ('%s')''' % query
match_b = '''MATCH (title, description, text) AGAINST ('%s' IN BOOLEAN MODE)''' % query
sql = """SELECT id, ((100000/LENGTH(text)) * %s) AS score, title, size, description FROM archive_item
WHERE %s ORDER BY %s""" % \
(match, match, orderBy) #, offset, count)
(match_b, match_b, orderBy) #, offset, count)
result = []
max_score= None
print sql
matches = ArchiveItem._connection.queryAll(sql)
if len(matches) > offset:
@ -45,7 +48,9 @@ WHERE %s ORDER BY %s""" % \
matches = matches[:count]
for m in matches:
item = ArchiveItem.get(m[0])
item.score = m[1]
if not max_score:
max_score = m[1] / 100
item.score = m[1] / max_score
result.append(item)
return result
@ -67,8 +72,10 @@ class ArchiveItem(SQLObject):
downloadUrl = UnicodeCol() # -> url (link to item)
storeUrl = UnicodeCol() # -> url (link to store)
size = IntCol() #bytes
rights = IntCol(default = 5) #-> int: 0 (free) - 5 (unfree)
itemType = UnicodeCol() #string (Text, Pictures, Music, Movies, Software)
rightsLevel = IntCol(default = 5) #-> int: 0 (free) - 5 (unfree)
rightsText = UnicodeCol(default = '')
kind = UnicodeCol() #string (Text, Pictures, Music, Movies, Software)
fileType = UnicodeCol() #fileType (pdf, txt etc)
genre = UnicodeCol(default = '')
archive = ForeignKey('Archive')
@ -80,9 +87,17 @@ class ArchiveItem(SQLObject):
#Fulltext search
#ALTER TABLE archive_item ADD FULLTEXT (title, description, text);
def _get_filetype(self):
return self.downloadUrl.split('.')[-1].upper()
def _get_sizeFormated(self):
return utils.formatFileSize(self.size)
def getPreview(self, sort):
if sort == 'size':
return utils.formatFileSize(self.size)
return self.sizeFormated
if sort == 'relevance':
return "%d" % self.score
return self.relDateFormated
def _set_author(self, value):
@ -90,15 +105,30 @@ class ArchiveItem(SQLObject):
if not self.authorSort:
self.authorSort = value
def _set_title(self, value):
self._SO_set_title(value)
if not self.titleSort:
self.titleSort = value
def _get_year(self):
return self.relDate.strftime('%Y')
def rightsLevelClass(self, level):
if level == self.rightsLevel:
return "rightsLevelActive"
return "rightsLevelInactive"
def _get_relDateFormated(self):
if self.itemType in ('Movie', 'Book'):
if self.kind in ('Movie', 'Book'):
return self.year
else:
return self.relDate.strftime('%Y-%m-%d')
def domain(self, url):
d = url.split('/')
if len(d) > 2:
return d[2].split('?')[0]
return url
#expand urls in case they are relative to the archive
def _get_archiveUrl(self):
return self.archive.full_url(self._SO_get_archiveUrl())
@ -113,6 +143,7 @@ class ArchiveItem(SQLObject):
result = jsonify_sqlobject(self)
result['relDate'] = self.relDate.strftime('%s')
result['pubDate'] = self.pubDate.strftime('%s')
result['modDate'] = self.relDate.strftime('%s')
return result
'''
return dict(
@ -134,24 +165,25 @@ class ArchiveItem(SQLObject):
def update(self, data):
for key in data:
setattr(self, key, data[key])
self.updateHashID()
self.setHashId()
def updateHashID(self):
def setHashId(self):
salt = u'%s/%s' % (self.archive.archiveName, self.archiveItemId)
self.hashID = md5.new(salt.encode('utf-8')).hexdigest()
class Archive(SQLObject):
archiveName = UnicodeCol(alternateID = True, length = 1000)
archiveId = UnicodeCol(alternateID = True, length = 1000)
archiveName = UnicodeCol()
archiveUrl = UnicodeCol()
archiveType = UnicodeCol(default=u'')
ttl = IntCol(default = "15")
ttl = IntCol(default = "900") #seconds
pubDate = DateTimeCol(default=datetime.now)
modDate = DateTimeCol(default=datetime.now)
created = DateTimeCol(default=datetime.now)
initialized = BoolCol(default = False)
css = UnicodeCol(default='')
js = UnicodeCol(default='')
icon = UnicodeCol() # -> url (128x128)
hashId = UnicodeCol(alternateID = True, length=128)
@ -171,13 +203,15 @@ class Archive(SQLObject):
def _get_update_url(self):
return self._query_url({'modDate': self.modDateTimestamp})
def _get_files_url(self):
return self._query_url({'files': '1'})
def _get_metadata_url(self):
return self._query_url({'metadata': '1'})
def data_url(self, id):
return self._query_url({'id': id})
def full_url(self, url):
if not url:
return ''
if url.find('://') > 0:
return url
if url.startswith('/'):
@ -187,37 +221,63 @@ class Archive(SQLObject):
url = "%s/%s" % (self.archiveUrl, url)
return url
def _get_iconUrl(self):
if self.icon:
return "/icon/%s.png" % self.hashId
else:
return "/static/images/iconCollection.png"
def update(self):
result = simplejson.loads(read_url(self.files_url))
if result and result.has_key('css'):
self.css = read_url(self.full_url(result['css']))
else:
self.css = ''
if result and result.has_key('js'):
self.js = read_url(self.full_url(result['js']))
result = simplejson.loads(read_url(self.metadata_url))
if result:
if result.has_key('name'):
self.archiveName = result['name']
if result.has_key('id'):
self.archiveId = result['id']
if result.has_key('ttl'):
self.ttl = int(result['ttl'])
if result.has_key('icon'):
self.icon = result['icon']
if result.has_key('css'):
try:
data = read_url(self.full_url(result['css']))
self.css = data
except:
self.css = ''
if result.has_key('js'):
try:
data = read_url(self.full_url(result['js']))
self.js = data
except:
self.js = ''
else:
self.icon = ''
self.js = ''
self.css = ''
result = simplejson.loads(read_url(self.update_url))
items = result.get('items', [])
print "importing", len(items), "items"
for id in items:
try:
data = read_url(self.data_url(id))
data = jsonLoadArchiveItem(data)
print data['title'].encode('utf-8')
self.updateItem(id)
except:
print "failed to load ", id, "from ", self.data_url(id)
continue
q = ArchiveItem.select(AND(
ArchiveItem.q.archiveItemId == id,
ArchiveItem.q.archiveID == self.id))
if q.count() == 0:
jsonImportArchiveItem(self, id, data)
else:
q[0].update(data)
self.initialized = True
self.modDate = datetime.now()
def updateItem(self, id):
data = read_url(self.data_url(id))
data = jsonLoadArchiveItem(data)
print data['title'].encode('utf-8')
q = ArchiveItem.select(AND(
ArchiveItem.q.archiveItemId == id,
ArchiveItem.q.archiveID == self.id))
if q.count() == 0:
jsonImportArchiveItem(self, id, data)
else:
q[0].update(data)
'''
get list of all items from archive and remove those from ArchiveItem that
are no longer in the list

View file

@ -85,3 +85,16 @@ def loadIconReflection(item):
else:
return ''
return loadFile(iconReflection)
'''
return icon data, reads from remote url if not cached
'''
def loadArchiveIcon(archive):
icon = iconPath('archiveIcon', archive)
if exists(icon):
data = loadFile(icon)
else:
data = read_url(archive.icon)
saveFile(icon, data)
return data

View file

@ -5,10 +5,12 @@
from datetime import datetime
import time
import md5
import simplejson
from scrapeit.utils import stripTags
import model
import md5
import utils
def jsonLoadArchiveItem(data):
json_array = simplejson.loads(data)
@ -21,9 +23,15 @@ def jsonLoadArchiveItem(data):
json_array['storeUrl'] = json_array.pop('storeURL')
for key in ('relDate', 'pubDate', 'modDate'):
json_array[key] = datetime.utcfromtimestamp(float(json_array[key]))
for key in ('rights', 'size'):
for key in ('rightsLevel', 'size'):
json_array[key] = int(json_array[key])
json_array['itemType'] = json_array.pop('type', 'Text')
json_array['fileType'] = json_array.pop('type', 'unknown')
for key in ('title', 'description'):
json_array[key] = stripTags(json_array[key])
for key in ('storeUrl', 'archiveUrl', 'title', 'description'):
json_array[key] = json_array.get(key, u'')
json_array['html'] = utils.fix_ampersands(json_array['html'])
return json_array
@ -37,7 +45,8 @@ def jsonImportArchiveItem(archive, archiveItemId, json_array):
hashId = hashID,
archiveItemId = "%s" % archiveItemId,
description=json_array['description'],
rights=json_array['rights'],
rightsLevel=json_array['rightsLevel'],
rightsText=json_array['rightsText'],
text=json_array['text'],
author=json_array['author'],
pubDate=json_array['pubDate'],
@ -50,6 +59,8 @@ def jsonImportArchiveItem(archive, archiveItemId, json_array):
genre=json_array['genre'],
title=json_array['title'],
size=json_array['size'],
itemType=json_array['itemType'],
fileType=json_array['fileType'],
kind=json_array['kind'],
icon= json_array['icon']
)
i.setHashId()

View file

@ -69,7 +69,7 @@ input {
font-size: 8px;
}
.item {
.oil21_item {
position: relative;
left: 0px;
top: 0px;
@ -120,78 +120,8 @@ input {
}
.listItemLink {
width: 128px;
height: 128px;
}
.item .textIconLarge {
.oil21_item .textIconLarge {
color: rgb(0, 0, 0);
}
table {
border-collapse: collapse;
border-spacing: 0px;
}
td {
padding: 0px;
}
#itemPageIcon {
width: 128px;
padding-left: 8px;
padding-right: 8px;
}
#itemPageText {
padding-left: 8px;
padding-right: 8px;
}
#itemPageTextLeftTop {
width: 8px;
height: 8px;
background: url(/static/images/itemPageTextLeftTop.png)
}
#itemPageTextCenterTop {
height: 8px;
background: url(/static/images/itemPageTextCenterTop.png);
}
#itemPageTextRightTop {
width: 8px;
height: 8px;
background: url(/static/images/itemPageTextRightTop.png)
}
#itemPageTextLeftMiddle {
width: 8px;
background: url(/static/images/itemPageTextLeftMiddle.png)
}
#itemPageTextCenterMiddle {
background: url(/static/images/itemPageTextCenterMiddle.png);
}
#itemPageTextRightMiddle {
width: 8px;
background: url(/static/images/itemPageTextRightMiddle.png)
}
#itemPageTextLeftBottom {
width: 8px;
height: 8px;
background: url(/static/images/itemPageTextLeftBottom.png)
}
#itemPageTextCenterBottom {
height: 8px;
background: url(/static/images/itemPageTextCenterBottom.png);
}
#itemPageTextRightBottom {
width: 8px;
height: 8px;
background: url(/static/images/itemPageTextRightBottom.png)
}

View file

@ -0,0 +1,149 @@
table {
border-collapse: collapse;
border-spacing: 0px;
}
td {
padding: 0px;
}
div {
font-family: Lucida Grande;
}
#itemPageText {
width: 864px;
margin-left: auto;
margin-right: auto;
margin-top: 8px;
margin-bottom: 16px;
}
.boxWhiteLeftTop {
width: 8px;
height: 8px;
background: url(/static/images/boxWhiteLeftTop.png);
}
.boxWhiteCenterTop {
height: 8px;
background: url(/static/images/boxWhiteCenterTop.png);
}
.boxWhiteRightTop {
width: 8px;
height: 8px;
background: url(/static/images/boxWhiteRightTop.png);
}
.boxWhiteLeftMiddle {
width: 8px;
background: url(/static/images/boxWhiteLeftMiddle.png);
}
.boxWhiteCenterMiddle {
background: url(/static/images/boxWhiteCenterMiddle.png);
text-align: center;
}
.boxWhiteRightMiddle {
width: 8px;
background: url(/static/images/boxWhiteRightMiddle.png);
}
.boxWhiteLeftBottom {
width: 8px;
height: 8px;
background: url(/static/images/boxWhiteLeftBottom.png);
}
.boxWhiteCenterBottom {
height: 8px;
background: url(/static/images/boxWhiteCenterBottom.png);
}
.boxWhiteRightBottom {
width: 8px;
height: 8px;
background: url(/static/images/boxWhiteRightBottom.png);
}
.boxData {
width: 144px;
height: 192px;
margin: 8px;
padding: 8px;
background: url(/static/images/boxData.png) center no-repeat;
text-align: center;
float: left;
}
.boxData img {
margin-top: 8px;
}
.iconText {
padding-top: 8px;
}
.rightsLevel {
width: 128px;
height: 32px;
margin-left: 8px;
text-align: center;
}
.textBold {
font-weight: bold;
}
.textLarge {
font-size: 12px;
}
.textMedium {
font-size: 11px;
}
.textSmall {
font-size: 10px;
}
.textXSmall {
font-size: 9px;
}
.textXXSmall {
font-size: 8px;
}
.textSpacing {
letter-spacing: 1px;
}
.textCenter {
text-align: center;
}
.textWhite {
color: rgb(255, 255, 255);
}
.rightsLevelActive {
opacity: 1.0;
}
.rightsLevelInactive {
opacity: 0.25;
}
.textSmall a {
color: #fff;
text-decoration: underline;
}
.rightsLevelTable {
width: 128px;
height: 32px;
vertical-align: middle;
display: table-cell;
}

View file

@ -1,13 +1,10 @@
#head {
position: fixed;
top: 0px;
width: 100%;
height: 64px;
background: rgb(64, 64, 64);
text-align: center;
z-index: 1;
width: 100%; height: 56px; top: 0px; background: url(/static/images/boxBlack75CenterMiddle.png); position: fixed; z-index: 1
}
#shadowTop {
width: 100%; height: 8px; top: 56px; background: url(/static/images/boxBlack75CenterBottom.png); position: fixed; z-index: 1;
}
#headList {
position: relative;
margin-left: auto;
@ -24,8 +21,5 @@
}
.headBottom {
position: absolute;
left: 0px;
bottom: 0px;
width: 128px;
width: 100%; height: 32px; top: 32px; background: url(/static/images/boxBlack75CenterMiddle.png); position: fixed; z-index: 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -1,6 +1,5 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
py:extends="'master.kid'">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
<title>Oil Archive</title>
@ -8,7 +7,7 @@
<body>
known archives
<ul>
<li py:for="archive in archives">${archive.archiveName}</li>
<li py:for="archive in archives">${archive.id} - <a href="${archive.archiveUrl}?modDate=-1">${archive.archiveName}</a></li>
</ul>
add new archive:
${add_archive(action="archives_add")}

View file

@ -1,136 +1,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<?python
selectView = [
('icon', 'View: Icon'),
('list', 'View: List'),
('quote', 'View: Quotes'),
]
selectSort = [
('title', 'Sort: Title'),
('date', 'Sort: Date'),
('size', 'Sort: Size'),
('relevance', 'Sort: Relevance'),
]
selectFind = [
('all', 'Find: All'),
('title', 'Find: Title'),
('author', 'Find: Author'),
('date', 'Find: Date'),
('genre', 'Find: Genre'),
]
selectList = [
('all', 'List: All'),
('Screenings', 'List: Screenings'),
]
def search_link(search, n = None, o = None):
link = "/search?"
if n:
o = search['o'] - (search['o'] % n) + 1
for key in search:
value = search[key]
if key == 'o' and o:
value = o -1
if key == 'n' and n:
value = n
if key not in ['length']:
link += "%s=%s&" %(key, value)
return link
?>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
<title>Oil of the 21st Century Archive</title>
</head>
<body>
<div id="head">
<div id="headList">
<div class="headTop">
</div>
<div class="headTop" style="left: 136px">
<select id="selectList" onChange="changeList()">
<option py:for="value, content in selectList"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['l'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 272px">
<select id="selectView" onChange="changeView()">
<option py:for="value, content in selectView"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['v'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 408px">
<select id="selectSort" onChange="changeSort()">
<option py:for="value, content in selectSort"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['s'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 544px">
<select id="selectFind" onChange="changeFind()">
<option py:for="value, content in selectFind"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['f'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 680px">
<input id="inputFind" type="search" placeholder="Find" autosave="find" results="10" onBlur="submitFind()" value="${search['q']}"/>
</div>
<div py:if="search['length'] > 30" id="numberDiv" class="headBottom textSmall">
Items per Page<br/> <span py:for="n in [30, 60, 90, 120]">
<a py:if="n != search['n']" href="${search_link(search, n=n)}">${n}</a>
<span py:if="n == search['n']" py:replace="n" />
</span>
</div>
<?python
number_pages = search['length'] / search['n']
if search['length'] % search['n']:
number_pages += 1
current_page = search['o'] / search['n'] + 1
current_page_start = search['o'] + 1
current_page_end = min(search['o']+search['n'], search['length'])
previous_page = current_page - 1
previous_page_start = None
if current_page > 1:
previous_page_end = current_page_start - 1
previous_page_start = current_page_start - search['n']
next_page = current_page + 1
next_page_start = None
if current_page < number_pages:
next_page_start = current_page_end + 1
next_page_end = min(next_page_start + search['n'] -1, search['length'])
last_page = number_pages
last_page_start = None
if search['length'] > search['o'] + search['n']:
last_page_start = search['n'] * (number_pages -1) + 1
last_page_end = search['length']
?>
<div py:if="search['length'] > search['n'] and current_page > 1" id="firstDiv" class="headBottom textSmall" style="left: 136px">
<a href="${search_link(search, o=1)}">First Page<br/>1 (1-${search['n']})</a>
</div>
<div py:if="previous_page_start" id="previousDiv" class="headBottom textSmall" style="left: 272px">
<a href="${search_link(search, o= previous_page_start)}">Previous Page<br/>${previous_page} (${previous_page_start}-${previous_page_end})</a>
</div>
<div id="currentDiv" class="headBottom textSmall" style="left: 408px">
Current Page<br/>${current_page} (${current_page_start}-${current_page_end})
</div>
<div py:if="next_page_start" id="nextDiv" class="headBottom textSmall" style="left: 544px">
<a href="${search_link(search, o= next_page_start)}">Next Page<br/>${next_page} (${next_page_start}-${next_page_end})</a>
</div>
<div py:if="last_page_start" id="lastDiv" class="headBottom textSmall" style="left: 680px">
<a href="${search_link(search, o= last_page_start)}">Last Page<br/>${last_page} (${last_page_start}-${last_page_end})</a>
</div>
</div>
</div>
<div id="shadowTop"></div>
<div id="listBody">
<div py:for="item in items" id="${item.hashId}" class="inline listItem">
<div class="table">
@ -138,7 +12,7 @@ if search['length'] > search['o'] + search['n']:
<img src="/view/${item.hashId}/icon.png" class="listItemLink link" onMouseOver="mouseOver('${item.hashId}','IconLarge')" onMouseOut="mouseOut('${item.hashId}','IconLarge')" />
</a>
</div>
<div class="item">
<div class="oil21_item">
<img src="/static/images/transparent.png" class="link" style="width: 128px; height: 8px" onMouseOver="mouseOver('${item.hashId}','IconLarge')" onMouseOut="mouseOut('${item.hashId}','IconLarge')" />
<div class="link textIconLarge" onMouseOver="mouseOver('${item.hashId}','IconLarge')" onMouseOut="mouseOut('${item.hashId}','IconLarge')">
${item.title}

View file

@ -1,5 +1,48 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?python import sitetemplate ?>
<?python
selectView = [
('icon', 'View: Icon'),
('list', 'View: List'),
('quote', 'View: Quotes'),
]
selectSort = [
('title', 'Sort: Title'),
('date', 'Sort: Date'),
('size', 'Sort: Size'),
('relevance', 'Sort: Relevance'),
]
selectFind = [
('all', 'Find: All'),
('title', 'Find: Title'),
('author', 'Find: Author'),
('date', 'Find: Date'),
('genre', 'Find: Genre'),
]
selectList = [
('all', 'List: All'),
('Screenings', 'List: Screenings'),
]
def search_link(search, n = None, o = None):
link = "/search?"
if n:
o = search['o'] - (search['o'] % n) + 1
for key in search:
value = search[key]
if key == 'o' and o:
value = o -1
if key == 'n' and n:
value = n
if key not in ['length']:
link += "%s=%s&" %(key, value)
return link
?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="sitetemplate">
<head py:match="item.tag=='{http://www.w3.org/1999/xhtml}head'" py:attrs="item.items()">
@ -32,7 +75,88 @@
<a href="/logout">Logout</a>
</span>
</div>
<div id="header">&nbsp;</div>
<div id="head">
<div id="headList">
<div class="headTop">
</div>
<div class="headTop" style="left: 136px">
<select id="selectList" onChange="changeList()">
<option py:for="value, content in selectList"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['l'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 272px">
<select id="selectView" onChange="changeView()">
<option py:for="value, content in selectView"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['v'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 408px">
<select id="selectSort" onChange="changeSort()">
<option py:for="value, content in selectSort"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['s'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 544px">
<select id="selectFind" onChange="changeFind()">
<option py:for="value, content in selectFind"
py:content="content"
py:attrs="dict(value=value, selected=(value==search['f'] and 'selected' or None))" />
</select>
</div>
<div class="headTop" style="left: 680px">
<input id="inputFind" type="search" placeholder="Find" autosave="find" results="10" onBlur="submitFind()" value="${search['q']}"/>
</div>
<div py:if="search['length'] > 30" id="numberDiv" class="headBottom textSmall">
Items per Page<br/> <span py:for="n in [30, 60, 90, 120]">
<a py:if="n != search['n']" href="${search_link(search, n=n)}">${n}</a>
<span py:if="n == search['n']" py:replace="n" />
</span>
</div>
<?python
number_pages = search['length'] / search['n']
if search['length'] % search['n']:
number_pages += 1
current_page = search['o'] / search['n'] + 1
current_page_start = search['o'] + 1
current_page_end = min(search['o']+search['n'], search['length'])
previous_page = current_page - 1
previous_page_start = None
if current_page > 1:
previous_page_end = current_page_start - 1
previous_page_start = current_page_start - search['n']
next_page = current_page + 1
next_page_start = None
if current_page < number_pages:
next_page_start = current_page_end + 1
next_page_end = min(next_page_start + search['n'] -1, search['length'])
last_page = number_pages
last_page_start = None
if search['length'] > search['o'] + search['n']:
last_page_start = search['n'] * (number_pages -1) + 1
last_page_end = search['length']
?>
<div py:if="search['length'] > search['n'] and current_page > 1" id="firstDiv" class="headBottom textSmall" style="left: 136px">
<a href="${search_link(search, o=1)}">First Page<br/>1 (1-${search['n']})</a>
</div>
<div py:if="previous_page_start" id="previousDiv" class="headBottom textSmall" style="left: 272px">
<a href="${search_link(search, o= previous_page_start)}">Previous Page<br/>${previous_page} (${previous_page_start}-${previous_page_end})</a>
</div>
<div id="currentDiv" class="headBottom textSmall" style="left: 408px">
Current Page<br/>${current_page} (${current_page_start}-${current_page_end})
</div>
<div py:if="next_page_start" id="nextDiv" class="headBottom textSmall" style="left: 544px">
<a href="${search_link(search, o= next_page_start)}">Next Page<br/>${next_page} (${next_page_start}-${next_page_end})</a>
</div>
<div py:if="last_page_start" id="lastDiv" class="headBottom textSmall" style="left: 680px">
<a href="${search_link(search, o= last_page_start)}">Last Page<br/>${last_page} (${last_page_start}-${last_page_end})</a>
</div>
</div>
</div>
<div id="shadowTop"></div>
<div id="main_content">
<div py:if="tg_flash" class="flash" py:content="tg_flash"></div>

View file

@ -4,37 +4,222 @@
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" py:replace="''"/>
<title>Oil21 - ${item.title}</title>
<!--
<style type="text/css" media="screen" py:if="item.archive.css">
@import "/css/${item.archive.hashId}.css";
<style type="text/css" media="screen">
@import "/static/css/archiveItem.css";
</style>
<style type="text/css" media="screen" py:if="item.archive.css">
@import "/css/${item.archive.hashId}.css";
</style>
<script py:if="item.archive.js" src="/js/${item.archive.hashId}.js" />
<!--
-->
</head>
<body>
<div class="itemPageIcon">
<img class="itemIcon" src="/view/${item.hashId}/icon.png" />
</div>
<div class="itemPageText">
<table>
<tr>
<td id="itemPageTextLeftTop"></td>
<td id="itemPageTextCenterTop"></td>
<td id="itemPageTextRightTop"></td>
</tr>
<tr>
<td id="itemPageTextLeftMiddle"></td>
<td id="itemPageTextCenterMiddle">
x${XML(item.html)}
</td>
<td id="itemPageTextRightMiddle"></td>
</tr>
<tr>
<td id="itemPageTextLeftBottom"></td>
<td id="itemPageTextCenterBottom"></td>
<td id="itemPageTextRightBottom"></td>
</tr>
</table>
</div>
<div style="width: 880px; height: 232px; margin-left: auto; margin-right: auto; margin-top: 48px">
<div class="boxData">
<div>
<img src="/view/${item.hashId}/icon.png" />
</div>
<div class="iconText textBold textMedium textCenter" style="width: 144px; height: 64px; background: url(/view/${item.hashId}/icon_reflection.png);background-position: center top; background-repeat: no-repeat;">
${item.author} - ${item.title}
</div>
</div>
<div class="boxData">
<div style="height: 64px">
<div style="width: 64px; height: 64px; display: table-cell; text-align: center; vertical-align: middle">
<img src="${item.archive.iconUrl}" style="width: 48px; height: 48px" />
</div>
<div style="height: 64px; padding-left: 4px; display: table-cell; text-align: left; vertical-align: middle">
<div class="textLarge textBold">
${item.archive.archiveName}
</div>
</div>
</div>
<div style="height: 64px">
<div style="width: 64px; height: 64px; display: table-cell; text-align: center; vertical-align: middle">
<img src="/static/images/iconType${item.kind}.png" style="width: 48px; height: 48px" />
</div>
<div style="height: 64px; padding-left: 4px; display: table-cell; text-align: left; vertical-align: middle">
<div class="textLarge textBold">
${item.kind}
</div>
<div class="textSmall">
${item.genre}
</div>
</div>
</div>
<div style="height: 64px">
<div style="width: 64px; height: 64px; display: table-cell; text-align: center; vertical-align: middle">
<img src="/static/images/iconFile${item.kind}.png" style="width: 48px; height: 48px" />
</div>
<div style="height: 64px; padding-left: 4px; display: table-cell; text-align: left; vertical-align: middle">
<div class="textBold textLarge">
${item.fileType}
</div>
<div class="textSmall">
${item.sizeFormated}
</div>
</div>
</div>
</div>
<div class="boxData">
<div style="height: 64px">
<div style="width: 144px; height: 64px; text-align: center; display: table-cell; vertical-align: middle">
<div class="textLarge textBold">
Released
</div>
<div class="textSmall" style="vertical-align: bottom">
${item.relDateFormated}
</div>
</div>
</div>
<div style="height: 64px">
<div style="width: 144px; height: 64px; text-align: center; display: table-cell; vertical-align: middle">
<div class="textLarge textBold">
Archived
</div>
<div class="textSmall" style="vertical-align: bottom">
${item.pubDate}
</div>
</div>
</div>
<div style="height: 64px">
<div style="width: 144px; height: 64px; text-align: center; display: table-cell; vertical-align: middle">
<div class="textLarge textBold">
Modified
</div>
<div class="textSmall" style="vertical-align: bottom">
${item.modDate}
</div>
</div>
</div>
</div>
<div class="boxData">
<div style="height: 64px">
<div style="width: 64px; height: 64px; display: table-cell; text-align: center; vertical-align: middle">
<img src="/static/images/iconLinkDownload.png" style="width: 48px; height: 48px" />
</div>
<div style="height: 64px; display: table-cell; text-align: left; vertical-align: middle">
<div class="textLarge textBold">
Download
</div>
<div class="textSmall" py:if="item.downloadUrl">
<a href="${item.downloadUrl}">${item.domain(item.downloadUrl)}</a>
</div>
</div>
</div>
<div style="height: 64px">
<div style="width: 64px; height: 64px; display: table-cell; text-align: center; vertical-align: middle">
<img src="/static/images/iconLinkArchive.png" style="width: 48px; height: 48px" />
</div>
<div style="height: 64px; display: table-cell; text-align: left; vertical-align: middle">
<div class="textLarge textBold">
Archive
</div>
<div class="textSmall" py:if="item.archiveUrl">
<a href="${item.archiveUrl}">${item.domain(item.archiveUrl)}</a>
</div>
</div>
</div>
<div style="height: 64px">
<div style="width: 64px; height: 64px; display: table-cell; text-align: center; vertical-align: middle">
<img src="/static/images/iconLinkStore.png" style="width: 48px; height: 48px" />
</div>
<div style="height: 64px; display: table-cell; text-align: left; vertical-align: middle">
<div class="textLarge textBold">
Store
</div>
<div class="textSmall" py:if="item.storeUrl">
<a href="${item.storeUrl}">${item.domain(item.storeUrl)}</a>
</div>
</div>
</div>
</div>
<div class="boxData">
<div py:attrs="{'class':'rightsLevel ' + item.rightsLevelClass(5)}" style="background: rgb(255, 0, 0);">
<div class="rightsLevelTable">
<div class="textBold textLarge textSpacing">
SEVERE
</div>
<div class="textXXSmall">
Severe Risk of Legal Action
</div>
</div>
</div>
<div py:attrs="{'class':'rightsLevel ' + item.rightsLevelClass(4)}" style="background: rgb(255, 192, 0);">
<div class="rightsLevelTable">
<div class="textBold textLarge textSpacing">
HIGH
</div>
<div class="textXXSmall">
High Risk of Legal Action
</div>
</div>
</div>
<div py:attrs="{'class':'rightsLevel ' + item.rightsLevelClass(3)}" style="background: rgb(255, 255, 0);">
<div class="rightsLevelTable">
<div class="textBold textLarge textSpacing">
ELEVATED
</div>
<div class="textXXSmall">
Elevated Risk of Legal Action
</div>
</div>
</div>
<div py:attrs="{'class':'rightsLevel ' + item.rightsLevelClass(2)}" style="background: rgb(128, 255, 0);">
<div class="rightsLevelTable">
<div class="textBold textLarge textSpacing">
GUARDED
</div>
<div class="textXXSmall">
General Risk of Legal Action
</div>
</div>
</div>
<div py:attrs="{'class':'rightsLevel ' + item.rightsLevelClass(1)}" style="background: rgb(0, 255, 0);">
<div class="rightsLevelTable">
<div class="textBold textLarge textSpacing">
LOW
</div>
<div class="textXXSmall">
Low Risk of Legal Action
</div>
</div>
</div>
<div py:attrs="{'class':'rightsLevel ' + item.rightsLevelClass(0)}" style="background: rgb(0, 255, 128);">
<div class="rightsLevelTable">
<div class="textBold textLarge textSpacing">
FREE
</div>
<div class="textXXSmall">
No Risk of Legal Action
</div>
</div>
</div>
</div>
</div>
<div class="itemPageText" style="width: 864px; margin-left: auto; margin-right: auto">
<table>
<tr>
<td class="boxWhiteLeftTop"></td>
<td class="boxWhiteCenterTop"></td>
<td class="boxWhiteRightTop"></td>
</tr>
<tr>
<td class="boxWhiteLeftMiddle"></td>
<td class="boxWhiteCenterMiddle" style="width: 848px">
${XML(item.html)}
</td>
<td class="boxWhiteRightMiddle"></td>
</tr>
<tr>
<td class="boxWhiteLeftBottom"></td>
<td class="boxWhiteCenterBottom"></td>
<td class="boxWhiteRightBottom"></td>
</tr>
</table>
</div>
</body>
</html>

View file

@ -2,6 +2,15 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=2:sts=2:ts=2
import re
'''
Returns the given HTML with all unencoded ampersands encoded correctly
'''
unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
def fix_ampersands(value):
return unencoded_ampersands_re.sub('&amp;', value)
'''
highlight search term in text, scipping html tags and script elements