~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to plugins/wb.doclib/mysqldoclib.py

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import getopt, sys
 
2
import os.path
 
3
try:
 
4
    from pysqlite2 import dbapi2 as sqlite3
 
5
except ImportError:
 
6
    import sqlite3
 
7
import codecs
 
8
import re
 
9
import threading, Queue, time, datetime
 
10
import cgi
 
11
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
 
12
from urlparse import urlparse
 
13
from os import curdir, sep, makedirs
 
14
import zipfile
 
15
 
 
16
global_app_data_dir = "./"
 
17
 
 
18
# --------------------------------
 
19
# Log functions
 
20
 
 
21
def log_error(messagetext):
 
22
  """Log an error message."""
 
23
  print "Error: ", messagetext
 
24
    
 
25
 
 
26
def log_message(messagetext):
 
27
  """Log a general message."""
 
28
  print messagetext
 
29
 
 
30
def verbose_print(verbose, messagetext):
 
31
  """Output a general message if verbose is turned on."""
 
32
  if verbose:
 
33
    print messagetext
 
34
 
 
35
# --------------------------------
 
36
# Utility functions
 
37
 
 
38
 
 
39
def get_app_data_dir():
 
40
  if sys.platform.startswith("darwin"):
 
41
    path = os.path.expanduser("~/Library/Application Support/MySQL/DocLibrary/")
 
42
  elif sys.platform.startswith("win"):
 
43
    path = os.path.expandvars("${APPDATA}/MySQL/DocLibrary/")
 
44
  else:
 
45
    path = os.path.expanduser("~/.mysql/DocLibrary/")
 
46
 
 
47
  if not os.path.exists(path): 
 
48
    os.makedirs(path)
 
49
 
 
50
  return path
 
51
 
 
52
def get_lib_db_path():
 
53
  path = os.path.join(get_app_data_dir(), "mysqldoclib.sqlite")
 
54
  if os.path.exists(path):
 
55
    return path
 
56
  return os.path.join(global_app_data_dir, "mysqldoclib.sqlite")
 
57
        
 
58
  
 
59
def get_webui_db_path():
 
60
  path = os.path.join(get_app_data_dir(), "mysqldoclib_webui.sqlite")
 
61
  if os.path.exists(path):
 
62
    return path
 
63
  return os.path.join(global_app_data_dir, "mysqldoclib_webui.sqlite")
 
64
 
 
65
def get_user_db_path():
 
66
  path = os.path.join(get_app_data_dir(), "mysqldoclib_usr.sqlite")
 
67
  if not os.path.exists(path):
 
68
    try:
 
69
      # Connect to the database
 
70
      db_conn = sqlite3.connect(path)
 
71
      try:
 
72
        # Execute SQL
 
73
        try:
 
74
          c = db_conn.cursor()
 
75
          try:
 
76
            c.execute("""
 
77
              CREATE TABLE IF NOT EXISTS page_rating (
 
78
                id_page_rating INTEGER PRIMARY KEY AUTOINCREMENT,
 
79
                id_manual_type INTEGER NOT NULL,
 
80
                title TEXT NOT NULL,
 
81
                rating INTEGER
 
82
              )""")
 
83
            c.execute("CREATE INDEX IF NOT EXISTS idx_page_rating ON page_rating(title)")
 
84
            
 
85
            c.execute("""
 
86
              CREATE TABLE IF NOT EXISTS page_view (
 
87
                id_page_view INTEGER PRIMARY KEY AUTOINCREMENT,
 
88
                path TEXT NOT NULL,
 
89
                hits INTEGER
 
90
              )""")
 
91
            c.execute("CREATE INDEX IF NOT EXISTS idx_page_view ON page_view(path)")
 
92
            
 
93
            c.execute("""
 
94
              CREATE TABLE IF NOT EXISTS lib_search (
 
95
                id_lib_search INTEGER PRIMARY KEY AUTOINCREMENT,
 
96
                search_text TEXT NOT NULL,
 
97
                hits INTEGER
 
98
              )""")
 
99
            c.execute("CREATE INDEX IF NOT EXISTS idx_lib_search ON lib_search(search_text)")
 
100
 
 
101
            c.execute("""
 
102
              CREATE TABLE IF NOT EXISTS lib_status (
 
103
                id_lib_file INTEGER PRIMARY KEY AUTOINCREMENT,
 
104
                id_lib INTEGER,
 
105
                downloaded INTEGER,
 
106
                is_selected INTEGER
 
107
              )""")
 
108
            c.execute("INSERT INTO lib_status(id_lib, downloaded, is_selected) VALUES (1, 1, 1)")
 
109
            
 
110
            db_conn.commit()
 
111
          finally:
 
112
            c.close()
 
113
        except Exception, e:
 
114
          log_error("Error while creating the usr database. %r" % e)
 
115
 
 
116
      finally:
 
117
        db_conn.close();
 
118
    except Exception, e:
 
119
        log_error("An error occurred while creating the usr database at %s. %r" % (path, e))
 
120
        raise e
 
121
  return path
 
122
 
 
123
def get_module_installation_dir():
 
124
  return "./"
 
125
 
 
126
def read_file_content(filename, encoding="utf-8"):
 
127
  """Read the contents of a text file"""
 
128
  try:
 
129
    # Open file in read mode using the correct encoding
 
130
    f = codecs.open(filename, "r", encoding)
 
131
    try:
 
132
      # Return the file contents
 
133
      return f.read()
 
134
    except Exception, e:
 
135
      log_error("An error occurred reading from the file %s." % filename)
 
136
    finally:
 
137
      f.close();
 
138
  except Exception, e:
 
139
    log_error("An error occurred opening the file %s." % filename)
 
140
 
 
141
def save_file_content(filename, content, encoding="utf-8"):
 
142
  """Read the contents of a text file"""
 
143
  try:
 
144
    # Open file in read mode using the correct encoding
 
145
    f = codecs.open(filename, "w", encoding)
 
146
    try:
 
147
      # Return the file contents
 
148
      f.write(content)
 
149
    except Exception, e:
 
150
      log_error("An error occurred reading from the file %s." % filename)
 
151
    finally:
 
152
      f.close();
 
153
  except Exception, e:
 
154
    log_error("An error occurred opening the file %s." % filename)
 
155
 
 
156
# --------------------------------
 
157
# Library creation functions
 
158
 
 
159
def execute_sql_script(db_conn, filename, encoding = "UTF-8"):
 
160
  """Executes a SQL script file"""
 
161
  
 
162
  # Read SQL file
 
163
  sql_commands = read_file_content(filename, encoding)
 
164
  if not sql_commands:
 
165
    return False
 
166
 
 
167
  try:
 
168
    c = db_conn.cursor()
 
169
    
 
170
    try:
 
171
      # Run the SQL Script to create the database
 
172
      c.executescript(sql_commands)
 
173
      
 
174
      db_conn.commit()
 
175
      
 
176
      return True
 
177
  
 
178
    except Exception, e:
 
179
      log_error("An error occurred while executing the SQL script. %r" % e);
 
180
      return False
 
181
    finally:
 
182
      c.close()
 
183
  except Exception, e:
 
184
    log_error("An error occurred aquiring a database cursor. %r" % e);
 
185
    return False
 
186
 
 
187
# --------------------------------
 
188
# Manual page caching functions
 
189
 
 
190
def html_remove_tags(data):
 
191
  """Removes all HTML tags from a given string"""
 
192
  # other expression: '<[^>]*?>'
 
193
  p = re.compile(r'<[^<]*?/?>')
 
194
  return p.sub(' ', data)
 
195
 
 
196
def html_remove_extra_spaces(data):
 
197
  """Removes all extra spaces from a given string"""
 
198
  p = re.compile(r'\s+')
 
199
  return p.sub(' ', data)
 
200
 
 
201
def html_get_page_title(data):
 
202
  """Returns the chapter and title of a manual html page string"""
 
203
  p = re.compile(r'\<title\>(?P<Chapter>(Chapter\s)?[A-Z]?[\d\.]+)\s*(?P<Title>.*)\<\/title\>')
 
204
  match = p.search(data)
 
205
  
 
206
  if match:
 
207
    return match.group('Chapter'), match.group('Title')
 
208
  else:
 
209
    p = re.compile(r'\<title\>(?P<Title>.*)\<\/title\>')
 
210
    match = p.search(data)
 
211
    if match:
 
212
      return "", match.group('Title')
 
213
    else:
 
214
      return "", ""
 
215
 
 
216
def html_apply_page_modifications(data):
 
217
  """Make required changes to the html"""
 
218
  p = re.compile(r'\<\/title\>')
 
219
  data = p.sub('</title><link rel="stylesheet" type="text/css" href="/webui/stylesheets/docs.css" />', data)
 
220
  
 
221
  p = re.compile(r'\starget=\"_top\"')
 
222
  data = p.sub('', data)
 
223
  return data
 
224
 
 
225
class ManualPageData():
 
226
  """Data class that holds information about a manual page"""
 
227
  filename = ""
 
228
  title = ""
 
229
  chapter = ""
 
230
  content = ""
 
231
  html_content = ""
 
232
 
 
233
class ScanManualPageThread(threading.Thread):
 
234
  filename_queue = 0
 
235
  manual_page_data_queue = 0
 
236
  lib_zip_file = ""
 
237
  def run(self):
 
238
    try:
 
239
      while True:
 
240
        # get a filename from the queue, do not block
 
241
        filename = self.filename_queue.get(False)
 
242
        #full_filename = os.path.join(self.path, filename)
 
243
        
 
244
        try:
 
245
          # Open HTML file as a utf-8 file
 
246
          #html_string = read_file_content(full_filename)
 
247
          #if not html_string:
 
248
            #continue
 
249
          
 
250
          html_file = self.lib_zip_file.open(filename)
 
251
          html_string = unicode(html_file.read(), "utf-8")
 
252
          if not html_string:
 
253
            continue
 
254
          
 
255
          # Make file modifications
 
256
          #save_file_content(full_filename, html_apply_page_modifications(html_string))
 
257
        
 
258
          # Add new page data object to the queue
 
259
          manual_page_data = ManualPageData()
 
260
          manual_page_data.filename = filename
 
261
          manual_page_data.chapter, manual_page_data.title = html_get_page_title(html_string)
 
262
          manual_page_data.content = html_remove_extra_spaces(html_remove_tags(html_string))
 
263
          manual_page_data.html_content = html_apply_page_modifications(html_string)
 
264
          
 
265
          self.manual_page_data_queue.put(manual_page_data)
 
266
        except Exception, e:
 
267
          log_error("An error processing the page. %r" % e)
 
268
          break
 
269
          
 
270
    except Queue.Empty:
 
271
      pass
 
272
 
 
273
def process_manual_page_data_queue(db_conn, lib_zip_file, path, id_manual, file_nr, file_count, manual_page_data_queue):
 
274
  try:
 
275
    # Get database cursor
 
276
    c = db_conn.cursor()
 
277
    files_processed = 0
 
278
    
 
279
    # Check if there are manual_page_data objects in the queue and if so, process them
 
280
    while True:
 
281
      try:
 
282
        # Fetch manual_page_data object from the queue if available, do not block
 
283
        manual_page_data = manual_page_data_queue.get(False)
 
284
        
 
285
        try:
 
286
          # Insert HTML page
 
287
          c.execute("INSERT OR REPLACE INTO web_object(path, content_type, content, allow_embedded_code_execution) VALUES(?, ?, ?, ?)",
 
288
            ["/" + path + "/" + os.path.basename(manual_page_data.filename), "text/html", manual_page_data.html_content, 0])
 
289
 
 
290
          # Insert manual page and content
 
291
          c.execute("INSERT INTO page(id_manual, id_web_object, title, chapter) VALUES (?, ?, ?, ?)", 
 
292
            [id_manual, c.lastrowid, manual_page_data.title, manual_page_data.chapter])
 
293
          c.execute("INSERT INTO page_content(id_page, title, content) VALUES (?, ?, ?)", 
 
294
            [c.lastrowid, manual_page_data.title, manual_page_data.content])
 
295
 
 
296
          files_processed += 1
 
297
          if (files_processed % 100 == 0):
 
298
            log_message("%d file(s) of %d processed..." % (file_nr + files_processed, file_count));
 
299
 
 
300
        except Exception, e:
 
301
          log_error("An error occurred while inserting the page values for %s. %r" % (manual_page_data.filename, e))
 
302
      except Queue.Empty:
 
303
        break
 
304
    
 
305
    return files_processed
 
306
  except Exception, e:
 
307
      log_error("An error occurred aquiring a database cursor. %r" % e)
 
308
 
 
309
 
 
310
def cache_pages(db_conn, manual_ids):
 
311
  try:
 
312
    # Get database cursor
 
313
    c = db_conn.cursor()
 
314
    try:
 
315
      # Get all available manuals versions
 
316
      c.execute("""-- Select all manuals
 
317
        SELECT m.id_manual, m.directory, m.description
 
318
        FROM manual m
 
319
        ORDER BY m.id_manual""")
 
320
      rows = c.fetchall()
 
321
 
 
322
    except Exception, e:
 
323
      log_error("An error occurred while executing the SQL commands. %r" % e)
 
324
    finally:
 
325
      c.close()
 
326
 
 
327
    # Loop over all manuals and cache the contents of the file directory
 
328
    for id_manual, directory, description in rows:
 
329
    
 
330
      # if the number of manuals has been limited
 
331
      if manual_ids:
 
332
        # only include the given manual
 
333
        if not str(id_manual) in manual_ids:
 
334
          log_message("Skipping manual %s." % description)
 
335
          continue
 
336
      
 
337
      zip_file = directory + ".zip"
 
338
      
 
339
      # Locate the zip file, first in the user app dir
 
340
      # zip_file_path = os.path.join(os.path.join(get_app_data_dir(), 'repository'), zip_file)
 
341
      # if not os.path.exists(zip_file_path):
 
342
      # then in the ./repository dir
 
343
      zip_file_path = os.path.join(os.path.join('.', 'repository'), zip_file)
 
344
      if not os.path.exists(zip_file_path):
 
345
        log_error("The zip file %s cannot be found." % zip_file_path)
 
346
        continue
 
347
      
 
348
      log_message("Processing %s ..." % zip_file_path)
 
349
      
 
350
      lib_zip_file = zipfile.ZipFile(zip_file_path, 'r')
 
351
      try:
 
352
        #path = os.path.join('./', directory)
 
353
        #files = [file for file in os.listdir(path) if file.lower().endswith(".html")]
 
354
        files = [file for file in lib_zip_file.namelist() if file.lower().endswith(".html")]
 
355
        file_count = len(files)
 
356
        file_nr = 0
 
357
        
 
358
        log_message("Caching manual %s, processing %d file(s) ..." % (description, file_count))
 
359
        
 
360
        # Generate synchronization objects
 
361
        filename_queue = Queue.Queue() 
 
362
        manual_page_data_queue = Queue.Queue() 
 
363
        
 
364
        # Fill filename queue
 
365
        for f in files: #[:1]:
 
366
          filename_queue.put(f)
 
367
        
 
368
        time_start = datetime.datetime.now()
 
369
        
 
370
        # Start threads
 
371
        Pool = []
 
372
        for i in range(1):
 
373
          thread = ScanManualPageThread()
 
374
          thread.filename_queue = filename_queue
 
375
          thread.manual_page_data_queue = manual_page_data_queue
 
376
          #thread.path = path
 
377
          thread.lib_zip_file = lib_zip_file
 
378
          
 
379
          Pool.append(thread)
 
380
          thread.start()
 
381
  
 
382
        # Wait for threads to complete
 
383
        while Pool:
 
384
          # Process all objects in queue
 
385
          file_nr += process_manual_page_data_queue(db_conn, lib_zip_file, directory, id_manual, file_nr, file_count, manual_page_data_queue)
 
386
          
 
387
          # Check if there are still threads that are alive
 
388
          for index, the_thread in enumerate(Pool):
 
389
            if the_thread.isAlive():
 
390
              continue
 
391
            else:
 
392
              del Pool[index]
 
393
            break
 
394
          
 
395
        # Process all objects still left in queue after the threads have all been closed
 
396
        file_nr += process_manual_page_data_queue(db_conn, lib_zip_file, directory, id_manual, file_nr, file_count, manual_page_data_queue)
 
397
        
 
398
        # Get database cursor
 
399
        c = db_conn.cursor()
 
400
        try:
 
401
          # Update manual to be installed
 
402
          generation_date = datetime.datetime.now().strftime("%Y-%m-%d")
 
403
          c.execute("UPDATE manual SET installed=1, generation_date=? WHERE id_manual=?", (generation_date, id_manual))
 
404
    
 
405
        except Exception, e:
 
406
          log_error("An error occurred while updating the manual entry. %r" % e)
 
407
        finally:
 
408
          c.close()
 
409
  
 
410
        db_conn.commit()
 
411
      
 
412
        time_duration = datetime.datetime.now() - time_start
 
413
      
 
414
        log_message("%d file(s) of %d processed. Duration %d.%d seconds." % (file_nr, file_count, 
 
415
          time_duration.seconds, time_duration.microseconds))
 
416
          
 
417
        
 
418
        # Add the images as web_objects  
 
419
        files = [file for file in lib_zip_file.namelist() if file.lower().endswith(".png")]
 
420
 
 
421
        log_message("Processing %d image file(s) ..." % len(files))
 
422
        
 
423
        for filename in files:
 
424
          try:
 
425
            # Get database cursor
 
426
            c = db_conn.cursor()
 
427
            
 
428
            try:
 
429
              image_file = lib_zip_file.open(filename)
 
430
              image_file_string = image_file.read()
 
431
              if not image_file_string:
 
432
                continue
 
433
            
 
434
              # Insert HTML page
 
435
              c.execute("INSERT OR REPLACE INTO web_object(path, content_type, content, allow_embedded_code_execution) VALUES(?, ?, ?, ?)",
 
436
                ["/" + directory + "/images/" + os.path.basename(filename), "image/png", sqlite3.Binary(image_file_string), 0])
 
437
                
 
438
            except Exception, e:
 
439
              log_error("An error occurred while inserting the image file %s. %r" % (filename, e))
 
440
          except Exception, e:
 
441
              log_error("An error occurred aquiring a database cursor. %r" % e)
 
442
        
 
443
        db_conn.commit()
 
444
      except Exception, e:
 
445
        log_error("An error occurred while executing the SQL commands. %r" % e)
 
446
      finally:
 
447
        lib_zip_file.close()
 
448
        
 
449
  except Exception, e:
 
450
      log_error("An error occurred aquiring a database cursor. %r" % e)
 
451
 
 
452
 
 
453
def rebuild_lib(verbose, doclib_db_name, manual_ids):
 
454
  try:
 
455
    # Check which database name to use
 
456
    if not doclib_db_name or doclib_db_name == "":
 
457
      doclib_db_name = get_lib_db_path()
 
458
      
 
459
    # Connect to the database
 
460
    db_conn = sqlite3.connect(doclib_db_name)
 
461
    
 
462
    try:
 
463
      log_message("Creating the documentation library structure...")
 
464
      
 
465
      if execute_sql_script(db_conn, './mysqldoclib.sql'):
 
466
        log_message("Documentation library created successfully.")
 
467
        
 
468
        cache_pages(db_conn, manual_ids)
 
469
        
 
470
        log_message( "Documentation library cached has been filled.")
 
471
      else:
 
472
        log_message("The documentation library structure has not been created.")
 
473
 
 
474
    finally:
 
475
      db_conn.close();
 
476
  except Exception, e:
 
477
      log_error("An error occurred while opening the database connection. %r" % e)
 
478
 
 
479
 
 
480
def rebuild_webui(verbose, webui_db_name):
 
481
  try:
 
482
    # Check which database name to use
 
483
    if not webui_db_name or webui_db_name == "":
 
484
      webui_db_name = get_lib_db_path()
 
485
      
 
486
    # Connect to the database
 
487
    db_conn = sqlite3.connect(webui_db_name)
 
488
    try:
 
489
      log_message("Creating the documentation library webui structure...")
 
490
      
 
491
      execute_sql_script(db_conn, './mysqldoclib_webui.sql')
 
492
      
 
493
      c = db_conn.cursor()
 
494
      try:
 
495
        for path, dirs, files in os.walk("webui"):
 
496
          for name in files:
 
497
            if name.endswith(".html") or name.endswith(".wbp") or name.endswith(".css"):
 
498
              # Open HTML file as a utf-8 file
 
499
              html_string = read_file_content(os.path.join(path, name))
 
500
              if not html_string:
 
501
                continue
 
502
                
 
503
              log_message("Path: %s, File: %s" % (path, name) )
 
504
              
 
505
              c.execute("INSERT OR REPLACE INTO web_object(path, content_type, content, allow_embedded_code_execution) VALUES(?, ?, ?, ?)",
 
506
                ["/" + path + "/" + os.path.basename(name), "text/html", html_string, 1])
 
507
            else:
 
508
              content_type = ""
 
509
              if name.endswith(".png"):
 
510
                content_type = "image/png"
 
511
              elif name.endswith(".gif"):
 
512
                content_type = "image/gif"
 
513
              elif name.endswith(".ico"):
 
514
                content_type = "image/vnd.microsoft.icon"
 
515
                
 
516
              img_file = open(os.path.join(path, name), "rb")
 
517
 
 
518
              log_message("Path: %s, File: %s" % (path, name) )
 
519
              
 
520
              c.execute("INSERT OR REPLACE INTO web_object(path, content_type, content, allow_embedded_code_execution) VALUES(?, ?, ?, ?)",
 
521
                ["/" + path + "/" + os.path.basename(name), content_type, sqlite3.Binary(img_file.read()), 0])
 
522
      finally:
 
523
        c.close()
 
524
  
 
525
      db_conn.commit()
 
526
    finally:
 
527
      db_conn.close();
 
528
  except Exception, e:
 
529
      log_error("An error occurred while opening the database connection. %r" % e)
 
530
 
 
531
# --------------------------------
 
532
# HTTP server functions
 
533
 
 
534
def open_lib_db():
 
535
  # Connect to the database
 
536
  db_conn = sqlite3.connect(get_lib_db_path())
 
537
  # Attach webui and usr database
 
538
  try:
 
539
    c = db_conn.cursor()
 
540
    try:
 
541
      c.execute("ATTACH DATABASE ? AS webui", (get_webui_db_path(),))
 
542
      c.execute("ATTACH DATABASE ? AS usr", (get_user_db_path(),))
 
543
    finally:
 
544
      c.close()
 
545
  except Exception, e:
 
546
    log_error("Could not attach webui or usr database. %r" % e)
 
547
 
 
548
  return db_conn
 
549
 
 
550
def build_search_result_page(search_string, manual_type):
 
551
  # Escape search string
 
