/[drupal]/contributions/sandbox/antinomia/modules/druplet/druplet.module
ViewVC logotype

Contents of /contributions/sandbox/antinomia/modules/druplet/druplet.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.6 - (show annotations) (download) (as text)
Sat Jun 30 01:14:09 2007 UTC (2 years, 4 months ago) by antinomia
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +19 -19 lines
File MIME type: text/x-php
drupal coding standards
1 <?php
2 // $Id: druplet.module,v 1.5 2007/06/29 17:02:10 antinomia Exp $
3
4 function druplet_menu($may_cache) {
5 if ($may_cache) {
6 $items[] = array(
7 'path' => 'admin/settings/druplet',
8 'title' => t('Druplet Settings'),
9 'description' => t('Configure Druplet settings.'),
10 'callback' => 'drupal_get_form',
11 'callback arguments' => array('druplet_admin_settings'),
12 'access' => user_access('administer site configuration'),
13 'type' => MENU_NORMAL_ITEM,
14 );
15 }
16 return $items;
17 }
18
19 function druplet_admin_settings() {
20 $form['druplet_host'] = array(
21 '#type' => 'textfield',
22 '#title' => t('Host'),
23 '#description' => t('Enter the host/domain name you wish to use to map sites to (i.e. mysite.com). New druplets will appear as subdomains (i.e. druplet.mysite.com)'),
24 '#default_value' => variable_get('druplet_host', $_SERVER['HTTP_HOST']),
25 '#size' => 20,
26 '#required' => TRUE,
27 );
28 $form['druplet_group'] = array(
29 '#type' => 'textfield',
30 '#title' => t('Druplet Group (Advanced)'),
31 '#description' => t('Enter the name of the group to add new SFTP users to.'),
32 '#default_value' => variable_get('druplet_group', 'druplet'),
33 '#size' => 20,
34 '#required' => TRUE,
35 );
36 $form['druplet_jailkit'] = array(
37 '#type' => 'checkbox',
38 '#title' => t('Jail SFTP users using JailKit (Advanced)'),
39 '#description' => t('This option allows for separate SFTP accounts to be set up for each Druplet. You must install JailKit for this to work. See the README.txt file for more information.'),
40 '#default_value' => variable_get('druplet_jailkit', FALSE),
41 );
42
43 return system_settings_form($form);
44 }
45
46
47 /**
48 * Implementation of hook_node_info().
49 */
50 function druplet_node_info() {
51 return array(
52 'druplet' => array(
53 'name' => t('Druplet'),
54 'description' => t('Build a new Druplet using an existing installation profile.'),
55 'module' => 'druplet',
56 'has_title' => TRUE,
57 'has_body' => TRUE,
58 'body_label' => t('Body'),
59 ),
60 );
61 }
62
63 function druplet_find_profiles() {
64 $profiles = file_scan_directory('./profiles', '\.profile$', array('.', '..', 'CVS'), 0, TRUE, 'name', 0);
65 foreach ($profiles as $key => $value) {
66 $profile_list[$key] = $key;
67 }
68 return $profile_list;
69 }
70
71 function druplet_form(&$node) {
72 $form['profile'] = array(
73 '#type' => 'select',
74 '#title' => t('Site Profile'),
75 '#description' => t('Choose which site profile you wish to use to configure your Druplet.'),
76 '#default_value' => $node->profile,
77 '#options' => druplet_find_profiles(),
78 '#weight' => -6,
79 '#disabled' => ($node->nid ? TRUE : FALSE)
80 );
81 $form['title'] = array(
82 '#type' => 'textfield',
83 '#title' => t('Druplet Name'),
84 '#description' => t('Pick a name for your Druplet. The name will be used as part of the domain name (i.e. druplet.mysite.com). Max 20 characters, use only alphanumerics, hyphens and underscores.'),
85 '#required' => TRUE,
86 '#default_value' => $node->title,
87 '#weight' => -5,
88 '#maxlength' => 20,
89 '#disabled' => ($node->nid ? TRUE : FALSE)
90 );
91 $form['body'] = array(
92 '#type' => 'textarea',
93 '#title' => t('Description'),
94 '#default_value' => $node->body,
95 '#weight' => -4,
96 '#required' => FALSE
97 );
98 $form['db_name'] = array(
99 '#type' => 'textfield',
100 '#title' => t('Database Name'),
101 '#description' => t('This field will be automatically populated'),
102 '#default_value' => $node->db_name,
103 '#weight' => -3,
104 '#maxlength' => 20,
105 '#disabled' => TRUE
106 );
107 $form['db_user'] = array(
108 '#type' => 'textfield',
109 '#title' => t('Database User'),
110 '#description' => t('This field will be automatically populated'),
111 '#default_value' => $node->db_user,
112 '#weight' => -2,
113 '#maxlength' => 20,
114 '#disabled' => TRUE
115 );
116 $form['db_password'] = array(
117 '#type' => 'textfield',
118 '#title' => t('Database Password'),
119 '#description' => t('This field will be automatically populated'),
120 '#default_value' => $node->db_password,
121 '#weight' => -1,
122 '#maxlength' => 20,
123 '#disabled' => TRUE
124 );
125 $form['submit'] = array(
126 '#type' => 'submit',
127 '#title' => t('Submit'),
128 '#value' => t('Submit'),
129 '#weight' => 0,
130 );
131 return $form;
132 }
133
134
135 function druplet_validate($node) {
136 // check to make sure druplet_host is set
137 if (!variable_get('druplet_host', $_SERVER['HTTP_HOST'])) {
138 form_set_error(' ', t('You must configure Druplet at admin/settings/druplet before you can add a druplet.'));
139 }
140
141 // check to make sure there are no funny characters
142 if (preg_replace('/[^a-zA-Z0-9_-]/', '', $node->title) != $node->title) {
143 form_set_error('title', t('Please use only alphanumerics, hyphens and underscores in the title.'));
144 }
145
146 // check to make sure no other druplets have the same name
147 if (db_num_rows(db_query("SELECT nid FROM node WHERE `title` = '%s'", strtolower($node->title)))) {
148 form_set_error('title', t('That druplet name has already been registered. Don\'t cramp their style, maaan!'));
149 }
150
151 }
152
153 function druplet_view($node) {
154 $node->content['body']['#value'] = theme_druplet($node);
155 return $node;
156 }
157
158 function druplet_submit(&$node) {
159 if (!$node->nid) {
160 $site = druplet_generate_site($node);
161 }
162 }
163
164 function druplet_insert($node) {
165 db_query("INSERT INTO {druplet} (vid, nid, profile, db_name, db_user, db_password) VALUES (%d, %d, '%s', '%s', '%s', '%s')", $node->vid, $node->nid, $node->profile, $node->db_name, $node->db_user, $node->db_password);
166 }
167
168 function druplet_load($node) {
169 $additions = db_fetch_object(db_query('SELECT profile, db_name, db_user, db_password FROM {druplet} WHERE vid = %d', $node->vid));
170 return $additions;
171 }
172
173 function druplet_update($node) {
174 // if this is a new node or we're adding a new revision,
175 if ($node->revision) {
176 druplet_insert($node);
177 }
178 else {
179 db_query("UPDATE {druplet} SET profile = '%s', db_name = '%s', db_user = '%s', db_password = '%s' WHERE vid = %d", $node->profile, $node->db_name, $node->db_user, $node->db_password, $node->nid);
180 }
181 }
182
183 function druplet_delete($node) {
184 // Notice that we're matching all revision, by using the node's nid.
185 db_query('DELETE FROM {druplet} WHERE nid = %d', $node->nid);
186 }
187
188 function druplet_generate_site($node) {
189
190 // Create variables that we'll need later during this process
191 $node->host = variable_get('druplet_host', $_SERVER['HTTP_HOST']);
192 $node->subdomain = strtolower($node->title);
193 $node->domain = $node->subdomain .'.'. $node->host;
194 $node->path = $_SERVER['DOCUMENT_ROOT'] .'/sites/'. $node->domain;
195
196 $node->db_name = 'druplet_'. strtolower($node->title);
197 $node->db_user = druplet_genpassword(10);
198 $node->db_password = druplet_genpassword(10);
199
200 // Generate New Site
201 $node = druplet_generate_sitebase($node);
202
203 $new_site_url = 'http://'. $node->domain;
204
205 // Open the new site in the user's browser
206
207 // header('Location: '. $new_site_url);
208 // exit;
209
210 return $node;
211
212 }
213
214
215 function druplet_generate_sitebase($site) {
216
217 // 1) Create a new directory as /drupal_webroot/sites/druplet.mysite.com
218 if (!mkdir($site->path)) {
219 drupal_set_message(t('Apache doesn\'t have write access to the sites directory. This is a fatal error.'));
220 }
221
222 // 2) Add a modules directory
223 if (!mkdir($site->path .'/modules')) {
224 drupal_set_message(t('Apache doesn\'t have write access to the modules directory. This is a fatal error.'));
225 }
226
227 // 3) Add a themes directory
228 if (!mkdir($site->path .'/themes')) {
229 drupal_set_message(t('Apache doesn\'t have write access to the themes directory. This is a fatal error.'));
230 }
231
232 // 4) Add a files directory
233 if (!mkdir($site->path .'/files')) {
234 drupal_set_message(t('Apache doesn\'t have write access to the files directory. This is a fatal error.'));
235 }
236
237 // 5) Set the files directory as writable
238 if (!chmod($site->path .'/files', 0777)) {
239 drupal_set_message(t('The files directory could not be set writeable by Apache. You\'ll want to do this manually.'));
240 }
241
242 // 6) Create the database
243 if (!db_query('CREATE DATABASE '. $site->db_name)) {
244 drupal_set_message(t('The database user was unable to build the database for this site. This is a fatal error.'));
245 }
246
247 // 7) Create a database user (using same name as database)
248 $query = "GRANT ALL PRIVILEGES ON ". $site->db_name .".* TO '". $site->db_name ."'@'localhost' IDENTIFIED BY '". $site->db_password ."'";
249 if (!db_query($query)) {
250 drupal_set_message(t('The database user was unable to set permissions on the database. This is a fatal error.'));
251 }
252
253 // 8) Build the settings.php file
254 $settingstxt_path = $_SERVER['DOCUMENT_ROOT'] .'/'. drupal_get_path('module', 'druplet') .'/settings.txt';
255 $file = fopen($settingstxt_path, 'r');
256 while ($line = fgets($file, 1000) ) {
257 $template_file .= $line;
258 }
259 fclose($file);
260 $settings_db_url = 'mysql://'. $site->db_name .':'. $site->db_password .'@localhost/'. $site->db_name;
261 $template_file = str_replace('%%db_url%%', $settings_db_url, $template_file); // set db url (i.e. mysql://username:password@localhost/dbname )
262 $settings_files_dir = 'sites/'. $site->domain .'/files'; // set files directory (i.e. sites/druplet.mysite.com/files )
263 $template_file = str_replace('%%files_dir%%', $settings_files_dir, $template_file); // set files directory (i.e. sites/druplet.mysite.com/files )
264 $template_file = str_replace('%%base_url%%', 'http://'. $site->domain, $template_file); // set $base_url )
265 // save the new settings.php file to the site directory
266 $file = fopen($site->path .'/settings.php', 'a');
267 if (fwrite($file, $template_file) === FALSE) {
268 drupal_set_message(t('Apache could not write settings.php to the Druplet site directory. This is a fatal error.'));
269 }
270 fclose($file);
271
272 if (variable_get('druplet_jailkit', FALSE)) {
273
274 // 9) Create and Jail a System User
275 $group = variable_get('druplet_group', 'druplet');
276 $site_name = $site->subdomain;
277 $host = $site->host;
278 $command = "sudo /usr/sbin/useradd -g $group $site_name";
279 if ($error = shell_exec($command)) {
280 drupal_set_message(t('The user could not be added, likely because Apache does not have access to useradd.'));
281 }
282
283 // jail user
284 $command = "sudo /usr/sbin/jk_jailuser -m -j /home/jail $site_name";
285 if ($error = shell_exec($command)) {
286 drupal_set_message(t('User was not jailed. Do you have JailKit installed? http://olivier.sessink.nl/jailkit'));
287 }
288
289 // change user password
290 $password = crypt($site->db_password);
291 $command = "sudo usermod -p '$password' $site_name";
292 if ($error = shell_exec($command)) {
293 drupal_set_message(t('The user\'s password could not be changed, likely because Apache does not have access to usermod.'));
294 }
295
296 // 10) Create directory in user jail and bind it to the site directory
297
298 $command = "sudo mkdir /home/jail/home/$site_name/$site_name.$host";
299 if ($error = shell_exec($command)) {
300 drupal_set_message(t('Directory could not be created in jail.'));
301 }
302
303 // bind site directory to user's home directory
304 $command = "sudo mount --bind /var/www/html/sites/$site_name.$host /home/jail/home/$site_name/$site_name.$host";
305 if ($error = shell_exec($command)) {
306 drupal_set_message(t('User jail could not be bound to site directory.'));
307 }
308 }
309
310 // 11) Chown the sites directory and settings.php file to apache
311 // chown site directory to user
312 $command = "sudo chown -R $site_name /var/www/html/sites/$site_name.$host";
313 if ($error = shell_exec($command)) {
314 drupal_set_message(t('Fatal Error.'));
315 }
316
317 // chown settings to apache
318 $command = "sudo chown apache /var/www/html/sites/$site_name.$host/settings.php";
319 if ($error = shell_exec($command)) {
320 drupal_set_message(t('Fatal Error.'));
321 }
322
323 // 12) Run installation profile
324
325 global $db_url;
326 $default_url = $db_url;
327 $db_url = array();
328 $db_url['default'] = $db_url;
329 $db_url['newsite'] = $settings_db_url;
330 db_set_active('newsite');
331
332 require_once $_SERVER['DOCUMENT_ROOT'] .'/includes/install.inc';
333
334 // 12b) Verify existence of all required modules.
335 $modules = drupal_verify_profile($site->profile, NULL);
336
337 // 12c) Perform actual installation defined in the profile.
338 drupal_install_profile($site->profile, $modules);
339 variable_set('install_profile', $site->profile);
340 drupal_maintenance_theme();
341
342 // Show profile finalization info.
343 $function = $site->profile .'_profile_final';
344 if (function_exists($function)) {
345 // More steps required
346 $function();
347 }
348
349 // return active db to main site
350 db_set_active('default');
351
352 // pass back the $site object
353 return $site;
354 }
355
356
357 function druplet_access($op, $node) {
358 global $user;
359
360 if ($op == 'create') {
361 // Only users with permission to do so may create this node type.
362 return user_access('create druplet');
363 }
364
365 // Users who create a node may edit or delete it later, assuming they have the
366 // necessary permissions.
367 if ($op == 'update' || $op == 'delete') {
368 if (user_access('edit own druplet') && ($user->uid == $node->uid)) {
369 return TRUE;
370 }
371 }
372 }
373
374 function druplet_perm() {
375 return array('create druplet', 'edit own druplet');
376 }
377
378 function druplet_genpassword($length) {
379 srand((double)microtime()*1000000);
380 $vowels = array("a", "e", "i", "o", "u");
381 $cons = array("b", "c", "d", "g", "h", "j", "k", "l", "m", "n", "p", "r", "s", "t", "u", "v", "w", "tr", "cr", "br", "fr", "th", "dr", "ch", "ph", "wr", "st", "sp", "sw", "pr", "sl", "cl");
382 $num_vowels = count($vowels);
383 $num_cons = count($cons);
384 for ($i = 0; $i < $length; $i++) {
385 $password .= $cons[rand(0, $num_cons - 1)] . $vowels[rand(0, $num_vowels - 1)];
386 }
387 return substr($password, 0, $length);
388 }
389
390
391 function theme_druplet($node) {
392 $subdomain = strtolower($node->title);
393 $host = variable_get('druplet_host', $_SERVER['HTTP_HOST']);
394 $site_url = "http://$subdomain.$host";
395 $phpmyadmin_url = $site_url .'/phpMyAdmin';
396 $header = array(t('Parameter'), t('Value'));
397 $rows = array();
398 $rows[] = array(t('Site URL'), l($site_url, $site_url, NULL, NULL, NULL, TRUE));
399 if (variable_get('druplet_jailkit', TRUE)) {
400 $rows[] = array(t('SFTP Host'), $subdomain .'.'. $host);
401 $rows[] = array(t('SFTP Username'), $subdomain);
402 $rows[] = array(t('SFTP Password'), $node->db_password);
403 }
404 $rows[] = array(t('phpMyAdmin URL'), l($phpmyadmin_url, $phpmyadmin_url, NULL, NULL, NULL, TRUE));
405 $rows[] = array(t('Database'), $node->db_name);
406 $rows[] = array(t('DB Username'), $node->db_user);
407 $rows[] = array(t('DB Password'), $node->db_password);
408 return theme('table', $header, $rows);
409 }

  ViewVC Help
Powered by ViewVC 1.1.2