| 1 | #!/usr/bin/python |
|---|
| 2 | # -*- coding: UTF-8 -*- |
|---|
| 3 | |
|---|
| 4 | __author__ = "Jens Diemer" |
|---|
| 5 | __url__ = "http://www.jensdiemer.de/Programmieren/Python/PyFileCenter" |
|---|
| 6 | #SVN: http://pylucid.python-hosting.com/file/CodeSnippets/PyFileCenter/FileBrowser.py |
|---|
| 7 | __license__ = "GNU General Public License (GPL)" |
|---|
| 8 | __description__ = "a pure Python-CGI-FileBrowser (Download-Center)" |
|---|
| 9 | |
|---|
| 10 | """ |
|---|
| 11 | Beispiel "index.py": |
|---|
| 12 | ------------------------------------------------------------------------------- |
|---|
| 13 | # Verzeichnis in dem sich "FileBrowser.py" befindet in den Suchpfad aufnehmen |
|---|
| 14 | sys.path.insert( 0, os.environ["DOCUMENT_ROOT"] + "cgi-bin/PyFileCenter/" ) |
|---|
| 15 | |
|---|
| 16 | import FileBrowser |
|---|
| 17 | |
|---|
| 18 | # db-config Überschreiben mit den richtigen Daten |
|---|
| 19 | FileBrowser.cfg.dbHost = 'localhost' |
|---|
| 20 | FileBrowser.cfg.dbDatabaseName = 'DatabaseName' |
|---|
| 21 | FileBrowser.cfg.dbUserName = 'UserName' |
|---|
| 22 | FileBrowser.cfg.dbPassword = 'Password' |
|---|
| 23 | |
|---|
| 24 | # Nur Endungen anzeigen, die in der Liste vorkommen |
|---|
| 25 | FileBrowser.cfg.ext_whitelist = [ ".7z", ".zip", ".py" ] |
|---|
| 26 | |
|---|
| 27 | # Hauptverz.: Navigation nur innerhalb diese Verz. mit Unterverz. erlauben |
|---|
| 28 | FileBrowser.cfg.base_path = "Programmieren" |
|---|
| 29 | |
|---|
| 30 | # Verz. in der Liste auslassen: |
|---|
| 31 | FileBrowser.cfg.dir_filter = ["OLD"] |
|---|
| 32 | |
|---|
| 33 | # Dateien die nicht angezeigt werden sollen |
|---|
| 34 | FileBrowser.cfg.file_filter = ["index.py"] |
|---|
| 35 | |
|---|
| 36 | # =False -> Nur Dateien im aktuellen Verz. anzeigen |
|---|
| 37 | FileBrowser.cfg.allow_subdirs = True |
|---|
| 38 | |
|---|
| 39 | # =True -> User kann in ZIP Archiven reinsehen |
|---|
| 40 | FileBrowser.cfg.allow_zipview = True |
|---|
| 41 | |
|---|
| 42 | # zurück-Link der auf jeder Seite eingeblendes werden soll |
|---|
| 43 | FileBrowser.cfg.backlink = { |
|---|
| 44 | "txt" : "< zurück zur Homepage", |
|---|
| 45 | "url" : "http://www.jensdiemer.de/" |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | # Verzeichnis bezogene zurück-Links |
|---|
| 49 | FileBrowser.cfg.dir_backlinks = { |
|---|
| 50 | "PyAdmin" : ["< PyAdmin Projektseite", "http://www.jensdiemer.de?PyAdmin"], |
|---|
| 51 | "PyDiskEraser" : ["< PyDiskEraser Projektseite", "http://www.jensdiemer.de?PyDiskEraser"], |
|---|
| 52 | "PyLucid" : ["< PyLucid Projektseite", "http://www.jensdiemer.de?PyLucid"] |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | # HTML-Head |
|---|
| 56 | FileBrowser.cfg.html_head["robots"] = "index,follow" |
|---|
| 57 | |
|---|
| 58 | |
|---|
| 59 | # Starten... |
|---|
| 60 | FileBrowser.FileBrowser() |
|---|
| 61 | ------------------------------------------------------------------------------- |
|---|
| 62 | |
|---|
| 63 | Es gibt noch einige andere Parameter in der cfg-Klasse. Einfach mal unten in |
|---|
| 64 | den Sourcen schauen! |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | |
|---|
| 68 | ____________________________________________________________________________________ |
|---|
| 69 | |
|---|
| 70 | Benötigte SQL-Tabellen |
|---|
| 71 | |
|---|
| 72 | CREATE TABLE `FileCenter_Stat_Path` ( |
|---|
| 73 | `id` INT NOT NULL AUTO_INCREMENT , |
|---|
| 74 | `name` VARCHAR( 255 ) NOT NULL , |
|---|
| 75 | PRIMARY KEY ( `id` ) |
|---|
| 76 | ); |
|---|
| 77 | |
|---|
| 78 | CREATE TABLE `FileCenter_Stat_Item` ( |
|---|
| 79 | `id` INT NOT NULL AUTO_INCREMENT , |
|---|
| 80 | `path_id` INT NOT NULL , |
|---|
| 81 | `name` VARCHAR( 255 ) NOT NULL , |
|---|
| 82 | `count` int(11) NOT NULL default '0', |
|---|
| 83 | PRIMARY KEY ( `id` ) |
|---|
| 84 | ); |
|---|
| 85 | |
|---|
| 86 | |
|---|
| 87 | """ |
|---|
| 88 | |
|---|
| 89 | __version__ = "v0.6.1" |
|---|
| 90 | |
|---|
| 91 | __history__ = """ |
|---|
| 92 | v0.6.1 |
|---|
| 93 | - Neu: cfg.pre_title und cfg.post_title |
|---|
| 94 | - Bugfix: cfg.base_path kann nun auch das aktuelle Verzeichnis sein, also ="" |
|---|
| 95 | - alle os.path in posixpath getausch |
|---|
| 96 | - SQL Fehler beim connect wird abgefangen |
|---|
| 97 | - Bugfix: dbconfig |
|---|
| 98 | v0.6 |
|---|
| 99 | - dbconfig als Klasse zum überscheiben geändert |
|---|
| 100 | v0.5.2 |
|---|
| 101 | - Bugfix: print_back_link() nutzt nun urllib.quote_plus() damit auch Sonderzeichen in |
|---|
| 102 | den Verzeichnisnamen erlaubt sind |
|---|
| 103 | v0.5.1 |
|---|
| 104 | - NEU: cfg.use_sql_statistic, damit es auch ohne SQL eingesetzt werden kann! |
|---|
| 105 | - NEU: cfg.debug |
|---|
| 106 | v0.5.0 |
|---|
| 107 | - NEU: MySQL Statistik |
|---|
| 108 | v0.4.2 |
|---|
| 109 | - Bilder-Gallerie: Ein Bild wird nun auch mit Thumbnail dargestellt, wenn es nicht |
|---|
| 110 | in cfg.thumb_pic_filter vorkommt, aber ein passendes Thumbnail gefunden wurde. |
|---|
| 111 | v0.4.1 |
|---|
| 112 | - Bilder-Gallerie: Beim anschauen eines Bilder kann man drauf klicken und es |
|---|
| 113 | es wird das nächste angezeigt |
|---|
| 114 | v0.4.0 |
|---|
| 115 | - NEU: Thumb-Gallerie-Einstellung |
|---|
| 116 | - NEU: CSS läßt sich nun auch ändern |
|---|
| 117 | - Bug: änderungen am HTML-Kopf funktionierten garnicht |
|---|
| 118 | v0.3.3 |
|---|
| 119 | - Alle URLs sollten nun durch urllib.quote() angezeigt werden |
|---|
| 120 | v0.3.2 |
|---|
| 121 | - Umstellung von subprocess auf os.popen, weil's subprocess erst ab 2.4 gibt |
|---|
| 122 | - ZIPfile-Viewer zeigt nun mehr an |
|---|
| 123 | v0.3.1 |
|---|
| 124 | - Umstrukturierung des Codes |
|---|
| 125 | - NEU: Download-Proxy für Scriptdateien, damit sie nicht auf dem Server ausgeführt |
|---|
| 126 | werden, sondern runterladen ;) |
|---|
| 127 | - NEU: Renderzeit anzeige... wow =:-0 |
|---|
| 128 | v0.3.0 |
|---|
| 129 | - Komplette Änderung der Konfiguration |
|---|
| 130 | v0.2.2 |
|---|
| 131 | - NEU: BackLink |
|---|
| 132 | v0.2.1 |
|---|
| 133 | - Downloadlinks nun immer mit MIME-type="application/octet-stream" - danke Leonidas |
|---|
| 134 | - Fehler: Downloadlinks korrigiert |
|---|
| 135 | v0.2 |
|---|
| 136 | - NEU: zipfile "Viewer" |
|---|
| 137 | v0.1.1 |
|---|
| 138 | - Darstellung der KBytes für Dateien mit "de_DE.UTF-8" |
|---|
| 139 | v0.1 |
|---|
| 140 | - erste Version |
|---|
| 141 | """ |
|---|
| 142 | |
|---|
| 143 | __info__ = '<a href="%s">FileBrowser</a> %s' % (__url__, __version__) |
|---|
| 144 | |
|---|
| 145 | import cgitb; cgitb.enable() |
|---|
| 146 | |
|---|
| 147 | import time |
|---|
| 148 | start_time = time.time() |
|---|
| 149 | |
|---|
| 150 | import os, sys, cgi, stat, re, urllib |
|---|
| 151 | import locale |
|---|
| 152 | import posixpath |
|---|
| 153 | import zipfile |
|---|
| 154 | |
|---|
| 155 | |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | |
|---|
| 159 | |
|---|
| 160 | CSS_style = """<style type="text/css"> |
|---|
| 161 | @media all { |
|---|
| 162 | /* |
|---|
| 163 | Mac IE Filter |
|---|
| 164 | http://w3development.de/css/hide_css_from_browsers/media/ |
|---|
| 165 | */ |
|---|
| 166 | body { |
|---|
| 167 | font-size: 0.9em; |
|---|
| 168 | } |
|---|
| 169 | html, body { |
|---|
| 170 | margin: 10px; |
|---|
| 171 | padding: 0; |
|---|
| 172 | font-family: tahoma, arial, sans-serif; |
|---|
| 173 | color: #000000; |
|---|
| 174 | background-color: #FFFFFF; |
|---|
| 175 | } |
|---|
| 176 | body { |
|---|
| 177 | min-height: 500px; |
|---|
| 178 | } |
|---|
| 179 | a { /* Link allgemein */ |
|---|
| 180 | text-decoration:none; |
|---|
| 181 | } |
|---|
| 182 | a:link { /* noch nicht besuchter Link */ |
|---|
| 183 | color: #000088; |
|---|
| 184 | } |
|---|
| 185 | a:visited { /* schon besuchter Link */ |
|---|
| 186 | color: #000000; |
|---|
| 187 | } |
|---|
| 188 | a:hover { /* Maus über dem Link */ |
|---|
| 189 | color: #4444FF; |
|---|
| 190 | text-decoration:underline; |
|---|
| 191 | } |
|---|
| 192 | a:active { /* Link wird angeklickt */ |
|---|
| 193 | color: #FF0000; |
|---|
| 194 | } |
|---|
| 195 | h1 { /* Seitenüberschrift */ |
|---|
| 196 | border-bottom: 1px solid #000000; |
|---|
| 197 | } |
|---|
| 198 | /* |
|---|
| 199 | _________________________________________ |
|---|
| 200 | Verzeichnis-Liste |
|---|
| 201 | */ |
|---|
| 202 | .gallery_dirs ul { |
|---|
| 203 | } |
|---|
| 204 | .gallery_dirs li { |
|---|
| 205 | list-style-type: none; |
|---|
| 206 | } |
|---|
| 207 | .gallery_dirs li:hover { |
|---|
| 208 | list-style-type:disc; |
|---|
| 209 | } |
|---|
| 210 | /* |
|---|
| 211 | _________________________________________ |
|---|
| 212 | Datei-Liste |
|---|
| 213 | */ |
|---|
| 214 | /* |
|---|
| 215 | Dateiliste allgemein |
|---|
| 216 | */ |
|---|
| 217 | .gallery_files ul { |
|---|
| 218 | text-align: center; |
|---|
| 219 | margin: 0px; |
|---|
| 220 | padding: 0px; |
|---|
| 221 | } |
|---|
| 222 | .gallery_files li { |
|---|
| 223 | float: left; |
|---|
| 224 | border: 1px solid #DDDDDD; |
|---|
| 225 | height: 150px; |
|---|
| 226 | width: auto; |
|---|
| 227 | text-align: center; |
|---|
| 228 | margin: 5px; |
|---|
| 229 | padding: 5px; |
|---|
| 230 | list-style-type: none; |
|---|
| 231 | background-color: #EEEEEE; |
|---|
| 232 | } |
|---|
| 233 | /* |
|---|
| 234 | nur normale Dateien |
|---|
| 235 | */ |
|---|
| 236 | .normal a { |
|---|
| 237 | padding: 0 5px 0px 5px; |
|---|
| 238 | } |
|---|
| 239 | /* |
|---|
| 240 | nur Bilder mit Thumnailansicht |
|---|
| 241 | */ |
|---|
| 242 | .gallery_pic a { |
|---|
| 243 | text-decoration:none; |
|---|
| 244 | } |
|---|
| 245 | .gallery_pic img { |
|---|
| 246 | border: 0px; |
|---|
| 247 | } |
|---|
| 248 | #dir_counter, #file_counter, #zip_counter { |
|---|
| 249 | font-size: 0.6em; |
|---|
| 250 | clear: both; |
|---|
| 251 | text-align: center; |
|---|
| 252 | color: #CCCCCC; |
|---|
| 253 | } |
|---|
| 254 | #zip_counter { |
|---|
| 255 | text-align: left; |
|---|
| 256 | } |
|---|
| 257 | /* |
|---|
| 258 | _________________________________________ |
|---|
| 259 | Footer |
|---|
| 260 | */ |
|---|
| 261 | #gallery_clear { |
|---|
| 262 | clear: both; |
|---|
| 263 | width: auto; |
|---|
| 264 | border: none; |
|---|
| 265 | } |
|---|
| 266 | #gallery_footer { |
|---|
| 267 | border-top: 2px solid #CCCCCC; |
|---|
| 268 | font-size: 0.6em; |
|---|
| 269 | text-align: center; |
|---|
| 270 | color: #CCCCCC; |
|---|
| 271 | } |
|---|
| 272 | #gallery_footer a { |
|---|
| 273 | padding: 0; |
|---|
| 274 | color: #CCCCCC; |
|---|
| 275 | text-decoration:none; |
|---|
| 276 | } |
|---|
| 277 | /* |
|---|
| 278 | _________________________________________ |
|---|
| 279 | ZIP |
|---|
| 280 | */ |
|---|
| 281 | .zipinfo td { |
|---|
| 282 | padding-right: 0.7em; |
|---|
| 283 | padding-left: 0.7em; |
|---|
| 284 | } |
|---|
| 285 | /* |
|---|
| 286 | _________________________________________ |
|---|
| 287 | Image View - Ein Bild wird Angezeit |
|---|
| 288 | */ |
|---|
| 289 | #img_view { |
|---|
| 290 | text-align:center; |
|---|
| 291 | width:auto; |
|---|
| 292 | margin:auto; |
|---|
| 293 | display:block; |
|---|
| 294 | text-decoration:none; |
|---|
| 295 | } |
|---|
| 296 | #img_view img { |
|---|
| 297 | border: 1px solid #000000; |
|---|
| 298 | } |
|---|
| 299 | #img_view a, #img_view a:hover { |
|---|
| 300 | text-decoration:none; |
|---|
| 301 | } |
|---|
| 302 | } |
|---|
| 303 | </style> |
|---|
| 304 | """ |
|---|
| 305 | |
|---|
| 306 | |
|---|
| 307 | |
|---|
| 308 | |
|---|
| 309 | |
|---|
| 310 | |
|---|
| 311 | |
|---|
| 312 | class cfg: |
|---|
| 313 | """ |
|---|
| 314 | Pseudo Klasse zum 'speichern' der Konfiguration. |
|---|
| 315 | |
|---|
| 316 | Hierhin wird die Basis Konfiguration gespeichert, die |
|---|
| 317 | von außerhalb verändert werden sollte. |
|---|
| 318 | """ |
|---|
| 319 | |
|---|
| 320 | # Nur Endungen anzeigen, die in der Liste vorkommen |
|---|
| 321 | ext_whitelist = [ ".jpg",".mpg",".avi" ] |
|---|
| 322 | |
|---|
| 323 | # Downloadbare Textdateien über eingebaute Downloadproxy |
|---|
| 324 | ext_download_proxy = [ ".py",".php",".php4",".php5" ] |
|---|
| 325 | |
|---|
| 326 | # Dateiendungen, bei denen nicht der file-Befehl ausgeführt werden soll, |
|---|
| 327 | # sondern der eingebaute "Analysator", der aber bisher nur *.py Dateien kann ;) |
|---|
| 328 | override_fileinfo = { |
|---|
| 329 | ".py" : "Python Script", |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | # Hauptverz.: Navigation nur innerhalb diese Verz. mit Unterverz. erlauben |
|---|
| 333 | base_path = "MeinDownloadVerzeichnis" |
|---|
| 334 | |
|---|
| 335 | # Verz. in der Liste auslassen: |
|---|
| 336 | dir_filter = ["PyFileCenter"] |
|---|
| 337 | |
|---|
| 338 | # Dateien die nicht angezeigt werden sollen |
|---|
| 339 | file_filter = [ "index.py", ".htaccess" ] |
|---|
| 340 | |
|---|
| 341 | # =False -> Nur Dateien im aktuellen Verz. anzeigen |
|---|
| 342 | allow_subdirs = True |
|---|
| 343 | |
|---|
| 344 | ## Thumb-Gallerie-Einstellung |
|---|
| 345 | # pic_ext = Dateiendungen, die als Bilder behandelt werden sollen |
|---|
| 346 | # thumb_pic_filter = Filter, der aus den Dateinamen rausgeschnitten werden soll, um |
|---|
| 347 | # damit das passende Thumbnail zu finden |
|---|
| 348 | # thumb_suffix = Liste der Suffixe im Dateiname mit dem ein Thumbnail markiert ist |
|---|
| 349 | # resize_thumb_size = Wird kein Thumbnail gefunden, wird das original Bild auf diese Werte |
|---|
| 350 | # verkleinert als Thumb genommen |
|---|
| 351 | # |
|---|
| 352 | # Bsp.: |
|---|
| 353 | # Urlaub01_WEB.jpg -> Bild zu dem ein Thumbnail gesucht wird |
|---|
| 354 | # Urlaub01_thumb.jpg -> Das passende Thumbnail |
|---|
| 355 | pic_ext = ( ".jpg", ".jpeg" ) |
|---|
| 356 | thumb_pic_filter = ( "_WEB", ) |
|---|
| 357 | thumb_suffix = ( "_thumb", ) |
|---|
| 358 | resize_thumb_size = ( 100,60 ) |
|---|
| 359 | |
|---|
| 360 | # zurück-Link der auf jeder Seite eingeblendes werden soll |
|---|
| 361 | backlink = { |
|---|
| 362 | "txt" : "< zurück zur Homepage", # wird HTML-escaped! |
|---|
| 363 | "url" : "http://www.jensdiemer.de/" |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | # Verzeichnis bezogene zurück-Links |
|---|
| 367 | dir_backlinks_code = '<p><a href="%(url)s">%(txt)s</a></p>' |
|---|
| 368 | dir_backlinks = {} |
|---|
| 369 | |
|---|
| 370 | # Link auf der Seite unten |
|---|
| 371 | footer_backlink_code = '<p><small><a href="%(url)s">%(txt)s</a></small></p>' |
|---|
| 372 | footer_backlink = { |
|---|
| 373 | "txt" : "< zurück zur Homepage", # wird HTML-escaped! |
|---|
| 374 | "url" : "http://www.jensdiemer.de/" |
|---|
| 375 | } |
|---|
| 376 | |
|---|
| 377 | # HTML Header Informationen |
|---|
| 378 | html_head = { |
|---|
| 379 | "robots" : "noindex,nofollow", |
|---|
| 380 | "keywords" : "", |
|---|
| 381 | "description" : "", |
|---|
| 382 | "CSS" : CSS_style, |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | # Textteile vor und nach dem Title und der Überschrift |
|---|
| 386 | pre_title = "", |
|---|
| 387 | post_title = "", |
|---|
| 388 | |
|---|
| 389 | ## Counter... |
|---|
| 390 | # ...in einer Dateiliste |
|---|
| 391 | dir_counter_txt = '<div id="dir_counter">(count %s)</div>' |
|---|
| 392 | # ...In Dateiliste |
|---|
| 393 | file_count_txt = '<div id="file_counter">(count %s)</div>' |
|---|
| 394 | # ...beim betrachten einer ZIP-Datei |
|---|
| 395 | zip_view_txt = '<div id="zip_counter">(count %s)</div>' |
|---|
| 396 | |
|---|
| 397 | # Wird nach den Standart CSS-Tag eingefügt |
|---|
| 398 | # Damit kann man also gezielt nur ein paar CSS-Eigenschaften überschreiben |
|---|
| 399 | # Muß komplett mit Anfangs-/Endtags sein, also: |
|---|
| 400 | # <style type="text/css">...</style> |
|---|
| 401 | additional_CSS = "" |
|---|
| 402 | |
|---|
| 403 | # Soll in der SQL-Datenbank ein counter Statistik geführt werden? |
|---|
| 404 | use_sql_statistic = True |
|---|
| 405 | |
|---|
| 406 | # SQL db-Config |
|---|
| 407 | dbHost = 'localhost' # Evtl. muß hier die Domain rein |
|---|
| 408 | dbDatabaseName = 'DatabaseName' |
|---|
| 409 | dbUserName = 'UserName' |
|---|
| 410 | dbPassword = 'Password' |
|---|
| 411 | |
|---|
| 412 | # Debug-Ausgaben |
|---|
| 413 | debug = False |
|---|
| 414 | |
|---|
| 415 | |
|---|
| 416 | |
|---|
| 417 | HTML_head = """<?xml version="1.0" encoding="UTF-8"?> |
|---|
| 418 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "xhtml1-strict.dtd"> |
|---|
| 419 | <html xmlns="http://www.w3.org/1999/xhtml"> |
|---|
| 420 | <head> |
|---|
| 421 | <title>%(pre_title)sFileBrowser%(post_title)s</title> |
|---|
| 422 | <meta name="robots" content="%(robots)s" /> |
|---|
| 423 | <meta name="keywords" content="%(keywords)s" /> |
|---|
| 424 | <meta name="description" content="%(description)s" /> |
|---|
| 425 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
|---|
| 426 | <meta name="MSSmartTagsPreventParsing" content="TRUE" /> |
|---|
| 427 | <meta http-equiv="imagetoolbar" content="no" /> |
|---|
| 428 | %(CSS)s |
|---|
| 429 | %(additional_CSS)s |
|---|
| 430 | </head> |
|---|
| 431 | <body>""" |
|---|
| 432 | |
|---|
| 433 | |
|---|
| 434 | |
|---|
| 435 | formatter_regex = re.compile("^ *\d*\D\d{1,3}|\d{1,3} *") |
|---|
| 436 | def formatter(number, format = "%.2f", decChar = ",", groupChar = "."): |
|---|
| 437 | """ |
|---|
| 438 | Convert to required format |
|---|
| 439 | by HarryH modify by Jens Diemer |
|---|
| 440 | |
|---|
| 441 | number = number to convert |
|---|
| 442 | format = python string formatting (%) |
|---|
| 443 | decChar = decimal char for the converted format |
|---|
| 444 | groupChar = group char for the converted format |
|---|
| 445 | |
|---|
| 446 | For example: |
|---|
| 447 | formatter(1234567890.987, "%.2f", ",", ".") |
|---|
| 448 | ==> 1.234.567.890,99 |
|---|
| 449 | formatter( 1234567890.987, "%i") |
|---|
| 450 | ==> 1.234.567.890 |
|---|
| 451 | |
|---|
| 452 | """ |
|---|
| 453 | def reverse(s): |
|---|
| 454 | # ersatz für string[::-1] welches erst ab v2.3 gibt :( |
|---|
| 455 | # Nach einer Idee von Milan |
|---|
| 456 | l = map(None, s) |
|---|
| 457 | l.reverse() |
|---|
| 458 | return ('').join(l) |
|---|
| 459 | |
|---|
| 460 | return reverse( |
|---|
| 461 | groupChar.join( |
|---|
| 462 | formatter_regex.findall( |
|---|
| 463 | reverse( (format % number).replace(".", decChar) ) |
|---|
| 464 | ) |
|---|
| 465 | ) |
|---|
| 466 | ) |
|---|
| 467 | |
|---|
| 468 | |
|---|
| 469 | #____________________________________________________________________________________ |
|---|
| 470 | |
|---|
| 471 | |
|---|
| 472 | class zipmanager: |
|---|
| 473 | def __init__( self, workdir, filename ): |
|---|
| 474 | self.filename = filename |
|---|
| 475 | |
|---|
| 476 | try: |
|---|
| 477 | self.zipobj = zipfile.ZipFile( posixpath.join(workdir, self.filename), "r" ) |
|---|
| 478 | except Exception,e: |
|---|
| 479 | print "<p>Error: %s</p>" % e |
|---|
| 480 | return |
|---|
| 481 | |
|---|
| 482 | self.view() |
|---|
| 483 | |
|---|
| 484 | def view( self ): |
|---|
| 485 | print '<table class="zipinfo">' |
|---|
| 486 | print "<tr>" |
|---|
| 487 | print "<th>filename</th>" |
|---|
| 488 | print "<th>compress_size</th>" |
|---|
| 489 | print "<th>size</th>" |
|---|
| 490 | print "<th>ratio</th>" |
|---|
| 491 | print "<th>date_time</th>" |
|---|
| 492 | print "</tr>" |
|---|
| 493 | total_compress_size = 0 |
|---|
| 494 | total_file_size = 0 |
|---|
| 495 | for fileinfo in self.zipobj.infolist(): |
|---|
| 496 | if fileinfo.filename.endswith("/"): |
|---|
| 497 | # Ist ein Verzeichniss |
|---|
| 498 | continue |
|---|
| 499 | |
|---|
| 500 | print "<tr>" |
|---|
| 501 | print "<td>%s</td>" % fileinfo.filename |
|---|
| 502 | |
|---|
| 503 | total_compress_size += fileinfo.compress_size |
|---|
| 504 | print '<td style="text-align: right;">%s</td>' % formatter( fileinfo.compress_size, "%i" ) |
|---|
| 505 | |
|---|
| 506 | total_file_size += fileinfo.file_size |
|---|
| 507 | print '<td style="text-align: right;">%s</td>' % formatter( fileinfo.file_size, "%i" ) |
|---|
| 508 | |
|---|
| 509 | try: |
|---|
| 510 | ratio = float(fileinfo.compress_size)/fileinfo.file_size*100 |
|---|
| 511 | except ZeroDivisionError: |
|---|
| 512 | ratio = 100.0 |
|---|
| 513 | print '<td style="text-align: right;">%s%%</td>' % formatter( ratio, "%0.1f" ) |
|---|
| 514 | |
|---|
| 515 | d = fileinfo.date_time |
|---|
| 516 | print '<td>%0.2i.%0.2i.%i %0.2i:%0.2i:%0.2i</td>' % (d[2],d[1],d[0],d[3],d[4],d[5]) |
|---|
| 517 | |
|---|
| 518 | print "</tr>" |
|---|
| 519 | print "</table>" |
|---|
| 520 | print "<ul>" |
|---|
| 521 | print "<li>total compress size: %s Bytes</li>" % formatter( total_compress_size, "%i" ) |
|---|
| 522 | print "<li>total size: %s Bytes</li>" % formatter( total_file_size, "%i" ) |
|---|
| 523 | try: |
|---|
| 524 | print "<li>Ratio: %s%%</li>" % formatter( float(total_compress_size)/total_file_size*100 ) |
|---|
| 525 | except ZeroDivisionError: |
|---|
| 526 | pass |
|---|
| 527 | print "</ul>" |
|---|
| 528 | |
|---|
| 529 | |
|---|
| 530 | |
|---|
| 531 | #____________________________________________________________________________________ |
|---|
| 532 | |
|---|
| 533 | class mySQL_statistic: |
|---|
| 534 | def __init__( self ): |
|---|
| 535 | self.db = self.connect() |
|---|
| 536 | self.db_conf = { |
|---|
| 537 | "path" : "FileCenter_Stat_Path", |
|---|
| 538 | "item" : "FileCenter_Stat_Item", |
|---|
| 539 | } |
|---|
| 540 | |
|---|
| 541 | def connect( self ): |
|---|
| 542 | import mySQL |
|---|
| 543 | try: |
|---|
| 544 | # SQL connection aufbauen |
|---|
| 545 | return mySQL.mySQL( |
|---|
| 546 | host = cfg.dbHost, |
|---|
| 547 | user = cfg.dbUserName, |
|---|
| 548 | passwd = cfg.dbPassword, |
|---|
| 549 | db = cfg.dbDatabaseName, |
|---|
| 550 | ) |
|---|
| 551 | except Exception, e: |
|---|
| 552 | print "Content-type: text/html\n" |
|---|
| 553 | print "<h1>PyLucid - Error</h1>" |
|---|
| 554 | print "<h2>Can't connect to SQL-DB: '%s'</h2>" % e |
|---|
| 555 | import sys |
|---|
| 556 | sys.exit() |
|---|
| 557 | |
|---|
| 558 | def get_path_id( self, path, auto_create=True ): |
|---|
| 559 | """ id eines Eintrages, existiert der Eintrag noch nicht, wird er angelegt """ |
|---|
| 560 | |
|---|
| 561 | def id( path ): |
|---|
| 562 | return self.db.select( |
|---|
| 563 | select_items = ["id"], |
|---|
| 564 | from_table = self.db_conf["path"], |
|---|
| 565 | where = ( "name", path ) |
|---|
| 566 | )[0]["id"] |
|---|
| 567 | |
|---|
| 568 | try: |
|---|
| 569 | return id( path ) |
|---|
| 570 | except IndexError: |
|---|
| 571 | # Path existiert noch nicht |
|---|
| 572 | if auto_create != True: |
|---|
| 573 | return False |
|---|
| 574 | # Path wird angelegt |
|---|
| 575 | self.db.insert( |
|---|
| 576 | table = self.db_conf["path"], |
|---|
| 577 | data = { "name" : path } |
|---|
| 578 | ) |
|---|
| 579 | return id( path ) |
|---|
| 580 | |
|---|
| 581 | def get_item( self, path_id, item_name, auto_create=True ): |
|---|
| 582 | |
|---|
| 583 | def id( path_id, item_name ): |
|---|
| 584 | result = self.db.select( |
|---|
| 585 | select_items = ["id","count"], |
|---|
| 586 | from_table = self.db_conf["item"], |
|---|
| 587 | where = [ ("path_id", path_id), ("name",item_name) ] |
|---|
| 588 | )[0] |
|---|
| 589 | return result["id"], result["count"] |
|---|
| 590 | |
|---|
| 591 | try: |
|---|
| 592 | return id( path_id, item_name ) |
|---|
| 593 | except IndexError: |
|---|
| 594 | # Item existiert noch nicht |
|---|
| 595 | if auto_create != True: |
|---|
| 596 | return False |
|---|
| 597 | # Eintrag für Item wird angelegt |
|---|
| 598 | self.db.insert( |
|---|
| 599 | table = self.db_conf["item"], |
|---|
| 600 | data = { "path_id": path_id, "name": item_name } |
|---|
| 601 | ) |
|---|
| 602 | return id( path_id, item_name ) |
|---|
| 603 | |
|---|
| 604 | def only_get_count( self, path, item_name ): |
|---|
| 605 | """ Nur den Counterwert zurückliefern, ohne den Zähler zu erhöhen """ |
|---|
| 606 | |
|---|
| 607 | path_id = self.get_path_id( path, auto_create=False ) |
|---|
| 608 | if path_id == False: |
|---|
| 609 | return "Path unknow!" |
|---|
| 610 | return False |
|---|
| 611 | |
|---|
| 612 | item_info = self.get_item( path_id, item_name, auto_create=False ) |
|---|
| 613 | if item_info == False: |
|---|
| 614 | print "Kein item_info [%s] path_id:%s" % (item_name, path_id) |
|---|
| 615 | return 0 |
|---|
| 616 | else: |
|---|
| 617 | return item_info[1] |
|---|
| 618 | |
|---|
| 619 | def count( self, path, item_name ): |
|---|
| 620 | """ Zähler eines Eintrages hochsetzten oder erstellen, wenn noch nicht existend. """ |
|---|
| 621 | |
|---|
| 622 | try: |
|---|
| 623 | path_id = self.get_path_id( path ) |
|---|
| 624 | except Exception, e: |
|---|
| 625 | print "<small>[db Error: %s]</small>" % e |
|---|
| 626 | return |
|---|
| 627 | item_id, item_count = self.get_item( path_id, item_name ) |
|---|
| 628 | |
|---|
| 629 | item_count += 1 |
|---|
| 630 | |
|---|
| 631 | self.db.update( |
|---|
| 632 | table = self.db_conf["item"], |
|---|
| 633 | data = { "count" : item_count }, |
|---|
| 634 | where = ( "id", item_id), |
|---|
| 635 | limit = 1 |
|---|
| 636 | ) |
|---|
| 637 | return item_count |
|---|
| 638 | |
|---|
| 639 | |
|---|
| 640 | #____________________________________________________________________________________ |
|---|
| 641 | |
|---|
| 642 | |
|---|
| 643 | class FileBrowser: |
|---|
| 644 | def __init__( self ): |
|---|
| 645 | if cfg.use_sql_statistic == True: |
|---|
| 646 | # SQL connection aufbauen |
|---|
| 647 | self.statistic = mySQL_statistic() |
|---|
| 648 | |
|---|
| 649 | self.thumbnails = {} # wird von read_dir gefüllt, wenn Thumbnails gefunden wurden |
|---|
| 650 | |
|---|
| 651 | self.absolute_basepath = posixpath.join(os.environ["DOCUMENT_ROOT"], cfg.base_path) |
|---|
| 652 | self.absolute_basepath = posixpath.normpath(self.absolute_basepath) |
|---|
| 653 | |
|---|
| 654 | self.CGIdata = self.getCGIdata() |
|---|
| 655 | |
|---|
| 656 | (self.relativ_path, self.absolute_path) = self.get_current_path() |
|---|
| 657 | # Wird vom Counter verwendet: |
|---|
| 658 | self.root_path = self.absolute_path[len(os.environ["DOCUMENT_ROOT"]):] |
|---|
| 659 | |
|---|
| 660 | if self.CGIdata.has_key("download"): |
|---|
| 661 | # ist ein Download-Proxy-Link aufruf |
|---|
| 662 | # Davor darf noch kein Header gesendet worden sein! |
|---|
| 663 | self.download_proxy( self.CGIdata["download"] ) |
|---|
| 664 | |
|---|
| 665 | # Erst der Header hier ausgeben, weil der Download-Proxy evtl. ausgeführt wurde!!! |
|---|
| 666 | print "Content-type: text/html; charset=utf-8\r\n" |
|---|
| 667 | self.print_head() |
|---|
| 668 | if cfg.debug: |
|---|
| 669 | print "absolute_basepath:", self.absolute_basepath |
|---|
| 670 | |
|---|
| 671 | if self.CGIdata.has_key("zipfile") and (cfg.allow_zipview == True): |
|---|
| 672 | # Es soll eine ZIP-Datei angezeigt werden. |
|---|
| 673 | self.print_back_link() |
|---|
| 674 | zm = zipmanager( self.absolute_path, self.CGIdata["zipfile"] ) |
|---|
| 675 | |
|---|
| 676 | if cfg.use_sql_statistic == True: |
|---|
| 677 | # Aktueller Counterwert, anzeigen und in DB um eins erhöhen |
|---|
| 678 | print cfg.zip_view_txt % self.statistic.count( self.root_path, "<view %s>" % self.CGIdata["zipfile"] ) |
|---|
| 679 | |
|---|
| 680 | self.print_back_link() |
|---|
| 681 | |
|---|
| 682 | elif self.CGIdata.has_key("img"): |
|---|
| 683 | # Ein Bild soll angezeigt werden |
|---|
| 684 | self.print_back_link() |
|---|
| 685 | self.print_img_view( self.CGIdata["img"] ) |
|---|
| 686 | self.print_back_link() |
|---|
| 687 | |
|---|
| 688 | elif self.CGIdata.has_key("next_img"): |
|---|
| 689 | # Das nächste Bild soll angezeigt werden |
|---|
| 690 | self.handle_next_img( self.CGIdata["next_img"] ) |
|---|
| 691 | |
|---|
| 692 | else: |
|---|
| 693 | # Normale Brower-Seite |
|---|
| 694 | files, dirs = self._read_dir() |
|---|
| 695 | self.print_body( files, dirs ) |
|---|
| 696 | |
|---|
| 697 | self.print_footer() |
|---|
| 698 | |
|---|
| 699 | ##_____________________________________________________________________________ |
|---|
| 700 | ## Allgemeine Routinen |
|---|
| 701 | |
|---|
| 702 | def print_back_link( self ): |
|---|
| 703 | print '<p><a href="?path=%s">< back</a></p>' % urllib.quote_plus(self.relativ_path) |
|---|
| 704 | |
|---|
| 705 | def get_current_path( self ): |
|---|
| 706 | if self.CGIdata.has_key( "path" ): |
|---|
| 707 | relativ_path = posixpath.normpath( self.CGIdata["path"] ) |
|---|
| 708 | else: |
|---|
| 709 | relativ_path = "." |
|---|
| 710 | |
|---|
| 711 | cfg.base_path = posixpath.normpath( cfg.base_path ) |
|---|
| 712 | |
|---|
| 713 | absolute_path = posixpath.join( self.absolute_basepath, relativ_path ) |
|---|
| 714 | absolute_path = posixpath.normpath( absolute_path ) |
|---|
| 715 | |
|---|
| 716 | self.check_absolute_path( absolute_path ) |
|---|
| 717 | |
|---|
| 718 | return relativ_path, absolute_path |
|---|
| 719 | |
|---|
| 720 | |
|---|
| 721 | def error( self, txt ): |
|---|
| 722 | """Fehlermeldung mit anschließendem sys.exit()""" |
|---|
| 723 | print "Content-Type: text/html\n" |
|---|
| 724 | print HTML_head |
|---|
| 725 | print "<h2>%s</h2>" % txt |
|---|
| 726 | print '<a href="?">back</a>' |
|---|
| 727 | self.print_footer() |
|---|
| 728 | sys.exit() |
|---|
| 729 | |
|---|
| 730 | def check_absolute_path( self, absolute_path ): |
|---|
| 731 | """ |
|---|
| 732 | Überprüft einen absoluten Pfad |
|---|
| 733 | """ |
|---|
| 734 | if (absolute_path.find("..") != -1) or (absolute_path.find("//") != -1): |
|---|
| 735 | # Hackerscheiß schon mal ausschließen |
|---|
| 736 | self.error( "not allowed!" ) |
|---|
| 737 | |
|---|
| 738 | if not absolute_path.startswith(self.absolute_basepath): |
|---|
| 739 | # Fängt nicht wie Basis-Pfad an... Da stimmt was nicht |
|---|
| 740 | #~ print "Content-Type: text/html\n" |
|---|
| 741 | #~ print "X%sX" % absolute_path |
|---|
| 742 | #~ print "X%sX" % self.absolute_basepath |
|---|
| 743 | self.error( "permission deny." ) |
|---|
| 744 | |
|---|
| 745 | if not posixpath.isdir( absolute_path ): |
|---|
| 746 | # Den Pfad gibt es nicht |
|---|
| 747 | self.error( "'%s' not exists" % absolute_path ) |
|---|
| 748 | |
|---|
| 749 | def getCGIdata( self ): |
|---|
| 750 | "CGI-POST Daten auswerten" |
|---|
| 751 | data = {} |
|---|
| 752 | FieldStorageData = cgi.FieldStorage( keep_blank_values=True ) |
|---|
| 753 | for i in FieldStorageData.keys(): data[i] = FieldStorageData.getvalue(i) |
|---|
| 754 | return data |
|---|
| 755 | |
|---|
| 756 | |
|---|
| 757 | def _read_dir( self ): |
|---|
| 758 | "Einlesen des Verzeichnisses" |
|---|
| 759 | |
|---|
| 760 | def is_thumb( name, file_name ): |
|---|
| 761 | "Kleine Hilfsfunktion um Thumbnails raus zu filtern" |
|---|
| 762 | for suffix in cfg.thumb_suffix: |
|---|
| 763 | if name[-len(suffix):] == suffix: |
|---|
| 764 | # Aktuelle Datei ist ein Thumbnail! |
|---|
| 765 | clean_name = name[:-len(suffix)] |
|---|
| 766 | self.thumbnails[clean_name] = file_name |
|---|
| 767 | return True |
|---|
| 768 | return False |
|---|
| 769 | |
|---|
| 770 | if cfg.debug: |
|---|
| 771 | print "read '%s'..." % self.absolute_path |
|---|
| 772 | |
|---|
| 773 | files = [] |
|---|
| 774 | dirs = [] |
|---|
| 775 | for item in os.listdir( self.absolute_path ): |
|---|
| 776 | abs_path = posixpath.join( self.absolute_path, item ) |
|---|
| 777 | if posixpath.isfile( abs_path ): |
|---|
| 778 | # Dateien verarbeiten |
|---|
| 779 | |
|---|
| 780 | if item in cfg.file_filter: |
|---|
| 781 | # Datei soll nicht angezeigt werden |
|---|
| 782 | continue |
|---|
| 783 | |
|---|
| 784 | name, ext = posixpath.splitext( item ) |
|---|
| 785 | |
|---|
| 786 | # Thumbnails rausfiltern |
|---|
| 787 | if is_thumb( name, item ): |
|---|
| 788 | # Ist ein Thumbnail -> soll nicht in die files-Liste! |
|---|
| 789 | continue |
|---|
| 790 | |
|---|
| 791 | if ext in cfg.ext_whitelist: |
|---|
| 792 | files.append( item ) |
|---|
| 793 | |
|---|
| 794 | else: |
|---|
| 795 | # Verzeichnis verarbeiten |
|---|
| 796 | |
|---|
| 797 | if cfg.allow_subdirs: |
|---|
| 798 | # Unterverz. sollen angezeigt werden |
|---|
| 799 | if not item in cfg.dir_filter: |
|---|
| 800 | dirs.append( item ) |
|---|
| 801 | |
|---|
| 802 | files.sort() |
|---|
| 803 | dirs.sort() |
|---|
| 804 | |
|---|
| 805 | if self.relativ_path != ".": |
|---|
| 806 | # Nur erweitern, wenn man nicht schon im Hauptverzeichnis ist |
|---|
| 807 | dirs.insert(0,"..") |
|---|
| 808 | |
|---|
| 809 | return files, dirs |
|---|
| 810 | |
|---|
| 811 | |
|---|
| 812 | ##_____________________________________________________________________________ |
|---|
| 813 | ## Datei-Routinen |
|---|
| 814 | |
|---|
| 815 | def get_charset( self, absolute_filepath ): |
|---|
| 816 | """Ermittelt das charset im Dateiheader: z.b.: # -*- coding: UTF-8 -*- """ |
|---|
| 817 | |
|---|
| 818 | f = file( absolute_filepath, "rU" ) |
|---|
| 819 | |
|---|
| 820 | headlines = [] |
|---|
| 821 | for i in range(2): |
|---|
| 822 | # ersten zwei Zeilen lesen |
|---|
| 823 | headlines.append( f.readline() ) |
|---|
| 824 | |
|---|
| 825 | f.close() |
|---|
| 826 | |
|---|
| 827 | return re.findall(r"- coding: (.+?) -", "".join(headlines) )[0] |
|---|
| 828 | |
|---|
| 829 | def process_file_command( self, filename ): |
|---|
| 830 | """ Datei Information mit Linux 'file' Befehl zusammentragen """ |
|---|
| 831 | |
|---|
| 832 | command = "file %s" % posixpath.join(self.absolute_path,filename) |
|---|
| 833 | fileinfo = os.popen( command ).readlines()[0] |
|---|
| 834 | |
|---|
| 835 | fileinfo = fileinfo.split(":",1)[1] |
|---|
| 836 | if fileinfo.find("ERROR") != -1: |
|---|
| 837 | # Ersatz für >"ERROR" in fileinfo< ;) |
|---|
| 838 | return "" |
|---|
| 839 | else: |
|---|
| 840 | return fileinfo |
|---|
| 841 | |
|---|
| 842 | |
|---|
| 843 | def print_fileinfo( self, filename, absolute_filepath, base, ext ): |
|---|
| 844 | """ file-Informationen mittels file oder manuell """ |
|---|
| 845 | |
|---|
| 846 | print "<small>" |
|---|
| 847 | |
|---|
| 848 | if ext in cfg.override_fileinfo: |
|---|
| 849 | # Es soll kein 'file'-Befehl ausgeführt werden |
|---|
| 850 | print cfg.override_fileinfo[ext] |
|---|
| 851 | if ext == ".py": |
|---|
| 852 | try: |
|---|
| 853 | print " - Charset: %s" % self.get_charset( absolute_filepath ) |
|---|
| 854 | except: |
|---|
| 855 | pass |
|---|
| 856 | else: |
|---|
| 857 | # Holt informationen über den 'file'-Befehl |
|---|
| 858 | try: |
|---|
| 859 | print self.process_file_command( filename ) |
|---|
| 860 | except Exception, e: |
|---|
| 861 | print "FileInfo Error:<br/>%s" % e |
|---|
| 862 | |
|---|
| 863 | print "</small>" |
|---|
| 864 | |
|---|
| 865 | |
|---|
| 866 | ##_____________________________________________________________________________ |
|---|
| 867 | ## Seiten generierung |
|---|
| 868 | |
|---|
| 869 | def print_head( self ): |
|---|
| 870 | """Kopf + Überschrift + backLink ausgeben""" |
|---|
| 871 | |
|---|
| 872 | # Key für zusätzliche CSS Einträge ans Dict hinzufügen |
|---|
| 873 | cfg.html_head.update( |
|---|
| 874 | { |
|---|
| 875 | "additional_CSS" : cfg.additional_CSS, |
|---|
| 876 | "pre_title" : cfg.pre_title, |
|---|
| 877 | "post_title" : cfg.post_title, |
|---|
| 878 | } |
|---|
| 879 | ) |
|---|
| 880 | print HTML_head % cfg.html_head |
|---|
| 881 | |
|---|
| 882 | current_path = posixpath.join(cfg.base_path, self.relativ_path) |
|---|
| 883 | current_path = posixpath.normpath(current_path) |
|---|
| 884 | if current_path == ".": current_path = "/" |
|---|
| 885 | print "<h1>%s%s%s</h1>" % (cfg.pre_title, current_path, cfg.post_title) |
|---|
| 886 | |
|---|
| 887 | if self.relativ_path in cfg.dir_backlinks: |
|---|
| 888 | print cfg.dir_backlinks_code % { |
|---|
| 889 | "url" : cfg.dir_backlinks[ self.relativ_path ][1], |
|---|
| 890 | "txt" : cgi.escape( cfg.dir_backlinks[ self.relativ_path ][0] ) |
|---|
| 891 | } |
|---|
| 892 | |
|---|
| 893 | |
|---|
| 894 | def print_dir( self, dir ): |
|---|
| 895 | """Verzeichnis auflisten""" |
|---|
| 896 | |
|---|
| 897 | if dir != "..": |
|---|
| 898 | before = "" |
|---|
| 899 | after = " >" |
|---|
| 900 | else: |
|---|
| 901 | before = "< " |
|---|
| 902 | after = "" |
|---|
| 903 | |
|---|
| 904 | print '<li><a href="?path=%(url)s">%(before)s%(txt)s%(after)s</a></li>' % { |
|---|
| 905 | "url" : urllib.quote( posixpath.normpath( posixpath.join( self.relativ_path, dir ) ) ), |
|---|
| 906 | "before" : before, |
|---|
| 907 | "txt" : dir, |
|---|
| 908 | "after" : after |
|---|
| 909 | } |
|---|
| 910 | |
|---|
| 911 | |
|---|
| 912 | def print_body( self, files, dirs ): |
|---|
| 913 | """Schleife für die eigentliche Seite""" |
|---|
| 914 | |
|---|
| 915 | if cfg.allow_subdirs: |
|---|
| 916 | print '<ul class="gallery_dirs">' |
|---|
| 917 | for dir in dirs: |
|---|
| 918 | self.print_dir( dir ) |
|---|
| 919 | print "</ul>" |
|---|
| 920 | |
|---|
| 921 | print '<ul class="gallery_files">' |
|---|
| 922 | for file in files: |
|---|
| 923 | self.print_file( file ) |
|---|
| 924 | |
|---|
| 925 | print "</ul>" |
|---|
| 926 | |
|---|
| 927 | if cfg.use_sql_statistic == True: |
|---|
| 928 | # Aktueller Counterwert, anzeigen und in DB um eins erhöhen |
|---|
| 929 | print cfg.dir_counter_txt % self.statistic.count( self.root_path, "<dir %s>" % self.root_path ) |
|---|
| 930 | |
|---|
| 931 | |
|---|
| 932 | def print_gallery_pic( self, pic_base_name, img_name, file_url ): |
|---|
| 933 | |
|---|
| 934 | print '<li class="gallery_pic">' |
|---|
| 935 | |
|---|
| 936 | print '<a href="?path=%s&img=%s">' % ( |
|---|
| 937 | urllib.quote( self.relativ_path ), |
|---|
| 938 | urllib.quote( img_name ) |
|---|
| 939 | ) |
|---|
| 940 | |
|---|
| 941 | if pic_base_name in self.thumbnails: |
|---|
| 942 | # Es gibt ein Thumbnail |
|---|
| 943 | img_path = posixpath.join( self.relativ_path, self.thumbnails[pic_base_name] ) |
|---|
| 944 | print '<img src="%s" />' % img_path |
|---|
| 945 | else: |
|---|
| 946 | # Kein passendes Thumb. gefunden -> Original Bild wird als Thumb. verwendet |
|---|
| 947 | print '<img src="%s" width="%s" height="%s" />' % ( |
|---|
| 948 | file_url, cfg.resize_thumb_size[0], cfg.resize_thumb_size[1] |
|---|
| 949 | ) |
|---|
| 950 | |
|---|
| 951 | print "<br/>" |
|---|
| 952 | print "<small>%s</small>" % pic_base_name.replace("_"," ") |
|---|
| 953 | print "</a>" |
|---|
| 954 | print "</li>" |
|---|
| 955 | |
|---|
| 956 | def _is_gallery_img( self, base, ext ): |
|---|
| 957 | """ Unterscheidet Bilder für Gallerymodus """ |
|---|
| 958 | if not (ext in cfg.pic_ext): |
|---|
| 959 | # Ist überhaupt kein Bild |
|---|
| 960 | return False |
|---|
| 961 | |
|---|
| 962 | if base in self.thumbnails: |
|---|
| 963 | # Ein Thumbnail ist für das aktuelle Bild vorhanden |
|---|
| 964 | return base |
|---|
| 965 | |
|---|
| 966 | # Ist ein Bild, könnte für eine Thumbnailansicht sein. |
|---|
| 967 | for suffix in cfg.thumb_pic_filter: |
|---|
| 968 | # Testet ob das Bild ein suffix besitzt (z.B. *_WEB.jpg) |
|---|
| 969 | if base[-len(suffix):] == suffix: |
|---|
| 970 | # Aktuelle Datei ist ein Gallerie-Bild |
|---|
| 971 | return base[:-len(suffix)] |
|---|
| 972 | |
|---|
| 973 | return False |
|---|
| 974 | |
|---|
| 975 | |
|---|
| 976 | def print_file( self, filename ): |
|---|
| 977 | """ Dateiinfromationen ermitteln und auflisten """ |
|---|
| 978 | |
|---|
| 979 | absolute_filepath = posixpath.join(self.absolute_path,filename) |
|---|
| 980 | base, ext = posixpath.splitext( filename ) |
|---|
| 981 | file_url = posixpath.join( self.relativ_path, filename ) |
|---|
| 982 | |
|---|
| 983 | clean_base_name = self._is_gallery_img( base, ext ) |
|---|
| 984 | if clean_base_name != False: |
|---|
| 985 | # Aktuelle Datei ist ein Gallerie-Bild |
|---|
| 986 | self.print_gallery_pic( clean_base_name, filename, file_url ) |
|---|
| 987 | return |
|---|
| 988 | |
|---|
| 989 | print '<li class="normal"><br/>' |
|---|
| 990 | |
|---|
| 991 | #~ if ext in cfg.ext_download_proxy: |
|---|
| 992 | # Download-Proxy-Link |
|---|
| 993 | print '<a href="?download=%s">%s</a>' % ( urllib.quote(file_url), urllib.quote(filename) ) |
|---|
| 994 | #~ else: |
|---|
| 995 | #~ # Normaler Link |
|---|
| 996 | #~ print '<a href="%s" type="application/octet-stream">%s</a>' % \ |
|---|
| 997 | #~ ( urllib.quote(file_url), urllib.quote(filename) ) |
|---|
| 998 | |
|---|
| 999 | print "<br/>" |
|---|
| 1000 | |
|---|
| 1001 | self.print_fileinfo( filename, absolute_filepath, base, ext ) |
|---|
| 1002 | |
|---|
| 1003 | print "<br/>" |
|---|
| 1004 | |
|---|
| 1005 | if cfg.allow_zipview and (ext == ".zip"): |
|---|
| 1006 | print '<a href="?path=%s&zipfile=%s">view ZIP file</a>' % \ |
|---|
| 1007 | ( urllib.quote(self.relativ_path) , urllib.quote(filename) ) |
|---|
| 1008 | print "<br/>" |
|---|
| 1009 | |
|---|
| 1010 | print "<br/>" |
|---|
| 1011 | |
|---|
| 1012 | item_stat = os.stat( absolute_filepath ) |
|---|
| 1013 | print "<small>%s</small>" % time.strftime( |
|---|
| 1014 | '%d.%m.%Y %H:%M', |
|---|
| 1015 | time.localtime(item_stat[stat.ST_MTIME]) |
|---|
| 1016 | ) |
|---|
| 1017 | print "<br/>" |
|---|
| 1018 | |
|---|
| 1019 | file_KBytes = item_stat[stat.ST_SIZE]/1024.0 |
|---|
| 1020 | try: |
|---|
| 1021 | locale.setlocale(locale.LC_ALL, "de_DE.UTF-8") |
|---|
| 1022 | print "%s KB" % locale.format("%0.1f", file_KBytes, True) |
|---|
| 1023 | except: |
|---|
| 1024 | print "%0.1f KB" % file_KBytes |
|---|
| 1025 | |
|---|
| 1026 | if cfg.use_sql_statistic == True: |
|---|
| 1027 | print "<br/>" |
|---|
| 1028 | print cfg.file_count_txt % self.statistic.only_get_count( self.root_path, filename ) |
|---|
| 1029 | |
|---|
| 1030 | print "</li>" |
|---|
| 1031 | |
|---|
| 1032 | |
|---|
| 1033 | def print_footer( self ): |
|---|
| 1034 | """zurück-Link + HTML-Fussdaten""" |
|---|
| 1035 | print '<hr id="gallery_clear">' |
|---|
| 1036 | |
|---|
| 1037 | # zurück-Link |
|---|
| 1038 | cfg.footer_backlink["txt"] = cgi.escape( cfg.footer_backlink["txt"] ) |
|---|
| 1039 | print cfg.footer_backlink_code % cfg.footer_backlink |
|---|
| 1040 | |
|---|
| 1041 | print '<div id="gallery_footer">' |
|---|
| 1042 | print "Generated by %s" % __info__ |
|---|
| 1043 | print "| render time %0.2fsec." % (time.time() - start_time) |
|---|
| 1044 | print "</div></body></html>" |
|---|
| 1045 | |
|---|
| 1046 | |
|---|
| 1047 | ##_____________________________________________________________________________ |
|---|
| 1048 | ## Zusätzliche Funktionen |
|---|
| 1049 | |
|---|
| 1050 | def download_proxy( self, filename ): |
|---|
| 1051 | abs_filepath = posixpath.join( os.environ["DOCUMENT_ROOT"], cfg.base_path, filename ) |
|---|
| 1052 | abs_filepath = posixpath.normpath( abs_filepath ) |
|---|
| 1053 | |
|---|
| 1054 | abs_path, filename = posixpath.split( abs_filepath ) |
|---|
| 1055 | |
|---|
| 1056 | self.check_absolute_path( abs_path ) |
|---|
| 1057 | |
|---|
| 1058 | #~ print 'Content-Type: text/html\n\n<pre>' # Debug |
|---|
| 1059 | |
|---|
| 1060 | try: |
|---|
| 1061 | charset = self.get_charset( abs_filepath ) |
|---|
| 1062 | except: |
|---|
| 1063 | charset = [] |
|---|
| 1064 | |
|---|
| 1065 | #~ print 'Cache-Control: no-cache, must-revalidate' |
|---|
| 1066 | #~ print 'Pragma: no-cache' |
|---|
| 1067 | print 'Content-Length: %s' % posixpath.getsize( abs_filepath ) |
|---|
| 1068 | print 'Content-Disposition: attachment; filename=%s' % filename |
|---|
| 1069 | print 'Content-Transfer-Encoding: binary' |
|---|
| 1070 | if charset != []: |
|---|
| 1071 | print 'Content-Type: text/plain; charset=%s\n' % charset |
|---|
| 1072 | else: |
|---|
| 1073 | print 'Content-Type: text/plain\n' |
|---|
| 1074 | |
|---|
| 1075 | f = file( abs_filepath, "rb" ) |
|---|
| 1076 | while True: |
|---|
| 1077 | # In Blöcken, die Datei rausprinten... |
|---|
| 1078 | data = f.read(8192) |
|---|
| 1079 | if not data: |
|---|
| 1080 | break |
|---|
| 1081 | sys.stdout.write(data) |
|---|
| 1082 | f.close() |
|---|
| 1083 | |
|---|
| 1084 | if cfg.use_sql_statistic == True: |
|---|
| 1085 | # Counter um eins erhöhen |
|---|
| 1086 | self.statistic.count( self.root_path, filename ) |
|---|
| 1087 | |
|---|
| 1088 | sys.exit() |
|---|
| 1089 | |
|---|
| 1090 | def print_img_view( self, img_name ): |
|---|
| 1091 | """ Anzeigen eines angeklickten Bildes """ |
|---|
| 1092 | |
|---|
| 1093 | if cfg.use_sql_statistic == True: |
|---|
| 1094 | # Aktueller Counterwert, wird in DB um eins erhöht |
|---|
| 1095 | count = self.statistic.count( self.root_path, img_name ) |
|---|
| 1096 | |
|---|
| 1097 | print '<a href="?path=%s&next_img=%s" title="next >" id="img_view">' % ( |
|---|
| 1098 | urllib.quote(self.relativ_path), urllib.quote(img_name) |
|---|
| 1099 | ) |
|---|
| 1100 | print '<img src="%s"/><br/>' % posixpath.join( self.relativ_path, img_name ) |
|---|
| 1101 | print '%s<br/>' % img_name |
|---|
| 1102 | print "<small>Counter: %s</small>" % count |
|---|
| 1103 | print '</a>' |
|---|
| 1104 | |
|---|
| 1105 | def handle_next_img( self, old_img_name ): |
|---|
| 1106 | """ Das nächste Bild soll angezeigt werden """ |
|---|
| 1107 | |
|---|
| 1108 | files, dirs = self._read_dir() |
|---|
| 1109 | |
|---|
| 1110 | if not old_img_name in files: |
|---|
| 1111 | # Der alte Name ist wohl falsch -> Normale Seite aufbauen |
|---|
| 1112 | self.print_body( files, dirs ) |
|---|
| 1113 | return |
|---|
| 1114 | |
|---|
| 1115 | current_pos = files.index( old_img_name ) |
|---|
| 1116 | |
|---|
| 1117 | try: |
|---|
| 1118 | next_img = files[ current_pos + 1 ] |
|---|
| 1119 | except IndexError: |
|---|
| 1120 | # Kein neues Image vorhanden -> Normale Seite aufbauen |
|---|
| 1121 | self.print_body( files, dirs ) |
|---|
| 1122 | return |
|---|
| 1123 | |
|---|
| 1124 | base, ext = posixpath.splitext( next_img ) |
|---|
| 1125 | if self._is_gallery_img( base, ext ) == False: |
|---|
| 1126 | # Aktuelle Datei ist kein Gallery-Bild -> Normale Seite aufbauen |
|---|
| 1127 | self.print_body( files, dirs ) |
|---|
| 1128 | return |
|---|
| 1129 | |
|---|
| 1130 | # Alles klar, nächstes Bild wird gezeigt |
|---|
| 1131 | self.print_back_link() |
|---|
| 1132 | self.print_img_view( next_img ) |
|---|
| 1133 | self.print_back_link() |
|---|
| 1134 | |
|---|
| 1135 | |
|---|
| 1136 | |
|---|
| 1137 | |
|---|
| 1138 | |
|---|
| 1139 | |
|---|
| 1140 | |
|---|
| 1141 | |
|---|