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 
9 class 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 
96 class 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 
123 class 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 
199 class 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 ?>