1: <?php
  2: 
  3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36: 
 37: 
 38: Yii::import('application.components.util.*');
 39: 
 40:  41:  42:  43:  
 44:   45:  46:  47:  48:  49:  50: 
 51: class ThemeBuildCommand extends CConsoleCommand {
 52: 
 53:      54:  55: 
 56:     public $inputDir = '../';
 57: 
 58:      59:  60: 
 61:     public $outputFile = 'components/ThemeGenerator/templates/generatedRules.php';
 62:     public $moduleOverridesFile = 
 63:         'components/ThemeGenerator/templates/generatedModuleOverrides.php';
 64: 
 65:      66:  67: 
 68:     public function run($args) {
 69:         if (isset($args[0])) {
 70:             $this->inputDir = $args[0];
 71:         }
 72: 
 73:         if (isset($args[1])) {
 74:             $this->outputFile = $args[1];
 75:         }
 76: 
 77:         if (isset($args[0]) && $args[0] == '--keys') {
 78:             echo "These are the avaliable theming keys\n";
 79: 
 80:             foreach(ThemeGenerator::getProfileKeys() as $key) {
 81:                 echo "$key\n";
 82:             }
 83: 
 84:             return;
 85:         }
 86: 
 87:         echo "Building theme...\n";
 88:         
 89:         $paths = $this->getCssFiles ($this->inputDir);
 90: 
 91:         $length = count($paths);
 92: 
 93:         if ($length < 1) {
 94:             echo "Error: no Css files found in directory: $this->inputDir";
 95:             return;
 96:         }
 97: 
 98:         echo "$length css files found\n";
 99:         echo "Scanning for theme tags\n";
100:         $counter = 0.0;
101: 
102:         
103:         $matches = array();
104:         foreach ($paths as $i => $path) {
105:             $matches = array_merge($matches, $this->scanCssFile($path));
106:             
107:             
108:             while ($counter < $i/$length) {
109:                 $counter+= 0.1;
110:                 $this->progressBar($counter);
111:             }
112: 
113:         }
114: 
115:         $matchesLength = count($matches);
116:         echo "\r$matchesLength rules found     \n";
117: 
118:         if ($matchesLength < 1) {
119:             echo "No rules found, aborting\n";
120:             return;
121:         }
122: 
123:         echo "Formatting rules...\n";
124: 
125:         
126:         $output = "<?php ".
127:         "/* This file is generated by ThemeBuildCommand.php. Do not edit manually */\n".
128:         "return \"\n"; 
129:         foreach ($matches as $selector => $rule) {
130:             $output .= $this->formatRule($selector, $rule);
131:         }
132:         $output .= "\n \"; ?>"; 
133: 
134:         $this->writeFile ($this->outputFile, $output);
135: 
136:         
137:         $output = "<?php ".
138:         "/* This file is generated by ThemeBuildCommand.php. Do not edit manually */\n".
139:         "return \"\n"; 
140:         foreach ($matches as $selector => $rule) {
141:             $output .= $this->formatModuleOverridesRule($selector, $rule);
142:         }
143:         $output .= "\n \"; ?>"; 
144: 
145:         $this->writeFile ($this->moduleOverridesFile, $output);
146:     }
147: 
148:     public function writeFile ($outputFile, $output) {
149:         
150:         if (file_exists($outputFile) && sha1_file($outputFile) == sha1($output)) {
151:             echo "No changes detected in $outputFile, Aborting\n";
152:             return;
153:         }
154: 
155:         echo "Saving to $outputFile\n";
156:         file_put_contents ($outputFile, $output);
157:     }
158: 
159:     160: 161: 162: 163: 
164:     public function getCssFiles($root) {
165:         $iter = new RecursiveIteratorIterator(
166:             new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS),
167:             RecursiveIteratorIterator::SELF_FIRST,
168:             RecursiveIteratorIterator::CATCH_GET_CHILD 
169:         );
170: 
171:         $paths = array($root);
172:         foreach ($iter as $path => $dir) {
173:             if (preg_match('/\.css$/', $path)) {
174:                 $paths[] = $path;
175:             }
176:         }
177: 
178:         return $paths;
179:     }
180: 
181:     182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 
194:     public function scanCssFile($path) {
195:         $handle = fopen($path, "r");
196:         $rules = array();
197: 
198:         $lineNumber = -1;
199:         while (($line = fgets($handle)) !== false) {
200:             $lineNumber++;
201:             if (!preg_match('/@theme/', $line)) continue;
202: 
203:             list($selector, $comment, $rule) = $this->makeRule($path, $lineNumber);
204: 
205:             
206:             if (!isset($rules[$selector])) {
207:                 $rules[$selector] = array($rule);
208:                 $rules[$selector]['comments'] = array($comment);
209:                 continue;
210:             } 
211: 
212:             
213:             if (in_array($rule, $rules[$selector])) continue;
214:             $rules[$selector][] = $rule;
215: 
216:             
217:             if (in_array($comment, $rules[$selector]['comments'])) continue;
218:             $rules[$selector]['comments'][] = $comment;
219:         }       
220: 
221:         fclose($handle);
222: 
223:         return $rules;
224:     }
225: 
226:     227: 228: 229: 230: 
231:     public function makeRule($file, $lineNumber) {
232:         $lines = file($file);
233:         $themeLine = $lines[$lineNumber];
234:         
235:         $stripped = preg_replace('/.*@theme\ *(.*)\*\//', '\1', $themeLine);
236: 
237:         
238:         $stripped = preg_replace('/\ \ */', ' ', $stripped);
239:         $stripped = preg_replace('/:/', '', $stripped);
240:         $params = explode(' ', $stripped);
241: 
242:         
243:         while(!preg_match('/line\ [0-9]+/', $lines[$lineNumber])) {
244:             $lineNumber--;
245: 
246:             if ($lineNumber < 0) {
247:                 throw new Exception("Backtracked and found no comment in $file", 1);
248:             }
249:         }
250: 
251:         $comment = $lines[$lineNumber];
252:         $selector = '';
253: 
254:         
255:         while(!preg_match('/{/', $lines[$lineNumber])) {
256:             $lineNumber++;
257:             $selector .= $lines[$lineNumber];
258:         }
259: 
260:         
261:         $selector = preg_replace('/\n[^$]/', "\n    ", $selector);
262: 
263:         $rule = $params[0];
264:         $value = $params[1];
265: 
266:         
267:         if (!in_array($value, ThemeGenerator::getProfileKeys())) {
268:             $comment = preg_replace('/\/\*(.*)\*\//', '\1', $comment);
269:             throw new Exception("\nTheme Key '$value' is not a valid key.\nFound at$comment");
270:         }
271: 
272:         return array ( 
273:                 $selector, 
274:                 $comment, 
275:                 array (
276:                     'rule' => $rule, 
277:                     'value' => $value
278:                 )
279:             );
280: 
281:     }
282: 
283:     284: 285: 286: 287: 
288:     public function formatRule($selector, $rule, $addNoThemeRule=false) {
289:         
290:         $comments = $rule['comments'];
291:         unset($rule['comments']);
292:         $string = "\n";
293: 
294:         foreach($comments as $index => $comment) {
295:             $string .= "    $comment";
296:         }
297: 
298:         $string .= "    $selector";
299: 
300:         
301:         
302:         if ($addNoThemeRule)
303:             $string = preg_replace('/\ *{/', ':not(.no-theme) {', $string);
304: 
305:         foreach($rule as $value) {
306:             if (preg_match ('/_override$/', $value['value'])) {
307:                 $string .= "        ".
308:                     '".((isset ($colors[\''.$value['value'].'\']) && '.
309:                         '$colors[\''.$value['value'].'\']) ? '.
310:                             '"'. $value['rule'].': $colors['.$value['value'].']" : "")."'."\n";
311:             } else {
312:                 $string .= "        ".$value['rule'].': $colors['.$value['value']."]\n";
313:             }
314:         }
315:         $string .= "    }\n";
316: 
317:         return $string;
318:     }
319: 
320:     321: 322: 323: 
324:     public function formatModuleOverridesRule($selector, $rule) {
325:         static $rulesCache = array ();
326: 
327:         
328:         
329:         $selector = preg_replace ('/(page-title\.)[a-zA-Z0-9]+/', '$1{\$module}', $selector);
330:         $selector = preg_replace ('/(widget-title-bar\.)[a-zA-Z0-9]+/', '$1{\$module}', $selector);
331: 
332:         if (!isset ($rulesCache[$selector])) $rulesCache[$selector] = array ();
333: 
334:         
335:         $comments = $rule['comments'];
336:         unset($rule['comments']);
337:         $string = "\n";
338: 
339:         foreach($comments as $index => $comment) {
340:             $string .= "    $comment";
341:         }
342: 
343:         $string .= "    $selector";
344: 
345:         $foundRule = false;
346:         foreach($rule as $value) {
347:             if (preg_match ('/_override$/', $value['value'])) {
348:                 $templated = preg_replace (
349:                     '/^([a-zA-Z0-9]+_)+[^_]+(_override)$/', '$1{\$module}$2', $value['value']);
350:                 if (!isset ($rulesCache[$selector][$templated])) {
351:                     $foundRule = true;
352:                     
353:                     $string .= "        ".
354:                         '".((isset ($colors["'.$templated.'"]) && '.
355:                             '$colors["'.$templated.'"]) ? '.
356:                                 '"'. $value['rule'].': {$colors["'.$templated.'"]}" : "")."'."\n";
357:                     $rulesCache[$selector][$templated] = true;
358:                 }
359:             } 
360:         }
361:         $string .= "    }\n";
362: 
363:         if (!$foundRule) $string = '';
364:         return $string;
365:     }
366: 
367:     public function getHelp() {
368:         return "\nBuilds a php theme file from various @theme tags in all CSS files. \nUsage: themebuild [INPUT DIRECTORY] [OUTPUT FILE]\nOptions: themebuild --keys \n     This will list all the avaliable keys for theming.\n";
369:     }
370: 
371:     
372:     public function progressBar($amount) {
373:         echo "\r".($amount*100)."% |";
374:         for ($j = 0; $j < 10; $j++) {
375:             if($j <  $amount * 10) {
376:                 echo '-';
377:             } else {
378:                 echo ' ';
379:             }
380:         }
381:         echo '|';
382: }
383: 
384: }
385: 
386: ?>
387: