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