552
  html_escape_table = {
 
553
    "&": "&amp;",
 
554
    '"': "&quot;",
 
555
    "'": "&apos;",
 
556
    ">": "&gt;",
 
557
    "<": "&lt;",
 
558
  }
 
559
  
 
560
  search_string_html = "".join(html_escape_table.get(c,c) for c in search_string)
 
561
  search_string = search_string.replace(";", "\\;").replace("'", "\\'")
 
562
 
 
563
  html = '''<html>
 
564
<head>
 
565
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 
566
  <link rel="stylesheet" type="text/css" href="stylesheets/ui.css" />
 
567
  <title>MySQL Workbench Documentation Library Search Result</title>
 
568
</head>
 
569
<body class="search">'''
 
570
 
 
571
  try:
 
572
    # Connect to the database
 
573
    db_conn = open_lib_db()
 
574
    try:
 
575
      # Get database cursor
 
576
      c = db_conn.cursor()
 
577
 
 
578
      # Log search
 
579
      c.execute("SELECT hits FROM usr.lib_search WHERE search_text = ?", (search_string,))
 
580
      rows = c.fetchall()
 
581
      if rows:
 
582
        c.execute("UPDATE usr.lib_search SET hits = hits + 1 WHERE search_text = ?", (search_string,))
 
583
      else:
 
584
        c.execute("INSERT INTO usr.lib_search (search_text, hits) VALUES(?, 1)", (search_string,))
 
585
        
 
586
      db_conn.commit()
 
587
 
 
588
      # Do the search
 
589
      sql_select = """
 
590
        SELECT p.chapter, p.title, wpo.path, pr.rating, m.directory,
 
591
            offsets(page_content) as fs_offsets, snippet(page_content) as snippet
 
592
        FROM page_content pc
 
593
          JOIN page p ON p.id_page = pc.id_page
 
594
          JOIN manual m ON m.id_manual = p.id_manual
 
595
          JOIN web_object wpo ON p.id_web_object = wpo.id_web_object
 
596
          LEFT OUTER JOIN page_rating pr ON p.title = pr.title
 
597
          LEFT OUTER JOIN usr.page_rating pru ON p.title = pru.title
 
598
        WHERE page_content MATCH '""" + search_string + "'"
 
599
      if int(manual_type) > 0:
 
600
        sql_select += " AND m.id_manual = " + str(manual_type) + " "
 
601
 
 
602
      sql_select += """
 
603
        ORDER BY pru.rating DESC, substr(fs_offsets, 1, 1), pr.rating DESC
 
604
        LIMIT 0, 51"""
 
605
      
 
606
      try:
 
607
        # Get search result
 
608
        c.execute(sql_select)
 
609
        
 
610
        rows = c.fetchall()
 
611
        
 
612
        html += "<h2>MySQL Document Manual Search</h2><hr/><p class='search_header'>"
 
613
        html += "Search Result for <b>`%s`</b> returned " % search_string_html
 
614
        
 
615
        if len(rows) > 50:
 
616
          html += "more than <b>50</b> matches.<br>Only the first 50 matches are displayed."
 
617
        else:
 
618
          html += "<b>%d</b> matches." % len(rows)
 
619
          
 
620
        html += "</p><br>"
 
621
        
 
622
        for chapter, title, path, rating, directory, offsets, snippet in rows:
 
623
          html += "<p class='search'><a href='" + path + "'>"
 
624
          html += chapter + " <b>" + title + "</b></a></p>"
 
625
          html += "<p class='search_snippet'>" + snippet + "</p><br>"
 
626
          
 
627
      except Exception, e:
 
628
        log_error("An error occurred while executing the SQL command. %r" % e)
 
629
        html += "<br>An error occurred while executing the SQL command.<br>%r" % str(e)
 
630
      finally:
 
631
        c.close()
 
632
  
 
633
    finally:
 
634
      db_conn.close();
 
635
  except Exception, e:
 
636
      log_error("An error occurred while opening the database connection. %r" % e)
 
637
      
 
638
  html += '''</body>
 
639
</html>
 
640
'''
 
641
  return html
 
642
 
 
643
class DocsLibHandler(BaseHTTPRequestHandler):
 
644
   
 
645
  def do_GET(self):
 
646
    try:
 
647
      url_full = self.path
 
648
      
 
649
      if url_full == "/":
 
650
        self.send_response(301)
 
651
        self.send_header("Location", "/webui/index.wbp")
 
652
        self.end_headers()
 
653
        return
 
654
      
 
655
      url_parsed = urlparse(url_full)
 
656
      url = url_parsed.path
 
657
      
 
658
      content_type = ""
 
659
      
 
660
      if url.endswith(".html"):
 
661
        content_type = "text/html"
 
662
      elif url.endswith(".css"):
 
