1 #!/usr/bin/env python 2 3 from os.path import abspath, exists, join, split 4 import os 5 import sys 6 import shutil 7 import re 8 9 # Regular expressions for editing MoinMoin scripts and configuration files. 10 11 def compile_definition(name): 12 return re.compile(r"^(\s*)#?(%s =).*$" % name, re.MULTILINE) 13 14 moin_cgi_prefix = re.compile("^#sys.path.insert\(0, 'PREFIX.*$", re.MULTILINE) 15 moin_cgi_wikiconfig = re.compile("^#sys.path.insert\(0, '/path/to/wikiconfigdir.*$", re.MULTILINE) 16 wikiconfig_py_site_name = compile_definition("site_name") 17 wikiconfig_py_url_prefix_static = compile_definition("url_prefix_static") 18 wikiconfig_py_superuser = compile_definition("superuser") 19 wikiconfig_py_acl_rights_before = compile_definition("acl_rights_before") 20 wikiconfig_py_page_front_page = compile_definition("page_front_page") 21 wikiconfig_py_data_dir = compile_definition("data_dir") 22 wikiconfig_py_data_underlay_dir = compile_definition("data_underlay_dir") 23 24 # Templates for Apache site definitions. 25 26 apache_site = """ 27 ScriptAlias %(url_path)s "%(web_app_dir)s/moin.cgi" 28 """ 29 30 apache_site_extra_moin18 = """ 31 Alias %(static_url_path)s "%(htdocs_dir)s/" 32 """ 33 34 apache_site_host_restriction = """ 35 <Location "%(url_path)s"> 36 Deny from all 37 Allow from %(superuser_client_host)s 38 </Location> 39 """ 40 41 # Utility functions. 42 43 def readfile(filename): 44 f = open(filename) 45 try: 46 return f.read() 47 finally: 48 f.close() 49 50 def writefile(filename, s): 51 f = open(filename, "w") 52 try: 53 f.write(s) 54 finally: 55 f.close() 56 57 def status(message): 58 print message 59 60 def note(message): 61 print message 62 63 class Installation: 64 65 "A class for installing and initialising MoinMoin." 66 67 # NOTE: Need to detect Web server user. 68 69 web_user = "www-data" 70 web_group = "www-data" 71 72 def __init__(self, moin_distribution, prefix, web_app_dir, web_site_dir, 73 common_dir, url_path, site_name, front_page_name, superuser): 74 75 """ 76 Initialise a Wiki installation using the following: 77 78 * moin_distribution - the directory containing a MoinMoin source 79 distribution 80 * prefix - the installation prefix (equivalent to /usr) 81 * web_app_dir - the directory where Web applications and scripts 82 reside (such as /home/www-user/cgi-bin) 83 * web_site_dir - the directory where Web site definitions reside 84 (such as /etc/apache2/sites-available) 85 * common_dir - the directory where the Wiki configuration, 86 resources and instance will reside (such as 87 /home/www-user/mywiki) 88 * url_path - the URL path at which the Wiki will be made 89 available (such as / or /mywiki) 90 * site_name - the name of the site (such as "My Wiki") 91 * front_page_name - the front page name for the site (such as 92 "FrontPage" or a specific name for the site) 93 * superuser - the name of the site's superuser (such as 94 "AdminUser") 95 """ 96 97 self.moin_distribution = moin_distribution 98 self.site_name = site_name 99 self.front_page_name = front_page_name 100 self.superuser = superuser 101 102 # NOTE: Support the detection of the Apache sites directory. 103 104 self.prefix, self.web_app_dir, self.web_site_dir, self.common_dir = \ 105 map(abspath, (prefix, web_app_dir, web_site_dir, common_dir)) 106 107 # Strip any trailing "/" from the URL path. 108 109 if url_path.endswith("/"): 110 self.url_path = url_path[:-1] 111 else: 112 self.url_path = url_path 113 114 # Define and create specific directories. 115 116 self.conf_dir = join(self.common_dir, "conf") 117 self.instance_dir = join(self.common_dir, "wikidata") 118 119 # For MoinMoin 1.8.x and earlier. 120 121 self.htdocs_dir = join(self.instance_dir, "share", "moin", "htdocs") 122 123 # Miscellaneous configuration options. 124 125 self.superuser_client_host = "127.0.0.1" 126 127 # Find the version. 128 129 self.moin_version = self.get_moin_version() 130 131 def get_moin_version(self): 132 133 "Inspect the MoinMoin package information, returning the version." 134 135 this_dir = os.getcwd() 136 os.chdir(self.moin_distribution) 137 138 f = open("PKG-INFO") 139 try: 140 for line in f.xreadlines(): 141 columns = line.split() 142 if columns[0] == "Version:": 143 return columns[1] 144 145 return None 146 147 finally: 148 f.close() 149 os.chdir(this_dir) 150 151 def setup(self): 152 153 "Set up the installation." 154 155 for d in (self.conf_dir, self.instance_dir, self.web_app_dir, self.web_site_dir): 156 if not exists(d): 157 os.makedirs(d) 158 159 self.install_moin() 160 self.install_data() 161 self.configure_moin() 162 self.edit_moin_scripts() 163 self.make_site_files() 164 self.make_post_install_script() 165 166 def install_moin(self): 167 168 "Enter the distribution directory and run the setup script." 169 170 # NOTE: Possibly check for an existing installation and skip repeated 171 # NOTE: installation attempts. 172 173 this_dir = os.getcwd() 174 os.chdir(self.moin_distribution) 175 176 log_filename = "install-%s.log" % split(self.common_dir)[-1] 177 178 status("Installing MoinMoin in %s..." % self.prefix) 179 180 os.system("python setup.py --quiet install --force --prefix='%s' --install-data='%s' --record='%s'" % ( 181 self.prefix, self.instance_dir, log_filename)) 182 183 os.chdir(this_dir) 184 185 def install_data(self): 186 187 "Install Wiki data." 188 189 # The default wikiconfig assumes data and underlay in the same directory. 190 191 status("Installing data and underlay in %s..." % self.conf_dir) 192 193 for d in ("data", "underlay"): 194 shutil.copytree(join(self.moin_distribution, "wiki", d), join(self.conf_dir, d)) 195 196 def configure_moin(self): 197 198 "Edit the Wiki configuration file." 199 200 # NOTE: Single Wiki only so far. 201 202 # Static URLs seem to be different in MoinMoin 1.9.x. 203 # For earlier versions, reserve URL space alongside the Wiki. 204 # NOTE: MoinMoin usually uses an apparently common URL space associated 205 # NOTE: with the version, but more specific locations are probably 206 # NOTE: acceptable if less efficient. 207 208 if self.moin_version.startswith("1.9"): 209 self.static_url_path = self.url_path 210 url_prefix_static_sub = r"\1\2 %r + url_prefix_static" % self.static_url_path 211 else: 212 self.static_url_path = self.url_path + "-static" 213 url_prefix_static_sub = r"\1\2 %r" % self.static_url_path 214 215 # Edit the Wiki configuration file. 216 217 wikiconfig_py = join(self.moin_distribution, "wiki", "config", "wikiconfig.py") 218 219 status("Editing configuration from %s..." % wikiconfig_py) 220 221 s = readfile(wikiconfig_py) 222 s = wikiconfig_py_site_name.sub(r"\1\2 %r" % self.site_name, s) 223 s = wikiconfig_py_url_prefix_static.sub(url_prefix_static_sub, s) 224 s = wikiconfig_py_superuser.sub(r"\1\2 %r" % [self.superuser], s) 225 s = wikiconfig_py_acl_rights_before.sub(r"\1\2 %r" % (u"%s:read,write,delete,revert,admin" % self.superuser), s) 226 s = wikiconfig_py_page_front_page.sub(r"\1\2 %r" % self.front_page_name, s, count=1) 227 228 if not self.moin_version.startswith("1.9"): 229 data_dir = join(self.conf_dir, "data") 230 data_underlay_dir = join(self.conf_dir, "underlay") 231 232 s = wikiconfig_py_data_dir.sub(r"\1\2 %r" % data_dir, s) 233 s = wikiconfig_py_data_underlay_dir.sub(r"\1\2 %r" % data_underlay_dir, s) 234 235 writefile(join(self.conf_dir, "wikiconfig.py"), s) 236 237 def edit_moin_scripts(self): 238 239 "Edit the moin script and the CGI script." 240 241 moin_script = join(self.prefix, "bin", "moin") 242 prefix_site_packages = join(self.prefix, "lib", "python%s.%s" % sys.version_info[:2], "site-packages") 243 244 status("Editing moin script at %s..." % moin_script) 245 246 s = readfile(moin_script) 247 s = s.replace("#import sys", "import sys\nsys.path.insert(0, %r)" % prefix_site_packages) 248 249 writefile(moin_script, s) 250 251 # Edit and install CGI script. 252 # NOTE: CGI only so far. 253 # NOTE: Permissions should be checked. 254 255 moin_cgi = join(self.instance_dir, "share", "moin", "server", "moin.cgi") 256 moin_cgi_installed = join(self.web_app_dir, "moin.cgi") 257 258 status("Editing moin.cgi script from %s..." % moin_cgi) 259 260 s = readfile(moin_cgi) 261 s = moin_cgi_prefix.sub("sys.path.insert(0, %r)" % prefix_site_packages, s) 262 s = moin_cgi_wikiconfig.sub("sys.path.insert(0, %r)" % self.conf_dir, s) 263 264 writefile(moin_cgi_installed, s) 265 os.system("chmod a+rx '%s'" % moin_cgi_installed) 266 267 def make_site_files(self): 268 269 "Make the Apache site files." 270 271 # NOTE: Using local namespace for substitution. 272 273 site_def = join(self.web_site_dir, self.site_name) 274 site_def_private = join(self.web_site_dir, "%s-private" % self.site_name) 275 276 status("Writing Apache site definitions to %s and %s..." % (site_def, site_def_private)) 277 278 s = apache_site % self.__dict__ 279 280 if not self.moin_version.startswith("1.9"): 281 s += apache_site_extra_moin18 % self.__dict__ 282 283 writefile(site_def, s) 284 285 # NOTE: By making the superuser in the installed Wiki, this site file 286 # NOTE: would no longer be required. 287 288 s += apache_site_host_restriction % self.__dict__ 289 290 writefile(site_def_private, s) 291 292 def make_post_install_script(self): 293 294 "Write a post-install script with additional actions." 295 296 this_user = os.environ["USER"] 297 postinst_script = "moinsetup-post.sh" 298 299 s = "" 300 301 for d in ("data", "underlay"): 302 s += "chown -R %s.%s '%s'\n" % (this_user, self.web_group, join(self.conf_dir, d)) 303 s += "chmod -R g+w '%s'\n" % join(self.conf_dir, d) 304 305 if not self.moin_version.startswith("1.9"): 306 s += "chown -R %s.%s '%s'\n" % (this_user, self.web_group, self.htdocs_dir) 307 308 writefile(postinst_script, s) 309 note("Run %s as root to set file ownership and permissions." % postinst_script) 310 311 # Main program. 312 313 if __name__ == "__main__": 314 315 # Obtain as many arguments as needed for the setup function. 316 317 try: 318 n = 9 # number of Installation initialiser arguments 319 args = sys.argv[1:n+1] 320 args[n-1] 321 except (IndexError, ValueError): 322 print Installation.__init__.__doc__ 323 sys.exit(1) 324 325 installation = Installation(*args) 326 installation.setup() 327 328 # vim: tabstop=4 expandtab shiftwidth=4