1<?php 2/** 3 * ProjectFile: a class that declares a single poject-file in a project 4 * definition file. 5 * 6 * @author Junling Ma <junlingm@gmail.com> 7 */ 8 9class ProjectFile { 10 protected $name = NULL; 11 protected $deps = NULL; 12 13 public function __construct($file) { 14 $this->name = $file->name(); 15 $this->deps = $file->dependency(); 16 } 17 18 public function name() { return $this->name; } 19 public function dependency() { return $this->deps; } 20 public function changed_on_disk() { return $this->changed_on_disk; } 21 22 public function path($project_path) { 23 return $project_path . $this->name; 24 } 25 26 public function delete($project_path) { 27 $path = $this->path($project_path); 28 if (file_exists($path)) { 29 $this->delete_media_file($project_path); 30 unlink($path); 31 } 32 } 33 34 public function copy($project_path, $file) { 35 $this->deps = $file->dependency(); 36 } 37 38 public function type() { return NULL; } 39 40 public function equal($project_path, $file) { 41 return $this->type() == $file->type() 42 && $this->deps == $file->dependency(); 43 } 44 45 public static function create($project, $file) { 46 $class = 'Project' . ucfirst($file->type()); 47 return new $class($project, $file); 48 } 49 50 public function time($project_path) { 51 $path = $this->path($project_path); 52 $time = (file_exists($path)) ? filemtime($path) : NULL; 53 return $time; 54 } 55 56 public function content($project_path) { 57 $path = $this->path($project_path); 58 if (file_exists($path)) 59 return file_get_contents($path); 60 return NULL; 61 } 62 63 protected function update_media_file($project_path) { 64 $path = $this->path($project_path); 65 if (is_link($path)) return; 66 global $ID; 67 $id = getNS($ID) . ':' . $this->name; 68 $media_path = mediaFN($id); 69 global $media_file_revision_limit; 70 if ($media_file_revision_limit > 0 && 71 filesize($media_path) > $media_file_revision_limit) { 72 unlink($media_path); 73 msg("File $id is over the \"media file revision limit\". A revision will not be saved"); 74 } 75 list($ext, $mime) = mimetype($path); 76 $data[0] = $path; 77 $data[1] = mediaFN($id); 78 $data[2] = $id; 79 $data[3] = $mime; 80 $data[4] = file_exists(mediaFN($id)); 81 $data[5] = 'copy_updated_file'; 82 trigger_event('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true); 83 } 84 85 protected function delete_media_file($project_path) { 86 global $ID; 87 $id = getNS($ID) . ':' . $this->name; 88 $media_path = mediaFN($id); 89 if (file_exists($media_path)) { 90 media_saveOldRevision($id); 91 unlink($media_path); 92 } 93 } 94} 95 96class ProjectSource extends ProjectFile { 97 public function __construct($project, $file) { 98 parent::__construct($file); 99 $this->set_content($project->path(), $file->content()); 100 } 101 102 public function is_target() { return false; } 103 public function type() { return SOURCE; } 104 105 public function equal($project_path, $file) { 106 if (!parent::equal($project_path, $file)) return false; 107 return $this->content($project_path) == $file->content(); 108 } 109 110 private function set_content($project_path, $content) { 111 file_put_contents($this->path($project_path), $content); 112 $this->update_media_file($project_path); 113 } 114 115 public function copy($project_path, $file) { 116 parent::copy($project_path, $file); 117 $this->set_content($project_path, $file->content()); 118 } 119 120 public function makable() { return true; } 121} 122 123class ProjectGenerated extends ProjectFile { 124 protected $recipe = NULL; 125 protected $last_made = NULL; 126 127 public function __construct($project, $file) { 128 parent::__construct($file); 129 $this->recipe = $file->recipe(); 130 } 131 132 public function equal($project_path, $file) { 133 return parent::equal($project_path, $file) 134 && $this->recipe == $file->recipe(); 135 } 136 137 public function type() { return TARGET; } 138 public function recipe() { return $this->recipe; } 139 140 public function copy($project_path, $file) { 141 parent::copy($project_path, $file); 142 $this->recipe = $file->recipe(); 143 } 144 145 public function delete($project_path, $delete_log = false) { 146 parent::delete($project_path); 147 if ($delete_log) { 148 $path = $this->path($project_path) . '.make.log'; 149 if (file_exists($path)) unlink($path); 150 } 151 } 152 153 public function make($working_path) { 154 $current_dir = getcwd(); 155 chdir($working_path); 156 $path = $working_path . $this->name; 157 if (!$this->recipe) return 0; 158 if (file_exists($path)) unlink($path); 159 $log = $this->log_file($working_path); 160 if (file_exists($log)) unlink($log); 161 $command = "/bin/sh -e >$log 2>&1"; 162 $recipe = $this->recipe(); 163 $f = popen($command, 'w'); 164 fprintf($f, "%s\n", $this->recipe()); 165 $result = pclose($f); 166 if ($result === 0) 167 $this->update_media_file($working_path); 168 else $this->delete_media_file($working_path); 169 chdir($current_dir); 170 return $result; 171 } 172 173 public function set_last_made_time($working_path) { 174 $this->last_made = $this->time($working_path); 175 } 176 177 public function needs_update($working_path) { 178 if ($this->last_made == NULL) return true; 179 return $this->time($working_path) != $this->last_made; 180 } 181 182 public function makable() { 183 if ($this->recipe === NULL) return false; 184 return (trim($this->recipe) != ""); 185 } 186 187 public function is_target() { return true; } 188 189 protected function log_file($working_path) { 190 return $this->path($working_path) . '.make.log'; 191 } 192 193 protected function log($working_path, $content) { 194 $log = $this->log_file($working_path); 195 file_put_contents($log, $content); 196 } 197} 198 199class ProjectCrossLink extends ProjectGenerated { 200 protected $crosslink = NULL; 201 202 public function __construct($project, $file) { 203 parent::__construct($project, $file); 204 $this->crosslink = $file->crosslink(); 205 } 206 207 public function crosslink() { return $this->crosslink; } 208 209 public function type() { return CROSSLINK; } 210 211 public function equal($project_path, $file) { 212 return parent::equal($project_path, $file) 213 && $this->crosslink == $file->crosslink(); 214 } 215 216 public function copy($project_path, $file) { 217 parent::copy($project_path, $file); 218 $this->crosslink = $file->crosslink(); 219 } 220 221 protected function crosslink_path() { 222 if (!$this->crosslink) return NULL; 223 $path = explode(":", $this->crosslink); 224 $media = ($path[0] == '[media]'); 225 if ($media) array_shift($path); 226 if (count($path) > 1 && $path[0] != PROJECTS_NAMESPACE) return NULL; 227 $id = implode(':', $path); 228 $project = Project::project(); 229 if (!getNS($id)) 230 $id = $project->id($id); 231 if ($media) return mediaFN($id); 232 return $project->path($id); 233 } 234 235 public function time($working_path) { 236 $path = $working_path . $this->name; 237 if (!file_exists($path)) return NULL; 238 $lspath = lstat($path); 239 return $lspath['mtime']; 240 } 241 242 public function make($working_path) { 243 $log = $this->log_file($working_path); 244 if (!$this->crosslink) { 245 file_put_contents($log, "The crosslink is not defined in this file!\n"); 246 return 1; 247 } 248 $link = realpath($this->crosslink_path()); 249 $path = $this->path($working_path); 250 $crosslink = $this->crosslink; 251 if (!file_exists($link)) { 252 if (file_exists($path)) unlink($path); 253 file_put_contents($log, "file $crosslink does not exist!\n"); 254 return 1; 255 } 256 file_put_contents($log, "linked $crosslink"); 257 if (file_exists($path)) unlink($path); 258 symlink($link, $path); 259 return 0; 260 } 261 262 public function makable() { return true; } 263} 264 265?>