663
        content_type = "text/css"
 
664
      elif url.endswith(".png"):
 
665
        content_type = "image/png"
 
666
      elif url.endswith(".gif"):
 
667
        content_type = "image/gif"
 
668
      elif url.endswith(".ico"):
 
669
        content_type = "image/vnd.microsoft.icon"
 
670
      elif url.endswith(".wbp"):
 
671
        content_type = "text/html"
 
672
      
 
673
      if len(content_type) > 0:
 
674
        if url.endswith("search.wbp"):
 
675
          log_message("Query: %s" % url_parsed.query)
 
676
          
 
677
          d = dict([(k,v) for k,junk,v in [line.partition("=") for line in url_parsed.query.split("&")]])
 
678
        
 
679
          search_string = d["search"].replace("+", " ").replace("%22", "\"")
 
680
          manual_type = d["manual_type"].strip()
 
681
  
 
682
          log_message("Search started, search_string: %s, manual_type: %d" % (search_string, int(manual_type)))
 
683
          
 
684
          self.send_response(200)
 
685
          self.send_header("Content-type", content_type)
 
686
          self.end_headers()
 
687
          self.wfile.write(build_search_result_page(search_string, int(manual_type)).encode("utf-8"))
 
688
          
 
689
        else:
 
690
          # check for web object in database
 
691
          # Get database cursor
 
692
          try:
 
693
            c = self.server.db_conn.cursor()
 
694
            
 
695
            try:
 
696
              database = ""
 
697
              if url.startswith("/webui"):
 
698
                database = "webui."
 
699
              
 
700
              # Get search result
 
701
              c.execute("""
 
702
                SELECT content_type, content, allow_embedded_code_execution 
 
703
                FROM """ + database + """web_object
 
704
                WHERE path = ?""", [url])
 
705
              
 
706
              rows = c.fetchall()
 
707
              
 
708
              if rows:
 
709
                for wo_content_type, wo_content, wo_allow_embedded_code_execution in rows:
 
710
                  self.send_response(200)
 
711
                  self.send_header("Content-type", wo_content_type)
 
712
                  self.end_headers()
 
713
                  if wo_content_type.startswith("text/"):
 
714
                    self.wfile.write(wo_content.encode("utf-8"))
 
715
                  else:
 
716
                    self.wfile.write(wo_content)
 
717
                  
 
718
                  # Count hits
 
719
                  c.execute("SELECT hits FROM usr.page_view WHERE path = ?", (url,))
 
720
                  rows = c.fetchall()
 
721
                  if rows:
 
722
                    c.execute("UPDATE usr.page_view SET hits = hits + 1 WHERE path = ?", (url,))
 
723
                  else:
 
724
                    c.execute("INSERT INTO usr.page_view (path, hits) VALUES(?, 1)", (url,))
 
725
                    
 
726
                  self.server.db_conn.commit()
 
727
              else:
 
728
                try:
 
729
                  f = open(curdir + sep + url)
 
730
                  self.send_response(200)
 
731
                  self.send_header("Content-type", content_type)
 
732
                  self.end_headers()
 
733
                  self.wfile.write(f.read())
 
734
                  f.close()
 
735
                except Exception, e:
 
736
                  self.send_error(404, "File Not Found: %s" % url)
 
737
                  log_error("File not found. %r" % e)
 
738
                  
 
739
            except Exception, e:
 
740
              self.send_error(404, "An error occurred. %r" % e)
 
741
              log_error("An error occurred while executing the SQL command. %r" % e)
 
742
 
 
743
            finally:
 
744
              c.close()
 
745
          except Exception, e:
 
746
            log_error("An error occurred while opening the database connection. %r" % e)
 
747
          
 
748
 
 
749
 
 
750
    except IOError:
 
751
      self.send_error(404, "File Not Found: %s" % url)
 
752
  
 
753
  def do_POST(self):
 
754
    try:
 
755
      ctype, pdict = cgi.parse_header(self.headers.getheader("content-type"))
 
756
      if ctype == "text/plain":
 
757
        log_message("Header Items: %s" % self.headers.items())
 
758
      
 
759
        # Get submitted values
 
760
        values = ""
 
761
        if self.headers.has_key('content-length'):
 
762
          length = int( self.headers['content-length'] )
 
763
          values = self.rfile.read(length)
 
764
        
 
765
        # AppleWebKit uses & as separators between values
 
766
        if self.headers.has_key('user-agent'):
 
767
          if "AppleWebKit" in self.headers['user-agent']:
 
768
            values = values.replace("&", "\n")
 
769
 
 
770
        d = dict([(k,v) for k,junk,v in [line.partition("=") for line in values.split("\n")]])
 
771
        
 
772
        search_string = d["search"].strip()
 
