summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2012-02-25 12:24:43 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2012-02-25 12:24:43 (GMT)
commit9fa9c2b98ae5c7c7c3e93e07e60c11d2dfed087d (patch) (side-by-side diff)
tree2b6f56484767840547a4b47aa6ec4e165d19d1f9
downloadweb-9fa9c2b98ae5c7c7c3e93e07e60c11d2dfed087d.tar.gz
web-9fa9c2b98ae5c7c7c3e93e07e60c11d2dfed087d.tar.bz2
Initial commit
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.htaccess6
-rw-r--r--INSTALL79
-rw-r--r--__init__.py0
-rw-r--r--dicoclient/__init__.py19
-rw-r--r--dicoclient/dicoclient.py415
-rw-r--r--dicoclient/dicoshell.py321
-rw-r--r--dicoclient/setup.py39
-rw-r--r--dicoweb.wsgi27
-rw-r--r--dummy_translations.py40
-rw-r--r--gcide.wsgi25
-rw-r--r--index.html15
-rw-r--r--manage.py23
-rw-r--r--settings-sample.py110
-rw-r--r--settings.py109
-rw-r--r--static/18px-Bulbgraph.pngbin0 -> 1210 bytes
-rw-r--r--static/dicoweb.css134
-rw-r--r--static/dicoweb.js80
-rw-r--r--static/gcide.css206
-rw-r--r--static/gnu-head-sm.jpgbin0 -> 5286 bytes
-rw-r--r--templates/404.html5
-rw-r--r--templates/500.html5
-rw-r--r--templates/about.html53
-rw-r--r--templates/base.html71
-rw-r--r--templates/download.html118
-rw-r--r--templates/index.html154
-rw-r--r--templates/license.html21
-rw-r--r--templates/opensearch.xml8
-rw-r--r--templatetags/__init__.py0
-rw-r--r--templatetags/dictlookup.py27
-rw-r--r--templatetags/macros.py160
-rw-r--r--templatetags/withsetting.py67
-rw-r--r--urls.py28
-rw-r--r--views.py253
33 files changed, 2618 insertions, 0 deletions
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..2df5844
--- a/dev/null
+++ b/.htaccess
@@ -0,0 +1,6 @@
+Options All -Indexes
+<Files ~ "^\.emacs">
+ Order allow,deny
+ Deny from all
+ Satisfy All
+</Files>
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..ee9a98b
--- a/dev/null
+++ b/INSTALL
@@ -0,0 +1,79 @@
+GNU Dico - Dicoweb INSTALL
+Copyright (C) 2008-2010, 2012 Wojciech Polak
+
+* Dicoweb requirements
+======================
+
+- Django 1.0+ -- a Python Web framework (http://www.djangoproject.com/)
+- Wit -- a wiki translator distributed within GNU Dico.
+ (http://puszcza.gnu.org.ua/projects/wit/)
+
+* Installation instructions
+===========================
+
+Rename 'settings-sample.py' to 'settings.py' and edit your
+local Dicoweb site configuration.
+
+
+** The development/test server
+------------------------------
+
+Change the current working directory into the `dicoweb' directory
+and run the command `python manage.py runserver'. You will see
+the following output:
+
+ Validating models...
+ 0 errors found.
+
+ Django version 1.0, using settings 'dicoweb.settings'
+ Development server is running at http://127.0.0.1:8000/
+ Quit the server with CONTROL-C.
+
+** Production server with mod_wsgi
+----------------------------------
+
+Apache configuration:
+
+ LoadModule wsgi_module modules/mod_wsgi.so
+ WSGIScriptAlias / /usr/local/django/dicoweb/dicoweb.wsgi
+ Alias /static "/usr/local/django/dicoweb/static"
+
+More detailed information is available at:
+http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango
+
+** Production server with mod_python
+------------------------------------
+
+Apache configuration:
+
+ LoadModule python_module modules/mod_python.so
+ <Location "/">
+ SetHandler python-program
+ PythonHandler django.core.handlers.modpython
+ PythonPath "sys.path + ['/usr/local/django', '/usr/local/django/dicoweb']"
+ SetEnv DJANGO_SETTINGS_MODULE dicoweb.settings
+ PythonInterpreter dicoweb
+ PythonDebug Off
+ </Location>
+
+ <Location "/static">
+ SetHandler None
+ </Location>
+ <Location "/favicon.ico">
+ SetHandler None
+ </Location>
+
+ <Directory "/usr/local/django/dicoweb/">
+ AllowOverride All
+ Options None
+ Order allow,deny
+ Allow from all
+ </Directory>
+
+
+
+Local Variables:
+mode: outline
+paragraph-separate: "[ ]*$"
+version-control: never
+End:
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/__init__.py
diff --git a/dicoclient/__init__.py b/dicoclient/__init__.py
new file mode 100644
index 0000000..a300cea
--- a/dev/null
+++ b/dicoclient/__init__.py
@@ -0,0 +1,19 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+__all__ = ["dicoclient"]
+
+from dicoclient import *
diff --git a/dicoclient/dicoclient.py b/dicoclient/dicoclient.py
new file mode 100644
index 0000000..76336c2
--- a/dev/null
+++ b/dicoclient/dicoclient.py
@@ -0,0 +1,415 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2010, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import socket
+import base64
+import quopri
+import hashlib
+
+__version__ = '1.0'
+
+class DicoClient:
+ """GNU Dico client module written in Python
+ (a part of GNU Dico software)"""
+
+ host = None;
+ levenshtein_distance = 0
+ mime = False
+
+ verbose = 0
+ timeout = 10
+ transcript = False
+ __connected = False
+
+ def __init__ (self, host=None):
+ if host != None:
+ self.host = host;
+
+ def __del__ (self):
+ if self.__connected:
+ self.socket.close ()
+
+ def open (self, host=None, port=2628, cred=None):
+ """Open the connection to the DICT server."""
+ if host != None:
+ self.host = host
+ if self.verbose:
+ self.__debug ('Connecting to %s:%d' % (self.host, port))
+ socket.setdefaulttimeout (int (self.timeout))
+ self.socket = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
+ self.socket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.socket.connect ((self.host, port))
+ self.__connected = True
+ self.fd = self.socket.makefile ();
+
+ self.server_banner = self.__read ()[0]
+ capas, msgid = re.search ('<(.*)> (<.*>)$',
+ self.server_banner).groups ()
+ self.server_capas = capas.split ('.')
+ self.server_msgid = msgid
+
+ self.__send_client ()
+ self.__read ()
+ if cred and 'user' in cred and 'password' in cred:
+ self.auth(cred['user'], cred['password'])
+
+ def auth (self, user, passwd):
+ if self.__connected and 'auth' in self.server_capas:
+ if self.verbose:
+ self.__debug ('Authenticating on %s' % (self.host))
+ m = hashlib.md5()
+ m.update(self.server_msgid)
+ m.update(passwd)
+ self.__send ('AUTH %s %s' % (user, m.hexdigest()))
+ res = self.__read ()
+ code, msg = res[0].split (' ', 1)
+ if int (code) == 230:
+ return True
+ return False
+
+ def close (self):
+ """Close the connection."""
+ if self.__connected:
+ self.__send_quit ()
+ self.__read ()
+ self.socket.close ()
+ self.__connected = False
+
+ def option (self, name, *args):
+ """Send the OPTION command."""
+ if self.__connected:
+ self.__send ('OPTION %s%s' %
+ (name, reduce (lambda x, y: str (x) +' '+ str (y),
+ args, '')))
+ res = self.__read ()
+ code, msg = res[0].split (' ', 1)
+ if int (code) == 250:
+ if name.lower () == 'mime':
+ self.mime = True
+ return True
+ return False
+
+ def __get_mime (self, lines):
+ cnt = 0
+ mimeinfo = {}
+ firstline = lines[0].lower ()
+ if firstline.find ('content-type:') != -1 or \
+ firstline.find ('content-transfer-encoding:') != -1:
+ cnt += 1
+ for line in lines:
+ if line == '':
+ break
+ t = line.split (':', 1)
+ mimeinfo[t[0].lower ()] = t[1].strip ()
+ cnt += 1
+ for i in range (0, cnt):
+ lines.pop (0)
+ else:
+ lines.pop (0)
+ if 'content-transfer-encoding' in mimeinfo:
+ if mimeinfo['content-transfer-encoding'].lower () == 'base64':
+ buf = base64.decodestring ('\n'.join (lines))
+ lines[:] = (buf.split ('\r\n'))
+ if lines[-1] == '': del lines[-1]
+ del mimeinfo['content-transfer-encoding']
+ elif mimeinfo['content-transfer-encoding'].lower () == 'quoted-printable':
+ buf = quopri.decodestring ('\n'.join (lines))
+ lines[:] = buf.split ('\r\n')
+ if lines[-1] == '': del lines[-1]
+ del mimeinfo['content-transfer-encoding']
+ return mimeinfo
+
+ def __get_rs (self, line):
+ code, text = line.split (' ', 1)
+ code = int (code)
+ return code, text
+
+ def __read (self):
+ if not self.__connected:
+ raise DicoNotConnectedError ('Not connected')
+ buf = []
+ line = self.__readline ()
+ if len (line) == 0:
+ raise DicoNotConnectedError ('Not connected')
+ buf.append (line)
+ code, text = self.__get_rs (line)
+
+ if code >= 100 and code < 200:
+ if code == 150:
+ while True:
+ rs = self.__readline ()
+ code, text = self.__get_rs (rs)
+ if code != 151:
+ buf.append (rs)
+ break
+ buf.append ([rs, self.__readblock ()])
+ else:
+ buf.append (self.__readblock ())
+ buf.append (self.__readline ())
+ return buf
+
+ def __readline (self):
+ line = self.fd.readline ().rstrip ()
+ if self.transcript:
+ self.__debug ('S:%s' % line)
+ return line
+
+ def __readblock (self):
+ buf = []
+ while True:
+ line = self.__readline ()
+ if line == '.':
+ break
+ buf.append (line)
+ return buf
+
+
+ def __send (self, command):
+ if not self.__connected:
+ raise DicoNotConnectedError ('Not connected')
+ self.socket.send (command.encode ('utf_8') + "\r\n")
+ if self.transcript:
+ self.__debug ('C:%s' % command)
+
+ def __send_client (self):
+ if self.verbose:
+ self.__debug ('Sending client information')
+ self.__send ('CLIENT "%s %s"' % ("GNU Dico (Python Edition)",
+ __version__))
+
+ def __send_quit (self):
+ if self.verbose:
+ self.__debug ('Quitting')
+ self.__send ('QUIT');
+
+ def __send_show (self, what, arg=None):
+ if arg != None:
+ self.__send ('SHOW %s "%s"' % (what, arg))
+ else:
+ self.__send ('SHOW %s' % what)
+ return self.__read ()
+
+ def __send_define (self, database, word):
+ if self.verbose:
+ self.__debug ('Sending query for word "%s" in database "%s"' %
+ (word, database))
+ self.__send ('DEFINE "%s" "%s"' % (database, word))
+ return self.__read ()
+
+ def __send_match (self, database, strategy, word):
+ if self.verbose:
+ self.__debug ('Sending query to match word "%s" in database "%s", using "%s"'
+ % (word, database, strategy))
+ self.__send ('MATCH "%s" "%s" "%s"' % (database, strategy, word))
+ return self.__read ()
+
+ def __send_xlev (self, distance):
+ self.__send ('XLEV %u' % distance)
+ return self.__read ()
+
+ def show_databases (self):
+ """List all accessible databases."""
+ if self.verbose:
+ self.__debug ('Getting list of databases')
+ res = self.__send_show ('DATABASES')
+ if self.mime:
+ mimeinfo = self.__get_mime (res[1])
+ dbs_res = res[1:-1][0]
+ dbs = []
+ for d in dbs_res:
+ short_name, full_name = d.split (' ', 1)
+ dbs.append ([short_name, self.__unquote (full_name)])
+ dct = {
+ 'count': len (dbs),
+ 'databases': dbs,
+ }
+ return dct
+
+ def show_strategies (self):
+ """List available matching strategies."""
+ if self.verbose:
+ self.__debug ('Getting list of strategies')
+ res = self.__send_show ('STRATEGIES')
+ if self.mime:
+ mimeinfo = self.__get_mime (res[1])
+ sts_res = res[1:-1][0]
+ sts = []
+ for s in sts_res:
+ short_name, full_name = s.split (' ', 1)
+ sts.append ([short_name, self.__unquote (full_name)])
+ dct = {
+ 'count': len (sts),
+ 'strategies': sts,
+ }
+ return dct
+
+ def show_info (self, database):
+ """Provide information about the database."""
+ res = self.__send_show ("INFO", database)
+ code, msg = res[0].split (' ', 1)
+ if int (code) < 500:
+ if self.mime:
+ mimeinfo = self.__get_mime (res[1])
+ dsc = res[1]
+ return {'desc': '\n'.join (dsc), 'type': 'info' }
+ else:
+ return {'error': code, 'msg': msg}
+
+ def show_lang_db (self):
+ """Show databases with their language preferences."""
+ res = self.__send_show ('LANG DB')
+ code, msg = res[0].split (' ', 1)
+ if int (code) < 500:
+ if self.mime:
+ mimeinfo = self.__get_mime (res[1])
+ dsc = res[1]
+ lang_src = {}
+ lang_dst = {}
+ for i in dsc:
+ pair = i.split (' ', 1)[1]
+ src, dst = pair.split (':', 1)
+ for j in src:
+ lang_src[src.strip()] = True
+ for j in dst:
+ lang_dst[dst.strip()] = True
+ return {
+ 'desc': '\n'.join (dsc),
+ 'type': 'lang',
+ 'lang_src': lang_src.keys (),
+ 'lang_dst': lang_dst.keys (),
+ }
+ else:
+ return {'error': code, 'msg': msg}
+
+ def show_lang_pref (self):
+ """Show server language preferences."""
+ res = self.__send_show ('LANG PREF')
+ code, msg = res[0].split (' ', 1)
+ if int (code) < 500:
+ return {'msg': msg}
+ else:
+ return {'error': code, 'msg': msg}
+
+ def show_server (self):
+ """Provide site-specific information."""
+ res = self.__send_show ('SERVER')
+ code, msg = res[0].split (' ', 1)
+ if int (code) < 500:
+ dsc = res[1]
+ return {'desc': '\n'.join (dsc), 'type': 'server'}
+ else:
+ return {'error': code, 'msg': msg}
+
+ def define (self, database, word):
+ """Look up word in database."""
+ database = database.replace ('"', "\\\"")
+ word = word.replace ('"', "\\\"")
+ res = self.__send_define (database, word)
+ code, msg = res[-1].split (' ', 1)
+ if int (code) < 500:
+ defs_res = res[1:-1]
+ defs = []
+ rx = re.compile ('^\d+ ("[^"]+"|\w+) ([a-zA-Z0-9_\-]+) ("[^"]*"|\w+)')
+ for i in defs_res:
+ term, db, db_fullname = rx.search (i[0]).groups ()
+ df = {
+ 'term': self.__unquote (term),
+ 'db': db,
+ 'db_fullname': self.__unquote (db_fullname),
+ }
+ if self.mime:
+ mimeinfo = self.__get_mime (i[1])
+ df.update (mimeinfo)
+ df['desc'] = '\n'.join (i[1])
+ defs.append (df)
+ dct = {
+ 'count': len (defs),
+ 'definitions': defs,
+ 'type': 'define'
+ }
+ return dct
+ else:
+ return {'error': code, 'msg': msg}
+
+ def match (self, database, strategy, word):
+ """Match word in database using strategy."""
+ if not self.__connected:
+ raise DicoNotConnectedError ('Not connected')
+
+ if self.levenshtein_distance and 'xlev' in self.server_capas:
+ res = self.__send_xlev (self.levenshtein_distance)
+ code, msg = res[-1].split (' ', 1)
+ if int (code) != 250 and self.verbose:
+ self.__debug ('Server rejected XLEV command')
+ self.__debug ('Server reply: %s' % msg)
+
+ database = database.replace ('"', "\\\"")
+ strategy = strategy.replace ('"', "\\\"")
+ word = word.replace ('"', "\\\"")
+
+ res = self.__send_match (database, strategy, word)
+ code, msg = res[-1].split (' ', 1)
+ if int (code) < 500:
+ if self.mime:
+ mimeinfo = self.__get_mime (res[1])
+ mts_refs = res[1:-1][0]
+ mts = {}
+ for i in mts_refs:
+ db, term = i.split (' ', 1)
+ if mts.has_key (db):
+ mts[db].append (self.__unquote (term))
+ else:
+ mts[db] = [self.__unquote (term)]
+ dct = {
+ 'matches': mts,
+ }
+ return dct
+ else:
+ return {'error': code, 'msg': msg}
+
+ def xlev (self, distance):
+ """Set Levenshtein distance."""
+ self.levenshtein_distance = distance
+ res = self.__send_xlev (distance)
+ code, msg = res[0].split (' ', 1)
+ if int (code) == 250:
+ return True
+ return False
+
+ def __unquote (self, s):
+ s = s.replace ("\\\\'", "'")
+ if s[0] == '"' and s[-1] == '"':
+ s = s[1:-1]
+ try:
+ s = self.__decode (s)
+ except UnicodeEncodeError:
+ pass
+ return s
+
+ def __decode (self, encoded):
+ for octc in (c for c in re.findall (r'\\(\d{3})', encoded)):
+ encoded = encoded.replace (r'\%s' % octc, chr (int (octc, 8)))
+ return unicode (encoded, 'utf_8')
+
+ def __debug (self, msg):
+ print 'dico: Debug: %s' % msg
+
+class DicoNotConnectedError (Exception):
+ def __init__ (self, value):
+ self.parameter = value
+ def __str__(self):
+ return repr (self.parameter)
diff --git a/dicoclient/dicoshell.py b/dicoclient/dicoshell.py
new file mode 100644
index 0000000..1c0e64d
--- a/dev/null
+++ b/dicoclient/dicoshell.py
@@ -0,0 +1,321 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2010, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import sys
+import atexit
+import getopt
+import readline
+import curses.ascii
+import socket
+import dicoclient
+
+class Shell:
+ """Simple GNU Dico-Python Shell."""
+
+ prompt = 'dico> '
+ prefix = '.'
+
+ default_host = 'gnu.org.ua'
+ database = '!'
+ strategy = '.'
+ last_matches = []
+ last_databases = []
+ last_strategies = []
+ transcript = False
+
+ def __init__ (self, opts, args):
+ for o, a in opts:
+ if o in ('-h', '--host'):
+ self.default_host = a
+
+ self.dc = dicoclient.DicoClient (self.default_host)
+
+ def run (self):
+ histfile = os.path.expanduser ('~/.dico_history')
+ try:
+ readline.read_history_file (histfile)
+ except IOError:
+ pass
+ atexit.register (readline.write_history_file, histfile)
+
+ print '\nType ? for help summary\n'
+ while True:
+ try:
+ input = raw_input (self.prompt).strip ()
+ input = unicode (input, 'utf_8')
+ except (EOFError, KeyboardInterrupt):
+ print
+ sys.exit ()
+
+ try:
+ self.parse (input)
+ except socket.timeout:
+ self.__error ('socket timed out')
+ except dicoclient.DicoNotConnectedError:
+ try:
+ self.dc.open ()
+ dict = self.dc.show_databases ()
+ self.last_databases = dict['databases']
+ dict = self.dc.show_strategies ()
+ self.last_strategies = dict['strategies']
+ self.parse (input)
+ except socket.error, (errno, strerror):
+ self.__error (strerror)
+
+ def parse (self, input):
+ if len (input) < 1:
+ return
+ if input[0] == self.prefix:
+ self.parse_command (input[1:])
+ elif input == '?':
+ self.print_help ()
+ elif re.match (r'^[0-9]+$', input):
+ try:
+ match = self.last_matches[int (input)]
+ dict = self.dc.define (match[0], match[1])
+ if 'count' in dict:
+ for d in dict['definitions']:
+ print 'From %s, %s:' % (d['db'], d['db_fullname'].
+ encode ('utf_8'))
+ print d['desc']
+ elif 'error' in dict:
+ print dict['msg']
+ except IndexError:
+ self.__error ('No previous match')
+ elif input[0] == '/':
+ if len (input) > 1:
+ dict = self.dc.match (self.database, self.strategy, input[1:])
+ if 'matches' in dict:
+ self.last_matches = []
+ lmi = 0
+ for db in dict['matches']:
+ print 'From %s, %s:' % (db, self.__lookup_db (db).
+ encode ('utf_8'))
+ for term in dict['matches'][db]:
+ print '%4d) "%s"' % (lmi, term.encode ('utf_8'))
+ self.last_matches.append ([db, term])
+ lmi = lmi + 1
+ elif 'error' in dict:
+ print dict['msg']
+ else:
+ if len (self.last_matches) > 0:
+ m = {}
+ lmi = 0
+ for i, db in enumerate (self.last_matches):
+ if not db[0] in m: m[db[0]] = []
+ m[db[0]].append (self.last_matches[i][1])
+ for db in m:
+ print 'From %s, %s:' % (db, self.__lookup_db (db))
+ for term in m[db]:
+ print '%4d) "%s"' % (lmi, term)
+ lmi = lmi + 1
+ else:
+ self.__error ('No previous match')
+ elif input[0] == '!':
+ if re.match (r'^![0-9]+$', input):
+ number = int (input[1:])
+ readline.insert_text (readline.get_history_item (number))
+ readline.redisplay ()
+ else:
+ dict = self.dc.define (self.database, input)
+ if 'count' in dict:
+ for d in dict['definitions']:
+ print 'From %s, %s:' % (d['db'], d['db_fullname'].
+ encode ('utf_8'))
+ print d['desc']
+ elif 'error' in dict:
+ print dict['msg']
+
+ def parse_command (self, input):
+ input = input.split (' ', 1)
+ cmd = input[0]
+ args = None
+ if len (input) == 2:
+ args = input[1]
+
+ if cmd == 'open':
+ try:
+ if args != None:
+ args = args.split (' ', 1)
+ if len (args) == 2:
+ self.dc.open (args[0], int (args[1]))
+ else:
+ self.dc.open (args[0])
+ else:
+ self.dc.open ()
+ dict = self.dc.show_databases ()
+ self.last_databases = dict['databases']
+ dict = self.dc.show_strategies ()
+ self.last_strategies = dict['strategies']
+ except socket.error, (errno, strerror):
+ self.__error (strerror)
+ elif cmd == 'close':
+ self.dc.close ()
+ elif cmd == 'database':
+ if args != None:
+ self.database = args
+ else:
+ print self.database
+ elif cmd == 'strategy':
+ if args != None:
+ self.strategy = args
+ else:
+ print self.strategy
+ elif cmd == 'distance':
+ if args != None:
+ self.dc.levenshtein_distance = int (args)
+ else:
+ if self.dc.levenshtein_distance:
+ print 'Configured Levenshtein distance: %u' % \
+ self.dc.levenshtein_distance
+ else:
+ print 'No distance configured'
+ elif cmd == 'ls':
+ dict = self.dc.show_strategies ()
+ self.last_strategies = dict['strategies']
+ if len (self.last_strategies):
+ for i in self.last_strategies:
+ print '%s "%s"' % (i[0], i[1])
+ elif cmd == 'ld':
+ dict = self.dc.show_databases ()
+ self.last_databases = dict['databases']
+ if len (self.last_databases):
+ for i in self.last_databases:
+ print '%s "%s"' % (i[0], i[1])
+ elif cmd == 'mime':
+ print self.dc.option ('MIME')
+ elif cmd == 'server':
+ dict = self.dc.show_server ()
+ if 'desc' in dict:
+ print dict['desc']
+ elif 'error' in dict:
+ self.__error (dict['error'] + ' ' + dict['msg'])
+ elif cmd == 'info':
+ if args != None:
+ dict = self.dc.show_info (args)
+ if 'desc' in dict:
+ print dict['desc']
+ elif 'error' in dict:
+ self.__error (dict['error'] + ' ' + dict['msg'])
+ elif cmd == 'history':
+ hl = int (readline.get_current_history_length ())
+ for i in xrange (0, hl):
+ print '%4d) %s' % (i, readline.get_history_item (i))
+ elif cmd == 'help':
+ self.print_help ()
+ elif cmd == 'transcript':
+ if args != None:
+ if args in ('yes', 'on', 'true'):
+ self.dc.transcript = True
+ elif args in ('no', 'off', 'false'):
+ self.dc.transcript = False
+ else:
+ self.__error ('Expected boolean value')
+ else:
+ if self.dc.transcript:
+ print 'transcript is on'
+ else:
+ print 'transcript is off'
+ elif cmd == 'verbose':
+ if args != None:
+ self.dc.verbose = args
+ else:
+ print self.dc.verbose
+ elif cmd == 'prompt':
+ if args != None:
+ self.prompt = args
+ else:
+ self.__error ('not enough arguments')
+ elif cmd == 'prefix':
+ if args != None:
+ if len (args) == 1 and args != '#' and \
+ curses.ascii.ispunct (args):
+ self.prefix = args
+ else:
+ self.__error ('Expected a single punctuation character')
+ elif cmd == 'version':
+ self.print_version ()
+ elif cmd == 'warranty':
+ self.print_warranty ()
+ elif cmd == 'quit':
+ sys.exit ()
+
+ def __lookup_db (self, db):
+ for d in self.last_databases:
+ if d[0] == db:
+ return d[1]
+ return ''
+
+ def __error (self, msg):
+ print 'dico: Error: %s' % msg
+
+ def print_version (self):
+ print 'GNU Dico (Python Edition) ' + dicoclient.__version__
+
+ def print_warranty (self):
+ self.print_version ()
+ print """Copyright (C) 2008, 2009, 2010 Wojciech Polak
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>."""
+
+ def print_help (self):
+ print 'WORD Define WORD.'
+ print '/WORD Match WORD.'
+ print '/ Redisplay previous matches.'
+ print 'NUMBER Define NUMBERth match.'
+ print '!NUMBER Edit NUMBERth previous command.'
+ print
+ print self.prefix + 'open [HOST [PORT]] Connect to a DICT server.'
+ print self.prefix + 'close Close the connection.'
+ print self.prefix + 'database [NAME] Set or display current database name.'
+ print self.prefix + 'strategy [NAME] Set or display current strategy.'
+ print self.prefix + 'distance [NUM] Set or query Levenshtein distance (server-dependent).'
+ print self.prefix + 'ls List available matching strategies'
+ print self.prefix + 'ld List all accessible databases'
+ print self.prefix + 'info [DB] Display the information about the database.'
+ print self.prefix + 'prefix [CHAR] Set or display command prefix.'
+ print self.prefix + 'transcript [BOOL] Set or display session transcript mode.'
+ print self.prefix + 'verbose [NUMBER] Set or display verbosity level.'
+ print self.prefix + 'prompt STRING Change command line prompt.'
+ print self.prefix + 'history Display command history.'
+ print self.prefix + 'help Display this help text.'
+ print self.prefix + 'version Print program version.'
+ print self.prefix + 'warranty Print copyright statement.'
+ print self.prefix + 'quit Quit the shell.'
+
+if __name__ == '__main__':
+ try:
+ opts, args = getopt.getopt (sys.argv[1:], 'h:', ['host='])
+ except getopt.GetoptError:
+ print '\nusage: %s [-h, --host=hostname]' % (sys.argv[0])
+ sys.exit (0)
+
+ shell = Shell (opts, args)
+ shell.print_version ()
+ shell.run ()
diff --git a/dicoclient/setup.py b/dicoclient/setup.py
new file mode 100644
index 0000000..265264a
--- a/dev/null
+++ b/dicoclient/setup.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+#
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+from distutils.core import setup
+import dicoclient
+
+setup(name='dicoclient',
+ version='1.0',
+ author='Wojciech Polak',
+ author_email='polak@gnu.org',
+ url='http://www.gnu.org/software/dico/',
+ py_modules=['dicoclient', 'dicoshell'],
+ license='GPL License',
+ description='A DICT protocol (RFC 2229) client library.',
+ platforms=['any'],
+ classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU General Public License (GPL)',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries :: Python Modules'
+ ]
+)
diff --git a/dicoweb.wsgi b/dicoweb.wsgi
new file mode 100644
index 0000000..deb8c43
--- a/dev/null
+++ b/dicoweb.wsgi
@@ -0,0 +1,27 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+SITE_ROOT = os.path.dirname (os.path.realpath (__file__))
+#if not 'DJANGO_SETTINGS_MODULE' in os.environ:
+# os.environ['DJANGO_SETTINGS_MODULE'] = 'dicoweb.settings'
+os.environ['DJANGO_SETTINGS_MODULE'] = 'gcide.settings'
+sys.path.insert (0, os.path.join (SITE_ROOT, '../'))
+
+from django.core.handlers.wsgi import WSGIHandler
+application = WSGIHandler ()
diff --git a/dummy_translations.py b/dummy_translations.py
new file mode 100644
index 0000000..7733bc8
--- a/dev/null
+++ b/dummy_translations.py
@@ -0,0 +1,40 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+from django.utils.translation import ugettext_noop
+
+DUMMY_TRANSLATIONS = (
+ # gnu.org.ua
+ ugettext_noop('Match words exactly'),
+ ugettext_noop('Match word prefixes'),
+ ugettext_noop('Match using SOUNDEX algorithm'),
+ ugettext_noop('Match everything (experimental)'),
+ ugettext_noop('Match headwords within given Levenshtein distance'),
+ ugettext_noop('Match headwords within given Levenshtein distance (normalized)'),
+ ugettext_noop('Match headwords within given Damerau-Levenshtein distance'),
+ ugettext_noop('Match headwords within given Damerau-Levenshtein distance (normalized)'),
+ ugettext_noop('POSIX 1003.2 (modern) regular expressions'),
+ ugettext_noop('Old (basic) regular expressions'),
+ ugettext_noop('Match word suffixes'),
+ ugettext_noop('Reverse search in Quechua databases'),
+ # dict.org
+ ugettext_noop('Match headwords exactly'),
+ ugettext_noop('Match prefixes'),
+ ugettext_noop('Match substring occurring anywhere in a headword'),
+ ugettext_noop('Match suffixes'),
+ ugettext_noop('Match headwords within Levenshtein distance one'),
+ ugettext_noop('Match separate words within headwords'),
+)
diff --git a/gcide.wsgi b/gcide.wsgi
new file mode 100644
index 0000000..28f5b14
--- a/dev/null
+++ b/gcide.wsgi
@@ -0,0 +1,25 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+
+SITE_ROOT = os.path.dirname (os.path.realpath (__file__))
+os.environ['DJANGO_SETTINGS_MODULE'] = 'gcide.settings'
+sys.path.insert (0, os.path.join (SITE_ROOT, '../'))
+
+from django.core.handlers.wsgi import WSGIHandler
+application = WSGIHandler ()
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5f6f390
--- a/dev/null
+++ b/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>GCIDE</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta http-equiv="refresh" content="0; url=http://gcide.gnu.org.ua">
+</head>
+<body>
+<p>
+You should be redirected to the actual <b>GCIDE</b> web page. If not,
+please <a href="http://gcide.gnu.org.ua">click here</a>.
+</p>
+</body>
+</html>
diff --git a/manage.py b/manage.py
new file mode 100644
index 0000000..8c0a156
--- a/dev/null
+++ b/manage.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+from django.core.management import execute_manager
+import settings
+
+if __name__ == '__main__':
+ execute_manager (settings)
diff --git a/settings-sample.py b/settings-sample.py
new file mode 100644
index 0000000..9abc5b8
--- a/dev/null
+++ b/settings-sample.py
@@ -0,0 +1,110 @@
+# Django settings for Dicoweb project.
+#
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2010, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+SITE_ROOT = os.path.dirname (os.path.realpath (__file__))
+
+DEBUG = True
+TEMPLATE_DEBUG = True
+
+ADMINS = (
+ ('Your Name', 'Your e-mail address'),
+)
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = ''
+DATABASE_NAME = ''
+DATABASE_USER = ''
+DATABASE_PASSWORD = ''
+DATABASE_HOST = ''
+DATABASE_PORT = ''
+
+SITE_ID = 1
+USE_I18N = True
+
+TIME_ZONE = 'Europe/Warsaw'
+LANGUAGE_CODE = 'en-us'
+LANGUAGE_COOKIE_NAME = 'dicoweb_lang'
+
+SESSION_COOKIE_NAME = 'dicoweb_sid'
+SESSION_ENGINE = 'django.contrib.sessions.backends.file'
+SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# Caching, see http://docs.djangoproject.com/en/dev/topics/cache/#topics-cache
+CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
+
+# Absolute path to the directory that holds media/static files.
+MEDIA_ROOT = os.path.join (SITE_ROOT, 'static')
+
+# URL that handles the media served from MEDIA_ROOT.
+MEDIA_URL = 'static'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'SET THIS TO A RANDOM STRING'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.load_template_source',
+ 'django.template.loaders.app_directories.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.cache.UpdateCacheMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.gzip.GZipMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.cache.FetchFromCacheMiddleware',
+)
+
+ROOT_URLCONF = 'dicoweb.urls'
+
+TEMPLATE_DIRS = (
+ os.path.join (SITE_ROOT, 'templates'),
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'dicoweb',
+)
+
+DICT_SERVERS = ('gnu.org.ua',)
+DICT_TIMEOUT = 10
+
+AUTH = {}
+# AUTH = { '127.0.0.1:12628' : { 'user' : 'gcide', 'password' : 'pass' } }
+
+DICT_TIMEOUT = 10
+
+GCIDE_VERSION = 0.51
+GCIDE_FTP = "ftp://download.gnu.org.ua/pub/gcide"
+GCIDE_DISTRIBUTIONS = ( {'ftp': GCIDE_FTP,
+ 'archive': 'gcide-0.51-rc1.tar.gz',
+ 'size': '18M',
+ 'hash': 'c5e7215fa834eb1675c33a7621dea6ae'},
+ {'ftp': GCIDE_FTP,
+ 'archive': 'gcide-0.51-rc1.tar.xz',
+ 'size': '14M',
+ 'hash': '4bd9b274a893b20b3950cdd5af1c27eb'},
+ {'ftp': GCIDE_FTP,
+ 'archive': 'gcide-0.51-rc1.zip',
+ 'size': '18M',
+ 'hash': '5be7d356878d1d0cd12f6c75478a0004' } )
+
diff --git a/settings.py b/settings.py
new file mode 100644
index 0000000..cc2c435
--- a/dev/null
+++ b/settings.py
@@ -0,0 +1,109 @@
+# Django settings for Dicoweb project.
+#
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2010, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+SITE_ROOT = os.path.dirname (os.path.realpath (__file__))
+
+DEBUG = False
+TEMPLATE_DEBUG = False
+
+ADMINS = (
+ ('admin', 'root@gnu.org.ua'),
+)
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = ''
+DATABASE_NAME = ''
+DATABASE_USER = ''
+DATABASE_PASSWORD = ''
+DATABASE_HOST = ''
+DATABASE_PORT = ''
+
+SITE_ID = 1
+USE_I18N = False
+
+TIME_ZONE = 'Europe/Kiev'
+LANGUAGE_CODE = 'en-us'
+LANGUAGE_COOKIE_NAME = 'dicoweb_lang'
+
+SESSION_COOKIE_NAME = 'dicoweb_sid'
+SESSION_ENGINE = 'django.contrib.sessions.backends.file'
+SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# Caching, see http://docs.djangoproject.com/en/dev/topics/cache/#topics-cache
+#CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
+
+
+# Absolute path to the directory that holds media/static files.
+MEDIA_ROOT = os.path.join (SITE_ROOT, 'static')
+
+# URL that handles the media served from MEDIA_ROOT.
+MEDIA_URL = 'static'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'SET THIS TO A RANDOM STRING'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.load_template_source',
+ 'django.template.loaders.app_directories.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.cache.UpdateCacheMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.gzip.GZipMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.cache.FetchFromCacheMiddleware',
+)
+
+ROOT_URLCONF = 'gcide.urls'
+
+TEMPLATE_DIRS = (
+ os.path.join (SITE_ROOT, 'templates'),
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'gcide',
+)
+
+DICT_SERVERS = ('127.0.0.1:12628',)
+
+AUTH = {}
+# AUTH = { '127.0.0.1:12628' : { 'user' : 'gcide', 'password' : 'pass' } }
+
+DICT_TIMEOUT = 10
+
+GCIDE_VERSION = 0.51
+GCIDE_FTP = "ftp://download.gnu.org.ua/pub/gcide"
+GCIDE_DISTRIBUTIONS = ( {'ftp': GCIDE_FTP,
+ 'archive': 'gcide-0.51-rc1.tar.gz',
+ 'size': '18M',
+ 'hash': 'c5e7215fa834eb1675c33a7621dea6ae'},
+ {'ftp': GCIDE_FTP,
+ 'archive': 'gcide-0.51-rc1.tar.xz',
+ 'size': '14M',
+ 'hash': '4bd9b274a893b20b3950cdd5af1c27eb'},
+ {'ftp': GCIDE_FTP,
+ 'archive': 'gcide-0.51-rc1.zip',
+ 'size': '18M',
+ 'hash': '5be7d356878d1d0cd12f6c75478a0004' } )
diff --git a/static/18px-Bulbgraph.png b/static/18px-Bulbgraph.png
new file mode 100644
index 0000000..7424ea1
--- a/dev/null
+++ b/static/18px-Bulbgraph.png
Binary files differ
diff --git a/static/dicoweb.css b/static/dicoweb.css
new file mode 100644
index 0000000..e5f6132
--- a/dev/null
+++ b/static/dicoweb.css
@@ -0,0 +1,134 @@
+/*
+ This file is part of GNU Dico.
+ Copyright (C) 2008-2010, 2012 Wojciech Polak
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+* {
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: arial, sans-serif;
+ font-size: 95%;
+ background-color: white;
+ color: black;
+}
+
+h1, h2, h3 {
+ font-weight: normal;
+}
+h2, h3, h4 {
+
+}
+h2 {
+ margin: 0;
+ padding-top: .5em;
+ padding-bottom: .17em;
+}
+
+img {
+ padding: 3px;
+ border: 0px;
+}
+
+a {
+
+}
+a:hover {
+ text-decoration: underline;
+}
+
+input {
+ padding: 1px;
+}
+
+ul {
+ margin-bottom: 1em;
+}
+li {
+ margin-left: 2em;
+ line-height: 1.5em;
+}
+p {
+ line-height: 1.8em;
+ margin-bottom: 0.2em;
+}
+pre {
+ font-size: 95%;
+ white-space: normal;
+ line-height: 140%;
+ padding: 1em;
+}
+table {
+ border-spacing: 2px;
+}
+tr, th, td {
+ padding: 2px;
+ vertical-align: top;
+}
+
+.hidden {
+ display: none
+}
+
+#top {
+ margin: 0 auto 0 auto;
+}
+#logo h1 {
+ padding-left: 15px;
+ padding-bottom: 3px;
+}
+#logo a {
+ text-decoration: none;
+}
+#logo img {
+ padding-left: 15px;
+}
+#searchform {
+ width: 600px;
+ margin: 60px auto 0 auto;
+}
+#searchform table {
+ margin-left: auto;
+ margin-right: auto;
+}
+.results, .derror {
+ border-top: dotted 1px #e1e1e1;
+ width: 700px;
+ margin: 1em auto 0 auto;
+ padding: 4px;
+ padding-top: 10px;
+ clear: both;
+}
+.dbname-short {
+ border-bottom: 1px dotted #aaaaaa;
+ cursor: help;
+}
+a.link-lmr {
+ float: right;
+ font-size: 8pt;
+}
+
+#footer {
+ font-size: 80%;
+ width: 700px;
+ text-align: center;
+ border-top: dotted 1px #e1e1e1;
+ margin: 1em auto 10px auto;
+ padding: 4px;
+ padding-top: 10px;
+}
diff --git a/static/dicoweb.js b/static/dicoweb.js
new file mode 100644
index 0000000..017e569
--- a/dev/null
+++ b/static/dicoweb.js
@@ -0,0 +1,80 @@
+/*
+ This file is part of GNU Dico.
+ Copyright (C) 2008-2009, 2012 Wojciech Polak
+
+ GNU Dico is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Dico is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+window.onload = function () {
+
+ var options = readCookie ('dicoweb_options');
+ if (options && options == '1') {
+ var o = GID ('options');
+ if (o) {
+ o.className = '';
+ GID ('toggle_options').innerHTML = _('less options');
+ }
+ }
+
+ GID ('form').onsubmit = function () {
+ var q = GID ('q');
+ var s = document.forms[0].strategy;
+ if (q.value == '' && s.value != 'all')
+ return false;
+ return true;
+ };
+
+ if (document.forms[0].server) {
+ document.forms[0].server.onchange = function () {
+ var q = GID ('q');
+ var u = '?server=' + this.value;
+ if (q && q.value != '')
+ u += '&q=' + encodeURIComponent (q.value);
+ window.location.replace (u);
+ };
+ }
+
+ var q = GID ('q');
+ if (q) {
+ if (q.value == '')
+ q.focus ();
+ }
+
+ function GID (x) {
+ return document.getElementById (x);
+ }
+
+ function gettext (msg) {
+ if (typeof gettext_msg != 'undefined' && gettext_msg[msg])
+ return gettext_msg[msg];
+ return msg;
+ }
+
+ function _(msg) {
+ return gettext (msg);
+ }
+
+ function readCookie (name) {
+ var nameEq = name + '=';
+ var ca = document.cookie.split (';');
+ for (var i = 0; i < ca.length; i++) {
+ var c = ca[i];
+ while (c.charAt (0) == ' ')
+ c = c.substring (1, c.length);
+ if (c.indexOf (nameEq) == 0)
+ return c.substring (nameEq.length, c.length);
+ }
+ return null;
+ }
+};
diff --git a/static/gcide.css b/static/gcide.css
new file mode 100644
index 0000000..5b4588c
--- a/dev/null
+++ b/static/gcide.css
@@ -0,0 +1,206 @@
+body, table, form {
+ margin: 0em;
+ padding: 0em;
+}
+
+body {
+ font-family: serif;
+ font-size: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 960px;
+ background-color: silver;
+ padding: 4px;
+}
+
+h1 {
+ text-align: center;
+}
+
+div {
+ background-color: white;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+div#content {
+ padding: 1em;
+ border: 1px solid black;
+}
+
+div#search {
+ display: table
+}
+
+div#results {
+ padding: 1em;
+ border-top: 1px solid #ccc;
+ margin-top: 1em;
+ min-height: 25em;
+}
+
+div#footer {
+ border-top: solid 2px #ccc;
+ font-size: 80%;
+ padding: 4px;
+}
+
+#menu {
+}
+
+#menu {
+}
+
+div#menu ul {
+ list-style-type: none;
+ width: 100%;
+ border-bottom: solid 2px #ccc;
+ border-collapse: collapse;
+ display: table;
+ margin: 0;
+}
+
+div#menu ul li {
+ display: block;
+ float: left;
+ vertical-align: bottom;
+ padding: 0;
+ color: #777;
+ font-size: 100%;
+}
+
+div#menu ul li.tab {
+ margin-left: 5%;
+}
+
+div#menu ul li a {
+ color: #777;
+ font-size: 100%;
+ text-decoration: none;
+ padding: 2px 0.75em;
+}
+
+div#menu ul li a.active {
+ color: #000;
+ background-color: #ccc;
+}
+
+div#menu ul li a:hover {
+ text-decoration: underline;
+}
+
+form {
+ width: 100%;
+}
+
+div#define {
+ float: left;
+}
+
+div#search {
+ float: right;
+}
+
+#search {
+}
+
+#searchform {
+ padding-top: 2em;
+ padding-bottom: 2em;
+ width: 100%;
+}
+
+
+.clear {
+ clear: both;
+}
+
+div.results pre {
+ font-family: serif;
+ font-size: 100%;
+ white-space: normal;
+ padding: 1em;
+}
+
+
+input {
+ border: 1px solid gray;
+}
+
+select {
+ border: 1px solid gray;
+}
+
+/* Download table */
+table.download {
+ border: 1px black solid;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.download td {
+ border: 1px black solid;
+ padding: 0.3em;
+}
+
+table.download tbody.previous {
+ color: silver;
+}
+
+table.download tbody.previous a {
+ color: lightblue;
+}
+
+/* Development warning */
+div.warning {
+ display: block;
+ z-index: 100;
+ position: fixed;
+ top: 0;
+ left: 0;
+ border: 1px solid gray;
+ background-color: yellow;
+ width: 100%;
+}
+
+div.warning p {
+ text-align: center;
+ padding: 0;
+ margin-left: 0;
+ margin-right: 0;
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+
+div.warning a {
+ color: red;
+}
+
+div.topmargin {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-left: 0;
+ margin-right: 0;
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+
+/* For use in notices */
+.block-note {
+ background-image: url(18px-Bulbgraph.png);
+ background-position: top left;
+ background-repeat: no-repeat;
+ background-color: #fff1b1;
+ border-left: 1px dotted black;
+ border-top: 1px dotted black;
+ border-right: 2px solid black;
+ border-bottom: 2px solid black;
+}
+.block-contents {
+ display: block;
+ padding-left: 20px;
+}
+ \ No newline at end of file
diff --git a/static/gnu-head-sm.jpg b/static/gnu-head-sm.jpg
new file mode 100644
index 0000000..49502c7
--- a/dev/null
+++ b/static/gnu-head-sm.jpg
Binary files differ
diff --git a/templates/404.html b/templates/404.html
new file mode 100644
index 0000000..1e1ad28
--- a/dev/null
+++ b/templates/404.html
@@ -0,0 +1,5 @@
+{% extends 'base.html' %}
+
+{% block results %}
+<div class="derror">Error 404</div>
+{% endblock %}
diff --git a/templates/500.html b/templates/500.html
new file mode 100644
index 0000000..c57b636
--- a/dev/null
+++ b/templates/500.html
@@ -0,0 +1,5 @@
+{% extends 'base.html' %}
+
+{% block results %}
+<div class="derror">Error 500</div>
+{% endblock %}
diff --git a/templates/about.html b/templates/about.html
new file mode 100644
index 0000000..97accd6
--- a/dev/null
+++ b/templates/about.html
@@ -0,0 +1,53 @@
+{# This file is part of GCIDE #}
+{# Copyright (C) 2012 Sergey Poznyakoff #}
+
+{# GCIDE is free software; you can redistribute it and/or modify #}
+{# it under the terms of the GNU General Public License as published by #}
+{# the Free Software Foundation; either version 3, or (at your option) #}
+{# any later version. #}
+
+{# GCIDE is distributed in the hope that it will be useful, #}
+{# but WITHOUT ANY WARRANTY; without even the implied warranty of #}
+{# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #}
+{# GNU General Public License for more details. #}
+
+{# You should have received a copy of the GNU General Public License #}
+{# along with GCIDE. If not, see <http://www.gnu.org/licenses/>. #}
+
+<p>
+The <dfn>GNU Collaborative International Dictionary of English</dfn>,
+or <dfn>GCIDE</dfn> for short, is a free dictionary derived from
+</p>
+<div style="text-align: center">
+ Webster's Revised Unabridged Dictionary<br />
+ Version published 1913<br />
+ by the C. & G. Merriam Co.<br />
+ Springfield, Mass.<br />
+ Under the direction of<br />
+ Noah Porter, D.D., LL.D.
+</div>
+<p>
+and supplemented with some new definitions, in particular from
+<a href="http://wordnet.princeton.edu/">WordNet</a>.
+</p>
+
+<p>The dictionary is created by Patric J. Cassidy, and proof-read and
+supplemented by volunteers from around the world.
+</p>
+
+<p>The complete text of the dictionary corpus along with accompanying
+ ancillary files is <a href="/download">available for download</a>
+ from GNU ftp site or any of its mirrors around the world.</p>
+
+<p>There are various ways of using this dictionary. First of all you
+ can consult it on-line from this site. Some other sites, such as
+ <a href="http://dicoweb.gnu.org.ua/?server=gnu.org.ua&db=gcide">dicoweb</a>,
+ provide a similar facility.</p>
+
+<p>You can also use the dictionary offline. To do so, download and
+ unpack its copy and use
+ the <a href="http://dico.gnu.org.ua/gcider.html">gcider</a> utility.</p>
+
+<p><b>GCIDE</b> is maintained by Patric J. Cassidy and Sergey
+ Poznyakoff. If you have any suggestions, feel free
+ to <a href="mailto:gcide --AT-- gnu.org">contact us</a>.</p>
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..52e8ad6
--- a/dev/null
+++ b/templates/base.html
@@ -0,0 +1,71 @@
+{# This file is part of GCIDE #}
+{# Copyright (C) 2012 Sergey Poznyakoff #}
+
+{# GCIDE is free software; you can redistribute it and/or modify #}
+{# it under the terms of the GNU General Public License as published by #}
+{# the Free Software Foundation; either version 3, or (at your option) #}
+{# any later version. #}
+
+{# GCIDE is distributed in the hope that it will be useful, #}
+{# but WITHOUT ANY WARRANTY; without even the implied warranty of #}
+{# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #}
+{# GNU General Public License for more details. #}
+
+{# You should have received a copy of the GNU General Public License #}
+{# along with GCIDE. If not, see <http://www.gnu.org/licenses/>. #}
+
+{% load withsetting %}
+{% withsetting MEDIA_URL as media_prefix %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<title>{{ page.title|striptags }}GNU Collaborative International Dictionary of English</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<meta name="keywords" content="gcide cide webster" />
+<meta name="description" content="GNU Collaborative International Dictionary of English" />
+<meta name="robots" content="{{ page.robots }}" />
+<link rel="stylesheet" type="text/css" href="{{ media_prefix }}/gcide.css" />
+<link rel="search" type="application/opensearchdescription+xml" title="GCIDE WebSearch" href="{% url opensearch %}" />
+<script type="text/javascript" src="{{ media_prefix }}/dicoweb.js"></script>
+</head>
+
+<body>
+{% block warning %}{% endblock %}
+<div id="content">
+<h1>GNU Collaborative International Dictionary of English</h1>
+
+
+<div id="menu">
+ <ul>
+ <li class="tab"><a {{ menu_class.about|safe }} href="/">About</a></li>
+ <li class="tab"><a {{ menu_class.license|safe }} href="/license">License</a>
+</li>
+ <li class="tab"><a {{ menu_class.download|safe }} href="/download">Download</a></li>
+ </ul>
+</div>
+
+{% block search %}{% endblock %}
+
+<div class="clear"></div>
+
+<div id="results">
+{% block results %}{% endblock %}
+</div>
+
+<div id="footer">
+<p>
+ Powered by <a href="http://www.gnu.org.ua/software/dico/">GNU
+ Dico</a>. Copyright &copy; 2008-{% now "Y" %} GNU Dico Team.
+</p>
+{% block license %}{% endblock %}
+<p>
+ Please send broken links and other corrections or suggestions to
+ <a href="mailto:gray@gnu.org.ua">Sergey Poznyakoff</a>.
+</p>
+</div>
+</div>
+
+</body>
+</html>
+{% endwithsetting %}
diff --git a/templates/download.html b/templates/download.html
new file mode 100644
index 0000000..38392e7
--- a/dev/null
+++ b/templates/download.html
@@ -0,0 +1,118 @@
+{# This file is part of GCIDE #}
+{# Copyright (C) 2012 Sergey Poznyakoff #}
+
+{# GCIDE is free software; you can redistribute it and/or modify #}
+{# it under the terms of the GNU General Public License as published by #}
+{# the Free Software Foundation; either version 3, or (at your option) #}
+{# any later version. #}
+
+{# GCIDE is distributed in the hope that it will be useful, #}
+{# but WITHOUT ANY WARRANTY; without even the implied warranty of #}
+{# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #}
+{# GNU General Public License for more details. #}
+
+{# You should have received a copy of the GNU General Public License #}
+{# along with GCIDE. If not, see <http://www.gnu.org/licenses/>. #}
+
+{% load withsetting %}
+
+{% withsetting GCIDE_FTP as ftp, GCIDE_DISTRIBUTIONS as distlist, GCIDE_VERSION as version %}
+
+<div class="block-note"><span class="block-contents">
+ Note: the download addresses below are for the <dfn>release
+ candidate</dfn> versions. They will change when the official
+ version is released]</span></div>
+<h2>Latest releases</h2>
+<table class="download">
+ <thead>
+ <tr>
+ <td>Archive</td>
+ <td>Size</td>
+ <td>MD5</td>
+ <td>Signature</td>
+ </tr>
+ </thead>
+ {% for dist in distlist %}
+ <tbody {% if dist.class %}class="{{ dist.class }}"{% endif %}>
+ <tr>
+ <td><a href="{{ dist.ftp }}/{{ dist.archive }}">{{ dist.archive }}</a></td>
+ <td>{{ dist.size }}</td>
+ <td>{{ dist.hash }}</td>
+ <td><a href="{{ dist.ftp }}/{{ dist.archive }}.sig">{{ dist.archive }}.sig</a></td>
+ </tr>
+ </tbody>
+ {% endfor %}
+</table>
+
+<p>
+You can use the signature file to verify that
+the corresponding file (without the .sig suffix) is intact. First,
+be sure to download both the <i>.sig</i> file and the corresponding tarball.
+Then, run a command like this:
+</p>
+
+<pre>
+ gpg --verify gcide-{{ version }}.tar.gz.sig
+</pre>
+
+<p>
+If that command fails because you don't have the required public key,
+then run the following to import it:
+</p>
+
+<pre>
+ gpg --keyserver keys.gnupg.net --recv-keys 55D0C732
+</pre>
+<p>
+and rerun the `gpg --verify' command.
+</p>
+
+<h2>Installation and Use</h2>
+<p>
+ Download the latest tarball and unpack it. This will create the directory
+<tt>gcide-{{ version }}</tt>, populated with dictionary corpus files
+(named <tt>CIDE.A</tt> through <tt>CIDE.Z</tt>) and several ancillary
+files.
+</p>
+<p>
+ The corpus files use a special markup, therefore <b>GNU</b> provides
+ special software that can be used to browse them. Download
+ the <a href="http://www.gnu.org.ua/software/dico/download.html">GNU
+ Dico</a>, compile and install it:</p>
+
+<pre>
+ $ tar xf dico-3.0.tar.gz
+ $ cd dico-3.0.tar.gz
+ $ ./configure
+ $ make
+ $ make install
+</pre>
+
+<p>
+ Once done, start the <b>gcider</b> utility to browse and view the
+ dictionary. When started for the first time, it will ask you about
+ the directory where the dictionary files reside. Then, the utility will
+ remember this information and re-use it on subsequent startups.
+</p>
+
+<p>
+ The detailed instructions on use of <b>gcider</b> are available <a href="http://dico.gnu.org.ua/gcider.html">online</a>.
+</p>
+
+<p>GNU Dico provides also
+ a <a href="http://dico.gnu.org.ua/modules/gcide">loadable module</a>
+ which enables dictionary servers to handle <b>GCIDE</b>.
+</p>
+
+<h2>Other releases</h2>
+<p>
+ This and older versions of <b>GCIDE</b> can be downloaded from
+<a href="{{ ftp }}">its ftp home</a>.
+</p>
+<p>
+ You can keep track of the news and updates at the
+<a href="http://puszcza.gnu.org.ua/projects/gcide">project's
+ homepage</a>.
+</p>
+
+{% endwithsetting %}
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..b0ea615
--- a/dev/null
+++ b/templates/index.html
@@ -0,0 +1,154 @@
+{# This file is part of GCIDE #}
+{# Copyright (C) 2012 Sergey Poznyakoff #}
+
+{# GCIDE is free software; you can redistribute it and/or modify #}
+{# it under the terms of the GNU General Public License as published by #}
+{# the Free Software Foundation; either version 3, or (at your option) #}
+{# any later version. #}
+
+{# GCIDE is distributed in the hope that it will be useful, #}
+{# but WITHOUT ANY WARRANTY; without even the implied warranty of #}
+{# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #}
+{# GNU General Public License for more details. #}
+
+{# You should have received a copy of the GNU General Public License #}
+{# along with GCIDE. If not, see <http://www.gnu.org/licenses/>. #}
+
+{% extends 'base.html' %}
+{% load dictlookup %}
+{% load macros %}
+{% load withsetting %}
+
+{% block warning %}
+{% withsetting DEBUG as debug %}
+{% if debug %}
+<div class="warning">
+<p>Warning! This is a development version. It may be incomplete and contain dangling links</p>
+</div>
+<div class="topmargin"><br /></div>
+{% endif %}
+{% endwithsetting %}
+{% endblock %}
+
+{% block search %}
+
+<div id="searchform">
+ <form id="form" method="get" action="">
+ <div id="define">
+ <input type="text" id="q" name="q" size="35"
+ style="width:350px" value="{{ q|escape }}" />
+ <input type="submit" name="define" value="Define" />
+ </div>
+ <div id="search">
+ <input type="submit" name="search" value="Search" />
+ <select name="strategy" style="width:350px">
+ {{ selects.st.html|safe }}
+ </select>
+ </div>
+ </form>
+</div>
+
+{% endblock %}
+
+{% macro matches title items %}
+ <div class="match results">
+ <h3><a name="match-results"></a>{{ title }}</h3>
+ <ul>
+ {% for mt in items %}
+ <li>
+ {% for term in mt.1 %}
+ <a href="?q={{ term|urlencode }}&amp;db={{ mt.0|urlencode }}&amp;define=1" title="Define {{ term }}">{{ term|title }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}
+ </li>
+ {% endfor %}
+ </ul>
+ </div>
+{% endmacro %}
+
+{% block results %}
+
+{% if result.error %}
+<div class="derror">
+ {{ result.msg }}
+</div>
+{% else %}
+ {% if result.type == "define" %}
+ <div class="definition results">
+ {% if result.definitions %}
+ {% if mtc.matches %}
+ <a href="#match-results" class="link-lmr">last match results</a>
+ {% endif %}
+ <h3>
+ {% if result.count == 1 %}
+ Found one definition
+ {% else %}
+ Found {{ result.count }} definitions
+ {% endif %}
+ </h3>
+ <ol class="definitions">
+ {% for df in result.definitions %}
+ <li>
+ <pre>
+ {% if df.format_html %}
+{{ df.desc|safe }}
+ {% else %}
+{{ df.desc|safe|linebreaksbr }}
+ {% endif %}
+ </pre></li>
+ {% endfor %}
+ </ol>
+ {% if mtc.matches %}
+ {% usemacro matches "Last match results" mtc.matches.items %}
+ {% endif %}
+ {% else %}
+ {% if result.matches %}
+ {% usemacro matches "No exact match found. Perhaps you meant one of the following:" result.matches.items %}
+ {% endif %}
+ </div>
+ {% endif %}
+ {% else %}
+ {% if result.type == "search" %}
+ {% if mtc.matches %}
+ {% usemacro matches "Matches" mtc.matches.items %}
+ {% endif %}
+ {% else %}
+ {% if result.desc %}
+ {% if result.type == "info" %}
+ <div class="info">
+ <pre>
+ {{ result.desc }}
+ </pre>
+ </div>
+ {% else %}
+ <div class="results">
+ <pre>
+ {{ result.desc|linebreaksbr }}
+ </pre>
+ </div>
+ {% endif %}
+ {% else %}
+ <div id="description">
+ {% if item %}
+ {% include item %}
+ {% else %}
+ {% include "about.html" %}
+ {% endif %}
+ </div>
+ {% endif %}
+ {% endif %}
+ {% endif %}
+{% endif %}
+
+{% endblock %}
+
+{% block license %}
+<p>
+This text is available under
+the
+{% if result %}
+<a href="http://www.gnu.org/licenses/gpl.html">GNU General Public License</a>
+{% else %}
+<a href="http://creativecommons.org/licenses/by-sa/3.0/">Creative
+Commons Attribution-ShareAlike License</a>
+{% endif %}
+.</p>
+{% endblock %}
diff --git a/templates/license.html b/templates/license.html
new file mode 100644
index 0000000..187a4aa
--- a/dev/null
+++ b/templates/license.html
@@ -0,0 +1,21 @@
+{# This file is part of GCIDE #}
+{# Copyright (C) 2012 Sergey Poznyakoff #}
+
+{# GCIDE is free software; you can redistribute it and/or modify #}
+{# it under the terms of the GNU General Public License as published by #}
+{# the Free Software Foundation; either version 3, or (at your option) #}
+{# any later version. #}
+
+{# GCIDE is distributed in the hope that it will be useful, #}
+{# but WITHOUT ANY WARRANTY; without even the implied warranty of #}
+{# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #}
+{# GNU General Public License for more details. #}
+
+{# You should have received a copy of the GNU General Public License #}
+{# along with GCIDE. If not, see <http://www.gnu.org/licenses/>. #}
+
+<p>
+<b>GNU CIDE</b> is distributed under the terms of the
+<a href="http://www.gnu.org/licenses/gpl.html">GNU General Public
+ License</a>, version 3 or (at your option) any later version.
+</p>
diff --git a/templates/opensearch.xml b/templates/opensearch.xml
new file mode 100644
index 0000000..a393c7e
--- a/dev/null
+++ b/templates/opensearch.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+ <ShortName>GCIDE WebSearch</ShortName>
+ <Description>GCIDE WebSearch Interface</Description>
+ <Url type="text/html" method="get" template="{{ url_query }}?q={searchTerms}"/>
+ <InputEncoding>UTF-8</InputEncoding>
+ <Image width="16" height="16">{{ url_media }}/favicon.ico</Image>
+</OpenSearchDescription>
diff --git a/templatetags/__init__.py b/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/templatetags/__init__.py
diff --git a/templatetags/dictlookup.py b/templatetags/dictlookup.py
new file mode 100644
index 0000000..e9ea334
--- a/dev/null
+++ b/templatetags/dictlookup.py
@@ -0,0 +1,27 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2009, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+from django.template import Library
+
+register = Library ()
+
+def dictlookup (dict, key):
+ try:
+ return dict.get (key, '')
+ except KeyError:
+ return ''
+
+register.filter (dictlookup)
diff --git a/templatetags/macros.py b/templatetags/macros.py
new file mode 100644
index 0000000..0095f23
--- a/dev/null
+++ b/templatetags/macros.py
@@ -0,0 +1,160 @@
+#
+# templatetags/macros.py - Support for macros in Django templates
+#
+# Author: Michal Ludvig <michal@logix.cz>
+# http://www.logix.cz/michal
+#
+
+"""
+Tag library that provides support for "macros" in
+Django templates.
+
+Usage example:
+
+0) Save this file as
+ <yourapp>/taglibrary/macros.py
+
+1) In your template load the library:
+ {% load macros %}
+
+2) Define a new macro called 'my_macro' with
+ parameter 'arg1':
+ {% macro my_macro arg1 %}
+ Parameter: {{ arg1 }} <br/>
+ {% endmacro %}
+
+3) Use the macro with a String parameter:
+ {% usemacro my_macro "String parameter" %}
+
+ or with a variable parameter (provided the
+ context defines 'somearg' variable, e.g. with
+ value "Variable parameter"):
+ {% usemacro my_macro somearg %}
+
+ The output of the above code would be:
+ Parameter: String parameter <br/>
+ Parameter: Variable parameter <br/>
+
+4) Alternatively save your macros in a separate
+ file, e.g. "mymacros.html" and load it to the
+ current template with:
+ {% loadmacros "mymacros.html" %}
+ Then use these loaded macros in {% usemacro %}
+ as described above.
+
+Macros can take zero or more arguments and both
+context variables and macro arguments are resolved
+in macro body when used in {% usemacro ... %} tag.
+
+Bear in mind that defined and loaded Macros are local
+to each template file and are not inherited
+through {% extends ... %} tags.
+"""
+
+from django import template
+from django.template import resolve_variable, FilterExpression
+from django.template.loader import get_template, get_template_from_string, find_template_source
+from django.conf import settings
+import re
+
+register = template.Library()
+
+def _setup_macros_dict(parser):
+ ## Metadata of each macro are stored in a new attribute
+ ## of 'parser' class. That way we can access it later
+ ## in the template when processing 'usemacro' tags.
+ try:
+ ## Only try to access it to eventually trigger an exception
+ parser._macros
+ except AttributeError:
+ parser._macros = {}
+
+class DefineMacroNode(template.Node):
+ def __init__(self, name, nodelist, args):
+ self.name = name
+ self.nodelist = nodelist
+ self.args = args
+
+ def render(self, context):
+ ## empty string - {% macro %} tag does no output
+ return ''
+
+@register.tag(name="macro")
+def do_macro(parser, token):
+ try:
+ args = token.split_contents()
+ tag_name, macro_name, args = args[0], args[1], args[2:]
+ except IndexError:
+ raise template.TemplateSyntaxError, "'%s' tag requires at least one argument (macro name)" % token.contents.split()[0]
+ # TODO: check that 'args' are all simple strings ([a-zA-Z0-9_]+)
+ r_valid_arg_name = re.compile(r'^[a-zA-Z0-9_]+$')
+ for arg in args:
+ if not r_valid_arg_name.match(arg):
+ raise template.TemplateSyntaxError, "Argument '%s' to macro '%s' contains illegal characters. Only alphanumeric characters and '_' are allowed." % (arg, macro_name)
+ nodelist = parser.parse(('endmacro', ))
+ parser.delete_first_token()
+
+ ## Metadata of each macro are stored in a new attribute
+ ## of 'parser' class. That way we can access it later
+ ## in the template when processing 'usemacro' tags.
+ _setup_macros_dict(parser)
+
+ parser._macros[macro_name] = DefineMacroNode(macro_name, nodelist, args)
+ return parser._macros[macro_name]
+
+class LoadMacrosNode(template.Node):
+ def render(self, context):
+ ## empty string - {% loadmacros %} tag does no output
+ return ''
+
+@register.tag(name="loadmacros")
+def do_loadmacros(parser, token):
+ try:
+ tag_name, filename = token.split_contents()
+ except IndexError:
+ raise template.TemplateSyntaxError, "'%s' tag requires at least one argument (macro name)" % token.contents.split()[0]
+ if filename[0] in ('"', "'") and filename[-1] == filename[0]:
+ filename = filename[1:-1]
+ t = get_template(filename)
+ macros = t.nodelist.get_nodes_by_type(DefineMacroNode)
+ ## Metadata of each macro are stored in a new attribute
+ ## of 'parser' class. That way we can access it later
+ ## in the template when processing 'usemacro' tags.
+ _setup_macros_dict(parser)
+ for macro in macros:
+ parser._macros[macro.name] = macro
+ return LoadMacrosNode()
+
+class UseMacroNode(template.Node):
+ def __init__(self, macro, filter_expressions):
+ self.nodelist = macro.nodelist
+ self.args = macro.args
+ self.filter_expressions = filter_expressions
+ def render(self, context):
+ for (arg, fe) in [(self.args[i], self.filter_expressions[i]) for i in range(len(self.args))]:
+ context[arg] = fe.resolve(context)
+ return self.nodelist.render(context)
+
+@register.tag(name="usemacro")
+def do_usemacro(parser, token):
+ try:
+ args = token.split_contents()
+ tag_name, macro_name, values = args[0], args[1], args[2:]
+ except IndexError:
+ raise template.TemplateSyntaxError, "'%s' tag requires at least one argument (macro name)" % token.contents.split()[0]
+ try:
+ macro = parser._macros[macro_name]
+ except (AttributeError, KeyError):
+ raise template.TemplateSyntaxError, "Macro '%s' is not defined" % macro_name
+
+ if (len(values) != len(macro.args)):
+ raise template.TemplateSyntaxError, "Macro '%s' was declared with %d parameters and used with %d parameter" % (
+ macro_name,
+ len(macro.args),
+ len(values))
+ filter_expressions = []
+ for val in values:
+ if (val[0] == "'" or val[0] == '"') and (val[0] != val[-1]):
+ raise template.TemplateSyntaxError, "Non-terminated string argument: %s" % val[1:]
+ filter_expressions.append(FilterExpression(val, parser))
+ return UseMacroNode(macro, filter_expressions)
diff --git a/templatetags/withsetting.py b/templatetags/withsetting.py
new file mode 100644
index 0000000..464cc78
--- a/dev/null
+++ b/templatetags/withsetting.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+# This file is part of GCIDE
+# Copyright (C) 2012 Sergey Poznyakoff
+#
+# GCIDE is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCIDE is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCIDE. If not, see <http://www.gnu.org/licenses/>.
+
+from django import template
+from django.template import Node, NodeList, Template, Context, Variable
+from django.template import TemplateSyntaxError
+from django.conf import settings
+import re
+
+register = template.Library()
+
+class WithSettingNode(template.Node):
+ def __init__(self, dict, nodelist):
+ self.dict = dict
+ self.nodelist = nodelist
+
+ def __repr__(self):
+ return "<WithSettingNode>"
+
+ def render(self, context):
+ context.push()
+ context.update(self.dict)
+ output = self.nodelist.render(context)
+ context.pop()
+ return output
+
+
+def mkdict(name,bits,dict):
+ if len(bits) < 3 or bits[1] != "as":
+ raise TemplateSyntaxError("%r expected format is 'value as name'" %
+ name)
+ if not bits[2].endswith(','):
+ if len(bits) > 3:
+ raise TemplateSyntaxError("%r expected format is 'value as name'" %
+ name)
+ dict[bits[2]] = eval('settings.' + bits[0])
+ else:
+ dict[bits[2][0:-1]] = eval('settings.' + bits[0])
+ mkdict(name,bits[3:],dict)
+
+#@register.tag
+def do_withsetting(parser, token):
+ bits = list(token.split_contents())
+ dict = {}
+ try:
+ mkdict(bits[0], bits[1:], dict)
+ except AttributeError, diag:
+ raise TemplateSyntaxError("%r: %s " % (bits[0], diag))
+ nodelist = parser.parse(('endwithsetting',))
+ parser.delete_first_token()
+ return WithSettingNode(dict, nodelist)
+
+do_withsetting = register.tag('withsetting', do_withsetting)
diff --git a/urls.py b/urls.py
new file mode 100644
index 0000000..94d4dcd
--- a/dev/null
+++ b/urls.py
@@ -0,0 +1,28 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2010, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+from django.conf import settings
+from django.conf.urls.defaults import *
+import views
+
+urlpatterns = patterns (
+ '',
+ (r'^/?$', views.index ),
+ (r'^(\w+)$', views.index ),
+ (r'^opensearch\.xml$', views.opensearch, {}, 'opensearch'),
+ (r'^static/(?P<path>.*)$', 'django.views.static.serve',
+ {'document_root': settings.MEDIA_ROOT}),
+)
diff --git a/views.py b/views.py
new file mode 100644
index 0000000..070754e
--- a/dev/null
+++ b/views.py
@@ -0,0 +1,253 @@
+# This file is part of GNU Dico.
+# Copyright (C) 2008-2010, 2012 Wojciech Polak
+#
+# GNU Dico is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Dico is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Dico. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import hashlib
+import socket
+from django.conf import settings
+from django.core import urlresolvers
+from django.core.cache import cache
+from django.shortcuts import render_to_response
+from django.utils.translation import ugettext as _
+
+import dicoclient
+from wit import wiki2html
+
+known_items = [ 'about', 'license', 'download' ]
+
+def index (request, item=None):
+ page = {
+ 'robots': 'index',
+ }
+ selects = {}
+ mtc = {}
+ result = {}
+ markup_style = 'default'
+ port = 2628
+ databases = []
+ strategies = []
+
+ menu_class = {}
+
+ if item and not item in known_items:
+ return render_to_response ('404.html', {})
+
+ sid = request.COOKIES.get ('dicoweb_sid', '')
+ request.session.set_expiry (0)
+
+ accept_lang = request.META.get ('HTTP_ACCEPT_LANGUAGE', '').split (',')
+ for i, lang in enumerate (accept_lang):
+ accept_lang[i] = lang.split (';')[0]
+
+ if 'server' in request.session:
+ server = request.session['server']
+ else:
+ server = settings.DICT_SERVERS[0]
+ server = request.GET.get ('server', server)
+ if not server in settings.DICT_SERVERS:
+ server = settings.DICT_SERVERS[0]
+ request.session['server'] = server;
+
+ if len (settings.DICT_SERVERS) > 1:
+ selects['sv'] = HtmlOptions (settings.DICT_SERVERS, server)
+
+ key = hashlib.md5 ('%s/%s' % (sid, server.encode ('ascii',
+ 'backslashreplace')))
+ sid = key.hexdigest ()
+
+ type = 'search'
+ if 'define' in request.GET:
+ type = 'define'
+ else:
+ type = 'search'
+
+ database = request.GET.get ('db', '*')
+ strategy = request.GET.get ('strategy', '.')
+
+ if (server in settings.AUTH and 'user' in settings.AUTH[server]):
+ cred = settings.AUTH[server]
+ srvkey = cred['user'] + '@' + server
+ else:
+ cred = None
+ srvkey = server
+
+ key_databases = str ('dicoweb/databases/' + srvkey)
+ key_strategies = str ('dicoweb/strategies/' + srvkey)
+ databases = cache.get (key_databases)
+ strategies = cache.get (key_strategies)
+
+ if server.find (':') != -1:
+ s = server.split (':', 1)
+ server = s[0]
+ port = int (s[1])
+
+ if not databases or not strategies:
+ dc = dicoclient.DicoClient ()
+ try:
+ dc.open (server, port, cred)
+ dc.timeout = settings.DICT_TIMEOUT
+ databases = dc.show_databases ()['databases']
+ strategies = dc.show_strategies ()['strategies']
+ dc.close ()
+ except (socket.timeout, socket.error, dicoclient.DicoNotConnectedError):
+ return render_to_response ('index.html', {'selects': selects})
+
+ cache.set (key_databases, databases, timeout=86400)
+ cache.set (key_strategies, strategies, timeout=86400)
+
+ for s in strategies:
+ s[1] = _(s[1])
+ strategies.insert (0, ['.', _('Default')])
+
+ selects['st'] = HtmlOptions (strategies, strategy)
+
+ if (len(databases) > 1):
+ selects['db'] = HtmlOptions (databases, database)
+ databases.insert (0, ['!', _('First match')])
+ databases.insert (0, ['*', _('All')])
+
+ q = request.GET.get ('q', '')
+
+ if 'q' in request.GET and q != '':
+ langkey = '*';
+ if database == '*': langkey = ','.join (accept_lang)
+
+ key = hashlib.md5 ('%s:%d/%s/%s/%s/%s/%s' %
+ (srvkey, port, langkey, type, database, strategy,
+ q.encode ('ascii', 'backslashreplace')))
+ key = key.hexdigest ()
+ result = cache.get ('dicoweb/' + key)
+
+ if not result:
+ try:
+ dc = dicoclient.DicoClient ()
+ dc.timeout = settings.DICT_TIMEOUT
+ dc.open (server, port, cred)
+ dc.option ('MIME')
+
+ if database == '*' and 'lang' in dc.server_capas:
+ dc.option ('LANG', ': ' + ' '.join (accept_lang))
+ if 'markup-wiki' in dc.server_capas:
+ if dc.option ('MARKUP', 'wiki'):
+ markup_style = 'wiki'
+
+ if database == 'dbinfo':
+ result = dc.show_info (q)
+ elif type == 'define':
+ result = dc.define (database, q)
+ if 'error' in result and result['error'] == '552':
+ result = dc.match (database, strategy, q)
+ type = 'search'
+ result['type'] = 'define'
+ else:
+ result = dc.match (database, strategy, q)
+ result['type'] = 'search'
+ dc.close ()
+
+ result['markup_style'] = markup_style
+ cache.set ('dicoweb/' + key, result, timeout=3600)
+
+ except (socket.timeout, socket.error,
+ dicoclient.DicoNotConnectedError):
+ return render_to_response ('index.html',
+ {'selects': selects})
+
+ # get last match results
+ if sid and type == 'search':
+ cache.set ('dicoweb/%s/last_match' % sid, key, timeout=3600)
+ else:
+ key = cache.get ('dicoweb/%s/last_match' % sid)
+ if key != None:
+ mtc = cache.get ('dicoweb/' + key)
+
+ mtc['dbnames'] = {}
+ if 'matches' in mtc:
+ for m in mtc['matches']:
+ for d in databases:
+ if d[0] == m:
+ mtc['dbnames'][m] = d[1]
+ break
+
+ if database == 'dbinfo': q = ''
+ if q != '':
+ page['title'] = q + ' - '
+ page['robots'] = 'noindex,nofollow'
+ else:
+ if not item:
+ item = 'about'
+ menu_class[item] = 'class="active"'
+ item += ".html"
+
+ if 'definitions' in result:
+ rx1 = re.compile ('{+(.*?)}+', re.DOTALL)
+ for df in result['definitions']:
+ if df.has_key ('content-type') \
+ and df['content-type'].startswith ('text/x-wiki'):
+ lang = df['x-wiki-language'] \
+ if df.has_key ('x-wiki-language') else 'en'
+ wikiparser = wiki2html.HtmlWiktionaryMarkup (text=df['desc'],
+ html_base='?q=',
+ lang=lang)
+ wikiparser.parse ()
+ df['desc'] = str (wikiparser)
+ df['format_html'] = True
+ else:
+ df['desc'] = re.sub ('_(.*?)_', '<b>\\1</b>', df['desc'])
+ df['desc'] = re.sub (rx1, __subs1, df['desc'])
+
+ return render_to_response ('index.html', {'page': page,
+ 'item': item,
+ 'menu_class': menu_class,
+ 'q': q,
+ 'mtc': mtc,
+ 'result': result,
+ 'selects': selects,})
+
+def opensearch (request):
+ url_query = request.build_absolute_uri (urlresolvers.reverse ('index'))
+ url_media = request.build_absolute_uri (settings.MEDIA_URL)
+ return render_to_response ('opensearch.xml', {'url_query': url_query,
+ 'url_media': url_media},
+ mimetype='application/xml')
+
+def __subs1 (match):
+ s = re.sub (r' +', ' ', match.group (1))
+ # FIXME: added "define=Define", which is in my opinion right.
+ return '<a href="?q=%s&define=Define" title="Search for %s">%s</a>' \
+ % (s.replace ('\n', ''), s.replace ('\n', ''), s)
+
+class HtmlOptions:
+ def __init__ (self, lst=[], value=''):
+ self.lst = lst
+ self.value = value
+
+ def html (self):
+ buf = []
+ for opt in self.lst:
+ if len (opt) == 2:
+ if not opt[1]:
+ opt[1] = opt[0]
+ if opt[0] == self.value:
+ buf.append ('<option value="%s" selected="selected">%s</option>' % (opt[0], opt[1]))
+ else:
+ buf.append ('<option value="%s">%s</option>' % (opt[0],
+ opt[1]))
+ else:
+ if opt == self.value:
+ buf.append ('<option value="%s" selected="selected">%s</option>' % (opt, opt))
+ else:
+ buf.append ('<option value="%s">%s</option>' % (opt, opt))
+ return '\n'.join (buf)

Return to:

Send suggestions and report system problems to the System administrator.