another day
|
@ -20,11 +20,15 @@ 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 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)
|
||||
|
|
|
@ -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()
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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,25 +87,48 @@ 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):
|
||||
self._SO_set_author(value)
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
149
oilarchive/static/css/archiveItem.css
Normal 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;
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
BIN
oilarchive/static/images/backgroundBruegelChildren.png
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
oilarchive/static/images/boxBlack75CenterBottom.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxBlack75CenterMiddle.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxBlackCenterBottom.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxBlackCenterMiddle.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxData.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
oilarchive/static/images/boxWhite75CenterBottom.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhite75CenterMiddle.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteCenterBottom.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteCenterMiddle.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteCenterTop.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteLeftBottom.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteLeftMiddle.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteLeftTop.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteRightBottom.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteRightMiddle.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/boxWhiteRightTop.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/iconCollection.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
oilarchive/static/images/iconFileAll.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
oilarchive/static/images/iconFileMovies.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
oilarchive/static/images/iconFileMusic.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
oilarchive/static/images/iconFileNews.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
oilarchive/static/images/iconFilePictures.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
oilarchive/static/images/iconFileSoftware.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
oilarchive/static/images/iconFileTexts.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
oilarchive/static/images/iconFolderAll.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
oilarchive/static/images/iconFolderMovies.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
oilarchive/static/images/iconFolderMusic.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
oilarchive/static/images/iconFolderNews.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
oilarchive/static/images/iconFolderPictures.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
oilarchive/static/images/iconFolderSmart.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
oilarchive/static/images/iconFolderSoftware.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
oilarchive/static/images/iconFolderTexts.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
oilarchive/static/images/iconLinkArchive.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
oilarchive/static/images/iconLinkDownload.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
oilarchive/static/images/iconLinkStore.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
oilarchive/static/images/iconTypeMovies.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
oilarchive/static/images/iconTypeMusic.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
oilarchive/static/images/iconTypeNews.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
oilarchive/static/images/iconTypePictures.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
oilarchive/static/images/iconTypeSoftware.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
oilarchive/static/images/iconTypeTexts.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
oilarchive/static/images/iconTypeTexts2.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
oilarchive/static/images/itemListLargeCenterActive.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/itemListLargeCenterMouseOver.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/itemListLargeCenterSelected.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/itemListLargeLeftActive.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/itemListLargeLeftMouseOver.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/itemListLargeLeftSelected.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/itemListLargeRightActive.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/itemListLargeRightMouseOver.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
oilarchive/static/images/itemListLargeRightSelected.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/itemListSmallCenterMouseOver.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
oilarchive/static/images/itemListSmallLeftMouseOver.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/itemListSmallRightMouseOver.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/itemPageBottomMouseOver.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/itemPageMiddleMouseOver.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
oilarchive/static/images/itemPageTopMouseOver.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
oilarchive/static/images/transparent25Black.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
oilarchive/static/images/transparent25White.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
oilarchive/static/images/transparent50Black.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
oilarchive/static/images/transparent50White.png
Normal file
After Width: | Height: | Size: 3 KiB |
|
@ -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")}
|
||||
|
|
|
@ -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>
|
||||
|
||||
<body>
|
||||
<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}
|
||||
|
|
|
@ -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"> </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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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('&', value)
|
||||
|
||||
|
||||
'''
|
||||
highlight search term in text, scipping html tags and script elements
|
||||
|
|