773
        manual_type = d["manual_type"].strip()
 
774
 
 
775
        log_message("Search started, search_string: %s, manual_type: %d" % (search_string, int(manual_type)))
 
776
        
 
777
        self.send_response(301)
 
778
        
 
779
        self.end_headers()
 
780
        
 
781
        self.wfile.write(build_search_result_page(search_string, int(manual_type)).encode("utf-8"))
 
782
 
 
783
      else: 
 
784
        self.send_error(404, "Wrong content-type" % ctype)
 
785
        self.end_headers()
 
786
        self.wfile.write("<HTML>Wrong content-type<BR><BR>")
 
787
      
 
788
    except Exception, e:
 
789
      verbose_print(self.server.verbose, "An Exception was raised while processing the POST handler. %r" % e)
 
790
 
 
791
  def log_message(self, message, *args):
 
792
    verbose_print(self.server.verbose, message % args)
 
793
  
 
794
  def log_request(self, code='-', size='-'):
 
795
    verbose_print(self.server.verbose, '"%s" %s %s' % (self.requestline, str(code), str(size)))
 
796
 
 
797
  def log_error(self, message, *args):
 
798
    verbose_print(self.server.verbose, message % args)
 
799
 
 
800
def serve_docs(port = 8080, verbose = 1, datadir= "./", ready_event=None, bind=''):
 
801
  global global_app_data_dir
 
802
  global_app_data_dir = datadir
 
803
  try:
 
804
    try:
 
805
      # Connect to the database
 
806
      db_conn = open_lib_db()
 
807
      try:
 
808
        server = HTTPServer((bind, port), DocsLibHandler)
 
809
        server.verbose = verbose
 
810
        server.db_conn = db_conn
 
811
        
 
812
        verbose_print(verbose, "Started HTTP server on port %d." % port)
 
813
        if ready_event:
 
814
            ready_event.set()
 
815
        server.serve_forever()
 
816
      finally:
 
817
        db_conn.close();
 
818
    except Exception, e:
 
819
        log_error("An error occurred while opening the database connection. %r" % e)
 
820
        raise e
 
821
  except KeyboardInterrupt:
 
822
    verbose_print(verbose, "Keyboard interrupt received, shutting down HTTP server.")
 
823
    server.socket.close()
 
824
 
 
825
 
 
826
# --------------------------------
 
827
# Main application
 
828
 
 
829
def usage():
 
830
  print """MySQL Document Library Standalone Application - mysqldoclib.py
 
831
Usage: wbdocs.py -h -pPort -v [build-lib | rebuild-lib | build-webui | rebuild-webui | serve-docs]
 
832
 
 
833
This applications serves and maintains a documentation library for MySQL products.
 
834
 
 
835
build-lib | rebuild-lib
 
836
  These commands create the documentation library repository.
 
837
 
 
838
build-webui | rebuild-webui
 
839
  These commands rebuild the webui repository that is used to server the web pages.
 
840
 
 
841
serve-docs
 
842
  This argument launches a web server to server the documentation library"""
 
843
 
 
844
def main(argv):
 
845
  try:
 
846
    opts, args = getopt.getopt(sys.argv[1:], "hp:d:v", ["help", "port=", "db="])
 
847
  except getopt.GetoptError, e:
 
848
    print "Invalid option passed to module. ", str(e)
 
849
    usage()
 
850
    sys.exit(2)
 
851
    
 
852
  verbose = False
 
853
  port = 8080
 
854
  db_name = ""
 
855
  timestamp = ""
 
856
  
 
857
  for o, a in opts:
 
858
    if o == "-v":
 
859
        verbose = True
 
860
    elif o in ("-h", "--help"):
 
861
        usage()
 
862
        sys.exit()
 
863
    elif o in ("-p", "--port"):
 
864
      if a.isdigit():
 
865
        port = int(a)
 
866
      else:
 
867
        assert False, "The specified port must be a number."
 
868
    elif o in ("-d", "--db"):
 
869
      db_name = a
 
870
    else:
 
871
        assert False, "Unhandled option."
 
872
        
 
873
  if args:
 
874
    if args[0] in ("build-lib", "rebuild-lib"):
 
875
      rebuild_lib(verbose, db_name, args[1:])
 
876
    if args[0] in ("build-webui", "rebuild-webui"):
 
877
      rebuild_webui(verbose, db_name)
 
878
    elif args[0] == "serve-docs":
 
879
      serve_docs(port, verbose)
 
880
  else:
 
881
    usage()
 
882
 
 
883
if __name__ == "__main__":
 
884
  main(sys.argv[1:])
 
885
  
 
886
 
 
887
  
 
 
b'\\ No newline at end of file'