[ Index ] |
PHP Cross Reference of MyBB |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Import an entire theme (stylesheets, properties & templates) from an XML file. 4 * 5 * @param string The contents of the XML file 6 * @param array Optional array of options or overrides 7 * @return boolean True on success, false on failure 8 */ 9 function import_theme_xml($xml, $options=array()) 10 { 11 global $mybb, $db; 12 13 require_once MYBB_ROOT."inc/class_xml.php"; 14 15 $parser = new XMLParser($xml); 16 $tree = $parser->get_tree(); 17 18 if(!is_array($tree) || !is_array($tree['theme'])) 19 { 20 return -1; 21 } 22 23 $theme = $tree['theme']; 24 25 // Do we have MyBB 1.2 template's we're importing? 26 $css_120 = ""; 27 28 if(is_array($theme['cssbits'])) 29 { 30 $cssbits = kill_tags($theme['cssbits']); 31 32 foreach($cssbits as $name => $values) 33 { 34 $css_120 .= "{$name} {\n"; 35 foreach($values as $property => $value) 36 { 37 if(is_array($value)) 38 { 39 $property = str_replace('_', ':', $property); 40 41 $css_120 .= "}\n{$name} {$property} {\n"; 42 foreach($value as $property2 => $value2) 43 { 44 $css_120 .= "\t{$property2}: {$value2}\n"; 45 } 46 } 47 else 48 { 49 $css_120 .= "\t{$property}: {$value}\n"; 50 } 51 } 52 $css_120 .= "}\n"; 53 } 54 } 55 56 if(is_array($theme['themebits'])) 57 { 58 $themebits = kill_tags($theme['themebits']); 59 60 $theme['properties']['tag'] = 'properties'; 61 62 foreach($themebits as $name => $value) 63 { 64 if($name == "extracss") 65 { 66 $css_120 .= $value; 67 continue; 68 } 69 70 $theme['properties'][$name] = $value; 71 } 72 } 73 74 if($css_120) 75 { 76 $css_120 = upgrade_css_120_to_140($css_120); 77 $theme['stylesheets']['tag'] = 'stylesheets'; 78 $theme['stylesheets']['stylesheet'][0]['tag'] = 'stylesheet'; 79 $theme['stylesheets']['stylesheet'][0]['attributes'] = array('name' => 'global.css', 'version' => $mybb->version_code); 80 $theme['stylesheets']['stylesheet'][0]['value'] = $css_120; 81 82 unset($theme['cssbits']); 83 unset($theme['themebits']); 84 } 85 86 if(is_array($theme['properties'])) 87 { 88 foreach($theme['properties'] as $property => $value) 89 { 90 if($property == "tag" || $property == "value") 91 { 92 continue; 93 } 94 95 $properties[$property] = $value['value']; 96 } 97 } 98 99 if(empty($mybb->input['name'])) 100 { 101 $name = $theme['attributes']['name']; 102 } 103 else 104 { 105 $name = $mybb->input['name']; 106 } 107 $version = $theme['attributes']['version']; 108 109 $query = $db->simple_select("themes", "tid", "name='".$db->escape_string($name)."'", array("limit" => 1)); 110 $existingtheme = $db->fetch_array($query); 111 if($options['force_name_check'] && $existingtheme['tid']) 112 { 113 return -3; 114 } 115 else if($existingtheme['tid']) 116 { 117 $options['tid'] = $existingtheme['tid']; 118 } 119 120 if($mybb->version_code != $version && $options['version_compat'] != 1) 121 { 122 return -2; 123 } 124 125 // Do we have any templates to insert? 126 if(!empty($theme['templates']['template']) && !$options['no_templates']) 127 { 128 if($options['templateset']) 129 { 130 $sid = $options['templateset']; 131 } 132 else 133 { 134 $sid = $db->insert_query("templatesets", array('title' => $db->escape_string($name)." Templates")); 135 } 136 137 $templates = $theme['templates']['template']; 138 if(is_array($templates)) 139 { 140 // Theme only has one custom template 141 if(array_key_exists("attributes", $templates)) 142 { 143 $templates = array($templates); 144 } 145 } 146 147 $security_check = false; 148 $templatecache = array(); 149 foreach($templates as $template) 150 { 151 if(check_template($template['value'])) 152 { 153 $security_check = true; 154 break; 155 } 156 157 $templatecache[] = array( 158 "title" => $db->escape_string($template['attributes']['name']), 159 "template" => $db->escape_string($template['value']), 160 "sid" => $db->escape_string($sid), 161 "version" => $db->escape_string($template['attributes']['version']), 162 "dateline" => TIME_NOW 163 ); 164 } 165 166 if($security_check == true) 167 { 168 return -4; 169 } 170 171 foreach($templatecache as $template) 172 { 173 // PostgreSQL causes apache to stop sending content sometimes and 174 // causes the page to stop loading during many queries all at one time 175 if($db->engine == "pgsql") 176 { 177 echo " "; 178 flush(); 179 } 180 181 $db->insert_query("templates", $template); 182 } 183 184 $properties['templateset'] = $sid; 185 } 186 187 // Not overriding an existing theme 188 if(!$options['tid']) 189 { 190 // Insert the theme 191 $theme_id = build_new_theme($name, $properties, $options['parent']); 192 } 193 // Overriding an existing - delete refs. 194 else 195 { 196 $db->delete_query("themestylesheets", "tid='{$options['tid']}'"); 197 $db->update_query("themes", array("properties" => $db->escape_string(serialize($properties))), "tid='{$options['tid']}'"); 198 $theme_id = $options['tid']; 199 } 200 201 // If we have any stylesheets, process them 202 if(!empty($theme['stylesheets']['stylesheet']) && !$options['no_stylesheets']) 203 { 204 // Are we dealing with a single stylesheet? 205 if(isset($theme['stylesheets']['stylesheet']['tag'])) 206 { 207 // Trick the system into thinking we have a good array =P 208 $theme['stylesheets']['stylesheet'] = array($theme['stylesheets']['stylesheet']); 209 } 210 211 foreach($theme['stylesheets']['stylesheet'] as $stylesheet) 212 { 213 if(substr($stylesheet['attributes']['name'], -4) != ".css") 214 { 215 continue; 216 } 217 218 if(!$stylesheet['attributes']['lastmodified']) 219 { 220 $stylesheet['attributes']['lastmodified'] = TIME_NOW; 221 } 222 223 $new_stylesheet = array( 224 "name" => $db->escape_string($stylesheet['attributes']['name']), 225 "tid" => $theme_id, 226 "attachedto" => $db->escape_string($stylesheet['attributes']['attachedto']), 227 "stylesheet" => $db->escape_string($stylesheet['value']), 228 "lastmodified" => intval($stylesheet['attributes']['lastmodified']), 229 "cachefile" => $db->escape_string($stylesheet['attributes']['name']) 230 ); 231 $sid = $db->insert_query("themestylesheets", $new_stylesheet); 232 $css_url = "css.php?stylesheet={$sid}"; 233 $cached = cache_stylesheet($theme_id, $stylesheet['attributes']['name'], $stylesheet['value']); 234 if($cached) 235 { 236 $css_url = $cached; 237 } 238 239 $attachedto = $stylesheet['attributes']['attachedto']; 240 if(!$attachedto) 241 { 242 $attachedto = "global"; 243 } 244 245 // private.php?compose,folders|usercp.php,global|global 246 $attachedto = explode("|", $attachedto); 247 foreach($attachedto as $attached_file) 248 { 249 $attached_actions = explode(",", $attached_file); 250 $attached_file = array_shift($attached_actions); 251 if(count($attached_actions) == 0) 252 { 253 $attached_actions = array("global"); 254 } 255 256 foreach($attached_actions as $action) 257 { 258 $theme_stylesheets[$attached_file][$action][] = $css_url; 259 } 260 } 261 } 262 // Now we have our list of built stylesheets, save them 263 $updated_theme = array( 264 "stylesheets" => $db->escape_string(serialize($theme_stylesheets)) 265 ); 266 $db->update_query("themes", $updated_theme, "tid='{$theme_id}'"); 267 } 268 269 update_theme_stylesheet_list($theme_id); 270 271 // And done? 272 return $theme_id; 273 } 274 275 /** 276 * Parse theme variables in a specific string. 277 * 278 * @param string The string to parse variables for 279 * @param array Array of variables 280 * @return string Parsed string with variables replaced 281 */ 282 function parse_theme_variables($string, $variables=array()) 283 { 284 foreach(array_keys($variables) as $variable) 285 { 286 $find[] = "{{$variable}}"; 287 $replace[] = $variables[$variable]; 288 } 289 return str_replace($find, $replace, $string); 290 } 291 292 /** 293 * Caches a stylesheet to the file system. 294 * 295 * @param string The theme ID this stylesheet belongs to 296 * @param string The name of the stylesheet 297 * @param string The contents of the stylesheet 298 */ 299 function cache_stylesheet($tid, $filename, $stylesheet) 300 { 301 global $mybb; 302 303 $filename = str_replace('/', '', $filename); 304 $tid = intval($tid); 305 306 // If we're in safe mode save to the main theme folder by default 307 if($mybb->safemode) 308 { 309 $theme_directory = "cache/themes"; 310 $filename = $tid."_".$filename; 311 } 312 // Does our theme directory exist? Try and create it. 313 elseif(!is_dir(MYBB_ROOT."cache/themes/theme{$tid}")) 314 { 315 if(!@mkdir(MYBB_ROOT."cache/themes/theme{$tid}")) 316 { 317 $theme_directory = "cache/themes"; 318 $filename = $tid."_".$filename; 319 } 320 else 321 { 322 // Add in empty index.html! 323 $fp = @fopen(MYBB_ROOT."cache/themes/theme{$tid}/index.html", "w"); 324 @fwrite($fp, ""); 325 @fclose($fp); 326 327 $theme_directory = "cache/themes/theme{$tid}"; 328 } 329 } 330 // Seems like we're all good 331 else 332 { 333 $theme_directory = "cache/themes/theme{$tid}"; 334 } 335 336 $theme_vars = array( 337 "theme" => $theme_directory 338 ); 339 $stylesheet = parse_theme_variables($stylesheet, $theme_vars); 340 $stylesheet = preg_replace_callback("#url\((\"|'|)(.*)\\1\)#", create_function('$matches', 'return fix_css_urls($matches[2]);'), $stylesheet); 341 342 $fp = @fopen(MYBB_ROOT."{$theme_directory}/{$filename}", "wb"); 343 if(!$fp) 344 { 345 return false; 346 } 347 348 @fwrite($fp, $stylesheet); 349 @fclose($fp); 350 return "{$theme_directory}/{$filename}"; 351 } 352 353 function resync_stylesheet($stylesheet) 354 { 355 global $db; 356 357 // Try and fix any missing cache file names 358 if(!$stylesheet['cachefile'] && $stylesheet['name']) 359 { 360 $stylesheet['cachefile'] = $stylesheet['name']; 361 $db->update_query("themestylesheets", array('cachefile' => $db->escape_string($stylesheet['name'])), "sid='{$stylesheet['sid']}'", 1); 362 } 363 364 // Still don't have the cache file name or is it not a flat file? Return false 365 if(!$stylesheet['cachefile'] || strpos($stylesheet['cachefile'], 'css.php') !== false) 366 { 367 return false; 368 } 369 370 if(!file_exists(MYBB_ROOT."cache/themes/theme{$stylesheet['tid']}/{$stylesheet['name']}") && !file_exists(MYBB_ROOT."cache/themes/{$stylesheet['tid']}_{$stylesheet['name']}")) 371 { 372 if(cache_stylesheet($stylesheet['tid'], $stylesheet['cachefile'], $stylesheet['stylesheet']) !== false) 373 { 374 $db->update_query("themestylesheets", array('cachefile' => $db->escape_string($stylesheet['name'])), "sid='{$stylesheet['sid']}'", 1); 375 376 update_theme_stylesheet_list($stylesheet['tid']); 377 378 if($stylesheet['sid'] != 1) 379 { 380 $db->update_query("themestylesheets", array('lastmodified' => TIME_NOW), "sid='{$stylesheet['sid']}'", 1); 381 } 382 } 383 384 return true; 385 } 386 else if($stylesheet['sid'] != 1 && @filemtime(MYBB_ROOT."cache/themes/theme{$stylesheet['tid']}/{$stylesheet['name']}") > $stylesheet['lastmodified']) 387 { 388 $contents = unfix_css_urls(file_get_contents(MYBB_ROOT."cache/themes/theme{$stylesheet['tid']}/{$stylesheet['name']}")); 389 $db->update_query("themestylesheets", array('stylesheet' => $db->escape_string($contents), 'lastmodified' => TIME_NOW), "sid='{$stylesheet['sid']}'", 1); 390 return true; 391 } 392 393 return false; 394 } 395 396 function fix_css_urls($url) 397 { 398 if(!preg_match("#^([a-z0-9]+\:|/)#i", $url) && strpos($url, "../../../") === false) 399 { 400 return "url(../../../{$url})"; 401 } 402 else 403 { 404 return "url({$url})"; 405 } 406 } 407 408 function unfix_css_urls($url) 409 { 410 return str_replace("../../../", "", $url); 411 } 412 413 /** 414 * Build a theme based on the specified parameters. 415 * 416 * @param string The name of the theme 417 * @param array Array of theme properties (if blank, inherits from parent) 418 * @param int The parent ID for this theme (defaults to Master) 419 * @return int The new theme ID 420 */ 421 function build_new_theme($name, $properties=null, $parent=1) 422 { 423 global $db; 424 425 $new_theme = array( 426 "name" => $db->escape_string($name), 427 "pid" => intval($parent), 428 "def" => 0, 429 "allowedgroups" => "all", 430 "properties" => "", 431 "stylesheets" => "" 432 ); 433 $tid = $db->insert_query("themes", $new_theme); 434 435 if($parent > 0) 436 { 437 $query = $db->simple_select("themes", "*", "tid='".intval($parent)."'"); 438 $parent_theme = $db->fetch_array($query); 439 if(count($properties) == 0 || !is_array($properties)) 440 { 441 $parent_properties = unserialize($parent_theme['properties']); 442 if(!empty($parent_properties)) 443 { 444 foreach($parent_properties as $property => $value) 445 { 446 if($property == "inherited") 447 { 448 continue; 449 } 450 451 $properties[$property] = $value; 452 if($parent_properties['inherited'][$property]) 453 { 454 $properties['inherited'][$property] = $parent_properties['inherited'][$property]; 455 } 456 else 457 { 458 $properties['inherited'][$property] = $parent; 459 } 460 } 461 $inherited_properties = true; 462 } 463 } 464 465 if(count($stylesheets) == 0) 466 { 467 $parent_stylesheets = unserialize($parent_theme['stylesheets']); 468 if(!empty($parent_stylesheets)) 469 { 470 foreach($parent_stylesheets as $location => $value) 471 { 472 if($location == "inherited") 473 { 474 continue; 475 } 476 477 foreach($value as $action => $sheets) 478 { 479 foreach($sheets as $stylesheet) 480 { 481 $stylesheets[$location][$action][] = $stylesheet; 482 $inherited_check = "{$location}_{$action}"; 483 if($parent_stylesheets['inherited'][$inherited_check][$stylesheet]) 484 { 485 $stylesheets['inherited'][$inherited_check][$stylesheet] = $parent_stylesheets['inherited'][$inherited_check][$stylesheet]; 486 } 487 else 488 { 489 $stylesheets['inherited'][$inherited_check][$stylesheet] = $parent; 490 } 491 } 492 } 493 } 494 $inherited_stylesheets = true; 495 } 496 } 497 } 498 499 if(!$inherited_properties) 500 { 501 $theme_vars = array( 502 "theme" => "cache/themes/theme{$tid}" 503 ); 504 $properties['logo'] = parse_theme_variables($properties['logo'], $theme_vars); 505 } 506 $updated_theme['stylesheets'] = $db->escape_string(serialize($stylesheets)); 507 $updated_theme['properties'] = $db->escape_string(serialize($properties)); 508 509 if(count($updated_theme) > 0) 510 { 511 $db->update_query("themes", $updated_theme, "tid='{$tid}'"); 512 } 513 514 return $tid; 515 } 516 517 518 519 /** 520 * Generates an array from an incoming CSS file. 521 * 522 * @param string The incoming CSS 523 * @return array Parsed CSS file as array, false on failure 524 */ 525 function css_to_array($css) 526 { 527 // Normalise line breaks 528 $css = str_replace(array("\r\n", "\n", "\r"), "\n", $css); 529 530 /** 531 * Play with the css a little - just to ensure we can parse it 532 * 533 * This essentially adds line breaks before and after each } not inside a string 534 * so it's parsed correctly below 535 */ 536 $stripped_css = preg_replace('#(?<!\\")\}#', "\n}\n", $css); 537 538 // Fetch out classes and comments 539 preg_match_all('#(\/\*(.|[\r\n])*?\*\/)?([a-z0-9a+\\\[\]\-\"=_:>\*\.\#\,\s\(\)\|~|@\^]+)(\s*)\{(.*?)\}\n#msi', $stripped_css, $matches, PREG_PATTERN_ORDER); 540 $total = count($matches[1]); 541 542 for($i=0; $i < $total; $i++) 543 { 544 $name = $description = ''; 545 $class_name = $matches[3][$i]; 546 $class_name = trim($class_name); 547 $comments = $matches[1][$i]; 548 preg_match_all("#Name:(.*)#i", $comments, $name_match); 549 if(isset($name_match[count($name_match)-1][0])) 550 { 551 $name = trim($name_match[count($name_match)-1][0]); 552 } 553 preg_match_all("#Description:(.*)#i", $comments, $description_match); 554 if(isset($description_match[count($description_match)-1][0])) 555 { 556 $description = trim($description_match[count($description_match)-1][0]); 557 } 558 $class_id = md5($class_name); 559 if(isset($already_parsed[$class_id])) 560 { 561 $already_parsed[$class_id]++; 562 $class_id .= "_".$already_parsed[$class_id]; 563 } 564 else 565 { 566 $already_parsed[$class_id] = 1; 567 } 568 $values = trim($matches[5][$i]); 569 $values = preg_replace("#/\*(.*?)\*/#s", "", $values); 570 $parsed_css[$class_id] = array("class_name" => $class_name, "name" => $name, "description" => $description, "values" => $values); 571 } 572 573 return $parsed_css; 574 } 575 576 function get_selectors_as_options($css, $selected_item="") 577 { 578 $select = ""; 579 580 if(!is_array($css)) 581 { 582 $css = css_to_array($css); 583 } 584 585 $selected = false; 586 587 if(is_array($css)) 588 { 589 uasort($css, "css_selectors_sort_cmp"); 590 591 foreach($css as $id => $css_array) 592 { 593 if(!$css_array['name']) 594 { 595 $css_array['name'] = $css_array['class_name']; 596 } 597 598 if($selected_item == $id || (!$selected_item && !$selected)) 599 { 600 $select .= "<option value=\"{$id}\" selected=\"selected\">{$css_array['name']}</option>\n"; 601 $selected = true; 602 } 603 else 604 { 605 $select .= "<option value=\"{$id}\">{$css_array['name']}</option>\n"; 606 } 607 } 608 } 609 return $select; 610 } 611 612 function css_selectors_sort_cmp($a, $b) 613 { 614 if(!$a['name']) 615 { 616 $a['name'] = $a['class_name']; 617 } 618 619 if(!$b['name']) 620 { 621 $b['name'] = $b['class_name']; 622 } 623 return strcmp($a['name'], $b['name']); 624 } 625 626 function get_css_properties($css, $id) 627 { 628 if(!is_array($css)) 629 { 630 $css = css_to_array($css); 631 } 632 633 if(!isset($css[$id])) 634 { 635 return false; 636 } 637 return parse_css_properties($css[$id]['values']); 638 } 639 640 /** 641 * Parses CSS supported properties and returns them as an array. 642 * 643 * @param string Value of CSS properties from within class or selector 644 * @return array Array of CSS properties 645 */ 646 function parse_css_properties($values) 647 { 648 if(!$values) 649 { 650 return; 651 } 652 653 $values = explode(";", $values); 654 foreach($values as $value) 655 { 656 $value = trim($value); 657 if(!$value) continue; 658 list($property, $css_value) = explode(":", $value, 2); 659 $property = trim($property); 660 switch(strtolower($property)) 661 { 662 case "background": 663 case "color": 664 case "width": 665 case "font-family": 666 case "font-size": 667 case "font-weight": 668 case "font-style": 669 case "text-decoration": 670 $css_bits[$property] = trim($css_value); 671 break; 672 default: 673 $css_bits['extra'] .= "{$property}: ".trim($css_value).";\n"; 674 675 } 676 } 677 return $css_bits; 678 } 679 680 /** 681 * Inserts an incoming string of CSS in to an already defined document. If the class ID is not found, the CSS is appended to the file. 682 * 683 * @param string CSS we wish to insert at this location 684 * @param string The selector for this piece of CSS 685 * @param string The existing CSS if we have any 686 * @param string (Optional) The optional friendly class id value just incase the CSS is not found in the file 687 */ 688 function insert_into_css($new_css, $selector="", $css="", $class_id="") 689 { 690 $new_css = str_replace(array("\r\n", "\n", "\r"), "\n", $new_css); 691 692 // Build the new CSS properties list 693 $new_css = explode("\n", $new_css); 694 foreach($new_css as $css_line) 695 { 696 $generated_css .= "\t".trim($css_line)."\n"; 697 } 698 699 // Parse out the CSS 700 if($css) 701 { 702 $parsed_css = css_to_array($css); 703 } 704 705 if(!$class_id) 706 { 707 $class_id = $parsed_css[$selector]['class_name']; 708 } 709 710 // The specified class ID cannot be found, add CSS to end of file 711 if(!$css || !$parsed_css[$selector]) 712 { 713 return $css."{$class_id}\n{\n{$generated_css}\n}\n\n"; 714 } 715 // Valid CSS, swap out old, swap in new 716 else 717 { 718 $css = str_replace(array("\r\n", "\n", "\r"), "\n", $css); 719 $css = preg_replace('#(?<!\\")\}#', "}\n", $css); 720 $css = preg_replace("#^(?!@)\s*([a-z0-9a+\\\[\]\-\"=_:>\*\.\#\,\s\(\)\|~\^]+)(\s*)\{(\n*)#isu", "\n$1 {\n", $css); 721 $css = preg_replace("#\s{1,}\{#", " {", $css); 722 $existing_block = $parsed_css[$selector]; 723 724 $break = strrpos($selector, "_"); 725 if($break !== false) 726 { 727 $actual_occurance = intval(substr($selector, ($break+1))); 728 } 729 730 if(!$actual_occurance) 731 { 732 $actual_occurance = 1; 733 } 734 735 $occurance = 1; 736 $pos = 0; 737 do 738 { 739 $pos = strpos($css, "\n".$existing_block['class_name']." {", $pos); 740 if($pos === false) 741 { 742 break; 743 } 744 if($occurance == $actual_occurance) 745 { 746 // This is the part we want to replace, now we need to fetch the opening & closing braces 747 $opening = strpos($css, "{", $pos); 748 $closing = strpos($css, "}", $pos); 749 $css = substr_replace($css, "\n".$generated_css."\n", $opening+1, $closing-$opening-1); 750 break; 751 } 752 ++$occurance; 753 ++$pos; 754 } while($occurance <= $actual_occurance); 755 } 756 $css = preg_replace("#{\n*#s", "{\n", $css); 757 $css = preg_replace("#\s*\}\s*#", "\n}\n\n", $css); 758 return $css; 759 } 760 761 function copy_stylesheet_to_theme($stylesheet, $tid) 762 { 763 global $db; 764 765 $stylesheet['tid'] = $tid; 766 unset($stylesheet['sid']); 767 768 foreach($stylesheet as $key => $value) 769 { 770 $stylesheet[$db->escape_string($key)] = $db->escape_string($value); 771 } 772 773 $sid = $db->insert_query("themestylesheets", $stylesheet); 774 775 return $sid; 776 } 777 778 function update_theme_stylesheet_list($tid) 779 { 780 global $db; 781 782 $stylesheets = array(); 783 784 $child_list = make_child_theme_list($tid); 785 $parent_list = make_parent_theme_list($tid); 786 787 if(!is_array($parent_list)) 788 { 789 return false; 790 } 791 792 $tid_list = implode(',', $parent_list); 793 794 // Get our list of stylesheets 795 $query = $db->simple_select("themestylesheets", "sid,cachefile,attachedto,tid,lastmodified,name", "tid IN ({$tid_list})", array('order_by' => 'tid', 'order_dir' => 'desc')); 796 while($stylesheet = $db->fetch_array($query)) 797 { 798 if(!$stylesheets[$stylesheet['name']]) 799 { 800 if($stylesheet['tid'] != $tid) 801 { 802 $stylesheet['inherited'] = $stylesheet['tid']; 803 } 804 805 $stylesheets[$stylesheet['name']] = $stylesheet; 806 } 807 } 808 809 foreach($stylesheets as $name => $stylesheet) 810 { 811 $sid = $stylesheet['sid']; 812 $css_url = "css.php?stylesheet={$sid}"; 813 814 foreach($parent_list as $theme_id) 815 { 816 if(file_exists(MYBB_ROOT."cache/themes/theme{$theme_id}/{$stylesheet['name']}") && filemtime(MYBB_ROOT."cache/themes/theme{$theme_id}/{$stylesheet['name']}") >= $stylesheet['lastmodified']) 817 { 818 $css_url = "cache/themes/theme{$theme_id}/{$stylesheet['name']}"; 819 break; 820 } 821 } 822 823 $attachedto = $stylesheet['attachedto']; 824 if(!$attachedto) 825 { 826 $attachedto = "global"; 827 } 828 // private.php?compose,folders|usercp.php,global|global 829 $attachedto = explode("|", $attachedto); 830 foreach($attachedto as $attached_file) 831 { 832 $attached_actions = array(); 833 if(strpos($attached_file, '?') !== false) 834 { 835 $attached_file = explode('?', $attached_file); 836 $attached_actions = explode(",", $attached_file[1]); 837 $attached_file = $attached_file[0]; 838 } 839 840 if(count($attached_actions) == 0) 841 { 842 $attached_actions = array("global"); 843 } 844 845 foreach($attached_actions as $action) 846 { 847 $theme_stylesheets[$attached_file][$action][] = $css_url; 848 849 if($stylesheet['inherited']) 850 { 851 $theme_stylesheets['inherited']["{$attached_file}_{$action}"][$css_url] = $stylesheet['inherited']; 852 } 853 } 854 } 855 } 856 857 // Now we have our list of built stylesheets, save them 858 $updated_theme = array( 859 "stylesheets" => $db->escape_string(serialize($theme_stylesheets)) 860 ); 861 $db->update_query("themes", $updated_theme, "tid='{$tid}'"); 862 863 // Do we have any children themes that need updating too? 864 if(count($child_list) > 0) 865 { 866 foreach($child_list as $id) 867 { 868 update_theme_stylesheet_list($id); 869 } 870 } 871 872 return true; 873 } 874 875 function make_parent_theme_list($tid) 876 { 877 static $themes_by_parent; 878 879 $themes = array(); 880 if(!is_array($themes_by_parent)) 881 { 882 $theme_cache = cache_themes(); 883 foreach($theme_cache as $key => $theme) 884 { 885 if($key == "default") 886 { 887 continue; 888 } 889 890 $themes_by_parent[$theme['tid']][$theme['pid']] = $theme; 891 } 892 } 893 894 if(!isset($themes_by_parent[$tid])) 895 { 896 return false; 897 } 898 899 reset($themes_by_parent); 900 reset($themes_by_parent[$tid]); 901 902 $themes = array(); 903 904 foreach($themes_by_parent[$tid] as $key => $theme) 905 { 906 $themes[] = $theme['tid']; 907 $parents = make_parent_theme_list($theme['pid']); 908 909 if(is_array($parents)) 910 { 911 $themes = array_merge($themes, $parents); 912 } 913 } 914 915 return $themes; 916 } 917 918 function make_child_theme_list($tid) 919 { 920 static $themes_by_child; 921 922 $themes = array(); 923 if(!is_array($themes_by_child)) 924 { 925 $theme_cache = cache_themes(); 926 foreach($theme_cache as $key => $theme) 927 { 928 if($key == "default") 929 { 930 continue; 931 } 932 933 $themes_by_child[$theme['pid']][$theme['tid']] = $theme; 934 } 935 } 936 937 if(!is_array($themes_by_child[$tid])) 938 { 939 return; 940 } 941 942 $themes = array(); 943 944 foreach($themes_by_child[$tid] as $theme) 945 { 946 $themes[] = $theme['tid']; 947 $children = make_child_theme_list($theme['tid']); 948 949 if(is_array($children)) 950 { 951 $themes = array_merge($themes, $children); 952 } 953 } 954 955 return $themes; 956 } 957 958 function cache_themes() 959 { 960 global $db, $theme_cache; 961 962 if(empty($theme_cache) || !is_array($theme_cache)) 963 { 964 $query = $db->simple_select("themes", "*", "", array('order_by' => "pid, name")); 965 while($theme = $db->fetch_array($query)) 966 { 967 $theme['properties'] = unserialize($theme['properties']); 968 $theme['stylesheets'] = unserialize($theme['stylesheets']); 969 $theme_cache[$theme['tid']] = $theme; 970 971 if($theme['def'] == 1) 972 { 973 $theme_cache['default'] = $theme['tid']; 974 } 975 } 976 } 977 978 // Do we have no themes assigned as default? 979 if(!$theme_cache['default']) 980 { 981 $theme_cache['default'] = 1; 982 } 983 984 return $theme_cache; 985 } 986 987 function build_theme_list($parent=0, $depth=0) 988 { 989 global $mybb, $db, $table, $lang, $page; // Global $table is bad, but it will have to do for now 990 static $theme_cache; 991 992 $padding = $depth*20; // Padding 993 994 if(!is_array($theme_cache)) 995 { 996 $themes = cache_themes(); 997 $query = $db->query(" 998 SELECT style, COUNT(uid) AS users 999 FROM ".TABLE_PREFIX."users 1000 GROUP BY style 1001 "); 1002 while($user_themes = $db->fetch_array($query)) 1003 { 1004 if($user_themes['style'] == 0) 1005 { 1006 $user_themes['style'] = $themes['default']; 1007 } 1008 1009 if($themes[$user_themes['style']]['users'] > 0) 1010 { 1011 $themes[$user_themes['style']]['users'] += intval($user_themes['users']); 1012 } 1013 else 1014 { 1015 $themes[$user_themes['style']]['users'] = intval($user_themes['users']); 1016 } 1017 } 1018 1019 // Restrucure the theme array to something we can "loop-de-loop" with 1020 foreach($themes as $key => $theme) 1021 { 1022 if($key == "default") 1023 { 1024 continue; 1025 } 1026 1027 $theme_cache[$theme['pid']][$theme['tid']] = $theme; 1028 } 1029 $theme_cache['num_themes'] = count($themes); 1030 unset($themes); 1031 } 1032 1033 if(!is_array($theme_cache[$parent])) 1034 { 1035 return; 1036 } 1037 1038 foreach($theme_cache[$parent] as $theme) 1039 { 1040 $popup = new PopupMenu("theme_{$theme['tid']}", $lang->options); 1041 if($theme['tid'] > 1) 1042 { 1043 $popup->add_item($lang->edit_theme, "index.php?module=style-themes&action=edit&tid={$theme['tid']}"); 1044 $theme['name'] = "<a href=\"index.php?module=style-themes&action=edit&tid={$theme['tid']}\">".htmlspecialchars_uni($theme['name'])."</a>"; 1045 1046 // We must have at least the master and 1 other active theme 1047 if($theme_cache['num_themes'] > 2) 1048 { 1049 $popup->add_item($lang->delete_theme, "index.php?module=style-themes&action=delete&tid={$theme['tid']}&my_post_key={$mybb->post_code}", "return AdminCP.deleteConfirmation(this, '{$lang->confirm_theme_deletion}')"); 1050 } 1051 1052 if($theme['def'] != 1) 1053 { 1054 $popup->add_item($lang->set_as_default, "index.php?module=style-themes&action=set_default&tid={$theme['tid']}&my_post_key={$mybb->post_code}"); 1055 $set_default = "<a href=\"index.php?module=style-themes&action=set_default&tid={$theme['tid']}&my_post_key={$mybb->post_code}\"><img src=\"styles/{$page->style}/images/icons/make_default.gif\" alt=\"{$lang->set_as_default}\" style=\"vertical-align: middle;\" title=\"{$lang->set_as_default}\" /></a>"; 1056 } 1057 else 1058 { 1059 $set_default = "<img src=\"styles/{$page->style}/images/icons/default.gif\" alt=\"{$lang->default_theme}\" style=\"vertical-align: middle;\" title=\"{$lang->default_theme}\" />"; 1060 } 1061 $popup->add_item($lang->force_on_users, "index.php?module=style-themes&action=force&tid={$theme['tid']}&my_post_key={$mybb->post_code}", "return AdminCP.deleteConfirmation(this, '{$lang->confirm_theme_forced}')"); 1062 } 1063 $popup->add_item($lang->export_theme, "index.php?module=style-themes&action=export&tid={$theme['tid']}"); 1064 $table->construct_cell("<div class=\"float_right\">{$set_default}</div><div style=\"margin-left: {$padding}px;\"><strong>{$theme['name']}</strong></div>"); 1065 $table->construct_cell(my_number_format($theme['users']), array("class" => "align_center")); 1066 $table->construct_cell($popup->fetch(), array("class" => "align_center")); 1067 $table->construct_row(); 1068 1069 // Fetch & build any child themes 1070 build_theme_list($theme['tid'], ++$depth); 1071 } 1072 } 1073 1074 // returns an array which can be sent to generate_select_box() 1075 function build_theme_array($ignoretid = null, $parent=0, $depth=0) 1076 { 1077 global $mybb, $lang, $list; 1078 static $theme_cache; 1079 1080 if(!is_array($theme_cache)) 1081 { 1082 $themes = cache_themes(); 1083 // Restrucure the theme array to something we can "loop-de-loop" with 1084 foreach($themes as $key => $theme) 1085 { 1086 if($key == "default") 1087 { 1088 continue; 1089 } 1090 1091 $theme_cache[$theme['pid']][$theme['tid']] = $theme; 1092 } 1093 unset($theme); 1094 } 1095 1096 if(!is_array($theme_cache[$parent]) || $ignoretid === $parent) 1097 { 1098 return; 1099 } 1100 1101 foreach($theme_cache[$parent] as $theme) 1102 { 1103 if($ignoretid === $theme['tid']) 1104 { 1105 continue; 1106 } 1107 1108 $list[$theme['tid']] = str_repeat("--", $depth).$theme['name']; 1109 // Fetch & build any child themes 1110 build_theme_array($ignoretid, $theme['tid'], $depth+1); 1111 } 1112 1113 if(!$parent) 1114 { 1115 return $list; 1116 } 1117 } 1118 1119 function upgrade_css_120_to_140($css) 1120 { 1121 // Update our CSS to the new stuff in 1.4 1122 $parsed_css = css_to_array($css); 1123 1124 if(!is_array($parsed_css)) 1125 { 1126 return ""; 1127 } 1128 1129 foreach($parsed_css as $class_id => $array) 1130 { 1131 $parsed_css[$class_id]['values'] = str_replace('#eea8a1', '#ffdde0', $array['values']); 1132 $parsed_css[$class_id]['values'] = str_replace('font-family: Verdana;', 'font-family: Verdana, Arial, Sans-Serif;', $array['values']); 1133 1134 switch($array['class_name']) 1135 { 1136 case '.bottommenu': 1137 $parsed_css[$class_id]['values'] = str_replace('padding: 6px;', 'padding: 10px;', $array['values']); 1138 break; 1139 case '.expcolimage': 1140 $parsed_css[$class_id]['values'] .= "\n\tmargin-top: 2px;"; 1141 break; 1142 case '.toolbar_normal': 1143 case '.toolbar_hover': 1144 case '.toolbar_clicked': 1145 case '.pagenav': 1146 case '.pagenavbit': 1147 case '.pagenavbit a': 1148 case '.pagenavcurrent': 1149 case '.quote_header': 1150 case '.quote_body': 1151 case '.code_header': 1152 case '.code_body': 1153 case '.usercpnav': 1154 case '.usercpnav li': 1155 case '.usercpnav .pmfolders': 1156 case '.usercpnav li': 1157 case '.usercpnav li': 1158 unset($parsed_css[$class_id]); 1159 break; 1160 default: 1161 } 1162 } 1163 1164 $to_add = array( 1165 md5('.trow_selected td') => array("class_name" => '.trow_selected td', "values" => 'background: #FFFBD9;'), 1166 md5('blockquote') => array("class_name" => 'blockquote', "values" => "border: 1px solid #ccc;\n\tmargin: 0;\n\tbackground: #fff;\n\tpadding: 4px;"), 1167 md5('blockquote cite') => array("class_name" => 'blockquote cite', "values" => "font-weight: bold;\n\tborder-bottom: 1px solid #ccc;\n\tfont-style: normal;\n\tdisplay: block;\n\tmargin: 4px 0;"), 1168 md5('blockquote cite span') => array("class_name" => 'blockquote cite span', "values" => "float: right;\n\tfont-weight: normal;"), 1169 md5('.codeblock') => array("class_name" => '.codeblock', "values" => "background: #fff;\n\tborder: 1px solid #ccc;\n\tpadding: 4px;"), 1170 md5('.codeblock .title') => array("class_name" => '.codeblock .title', "values" => "border-bottom: 1px solid #ccc;\n\tfont-weight: bold;\n\tmargin: 4px 0;"), 1171 md5('.codeblock code') => array("class_name" => '.codeblock code', "values" => "overflow: auto;\n\theight: auto;\n\tmax-height: 200px;\n\tdisplay: block;\n\tfont-family: Monaco, Consolas, Courier, monospace;\n\tfont-size: 13px;"), 1172 md5('.subject_new') => array("class_name" => '.subject_new', "values" => "font-weight: bold;"), 1173 md5('.highlight') => array("class_name" => '.highlight', "values" => "background: #FFFFCC;\n\tpadding: 3px;"), 1174 md5('.pm_alert') => array("class_name" => '.pm_alert', "values" => "background: #FFF6BF;\n\tborder: 1px solid #FFD324;\n\ttext-align: center;\n\tpadding: 5px 20px;\n\tfont-size: 11px;"), 1175 md5('.red_alert') => array("class_name" => '.red_alert', "values" => "background: #FBE3E4;\n\tborder: 1px solid #A5161A;\n\tcolor: #A5161A;\n\ttext-align: center;\n\tpadding: 5px 20px;\n\tfont-size: 11px;"), 1176 md5('.high_warning') => array("class_name" => '.high_warning', "values" => "color: #CC0000;"), 1177 md5('.moderate_warning') => array("class_name" => '.moderate_warning', "values" => "color: #F3611B;"), 1178 md5('.low_warning') => array("class_name" => '.low_warning', "values" => "color: #AE5700;"), 1179 md5('div.error') => array("class_name" => 'div.error', "values" => "padding: 5px 10px;\n\tborder-top: 2px solid #FFD324;\n\tborder-bottom: 2px solid #FFD324;\n\tbackground: #FFF6BF\n\tfont-size: 12px;"), 1180 md5('.high_warning') => array("class_name" => '.high_warning', "values" => "color: #CC0000;"), 1181 md5('.moderate_warning') => array("class_name" => '.moderate_warning', "values" => "color: #F3611B;"), 1182 md5('.low_warning') => array("class_name" => '.low_warning', "values" => "color: #AE5700;"), 1183 md5('div.error') => array("class_name" => 'div.error', "values" => "padding: 5px 10px;\n\tborder-top: 2px solid #FFD324;\n\tborder-bottom: 2px solid #FFD324;\n\tbackground: #FFF6BF;\n\tfont-size: 12px;"), 1184 md5('div.error p') => array("class_name" => 'div.error p', "values" => "margin: 0;\n\tcolor: #000;\n\tfont-weight: normal;"), 1185 md5('div.error p em') => array("class_name" => 'div.error p em', "values" => "font-style: normal;\n\tfont-weight: bold;\n\tpadding-left: 24px;\n\tdisplay: block;\n\tcolor: #C00;\n\tbackground: url({$mybb->settings['bburl']}/images/error.gif) no-repeat 0;"), 1186 md5('div.error.ul') => array("class_name" => 'div.error.ul', "values" => "margin-left: 24px;"), 1187 md5('.online') => array("class_name" => '.online', "values" => "color: #15A018;"), 1188 md5('.offline') => array("class_name" => '.offline', "values" => "color: #C7C7C7;"), 1189 md5('.pagination') => array("class_name" => '.pagination', "values" => "font-size: 11px;\n\tpadding-top: 10px;\n\tmargin-bottom: 5px;"), 1190 md5('.tfoot .pagination, .tcat .pagination') => array("class_name" => '.tfoot .pagination, .tcat .pagination', "values" => "padding-top: 0;"), 1191 md5('.pagination .pages') => array("class_name" => '.pagination .pages', "values" => "font-weight: bold;"), 1192 md5('.pagination .pagination_current, .pagination a') => array("class_name" => '.pagination .pagination_current, .pagination a', "values" => "padding: 2px 6px;\n\tmargin-bottom: 3px;"), 1193 md5('.pagination a') => array("class_name" => '.pagination a', "values" => "border: 1px solid #81A2C4;"), 1194 md5('.pagination .pagination_current') => array("class_name" => '.pagination .pagination_current', "values" => "background: #F5F5F5;\n\tborder: 1px solid #81A2C4;\n\tfont-weight: bold;"), 1195 md5('.pagination a:hover') => array("class_name" => '.pagination a:hover', "values" => "background: #F5F5F5;\n\ttext-decoration: none;"), 1196 md5('.thread_legend, .thread_legend dd') => array("class_name" => '.thread_legend, .thread_legend dd', "values" => "margin: 0;\n\tpadding: 0;"), 1197 md5('.thread_legend dd') => array("class_name" => '.thread_legend dd', "values" => "padding-bottom: 4px;\n\tmargin-right: 15px;"), 1198 md5('.thread_legend img') => array("class_name" => '.thread_legend img', "values" => "margin-right: 4px;\n\tvertical-align: bottom;"), 1199 md5('.forum_legend, .forum_legend dt, .forum_legend dd') => array("class_name" => '.forum_legend, .forum_legend dt, .forum_legend dd', "values" => "margin: 0;\n\tpadding: 0;"), 1200 md5('.forum_legend dd') => array("class_name" => '.forum_legend dd', "values" => "float: left;\n\tmargin-right: 10px;"), 1201 md5('.forum_legend dt') => array("class_name" => '.forum_legend dt', "values" => "margin-right: 10px;\n\tfloat: left;"), 1202 md5('.success_message') => array("class_name" => '.success_message', "values" => "color: #00b200;\n\tfont-weight: bold;\n\tfont-size: 10px;\n\tmargin-bottom: 10px;"), 1203 md5('.error_message') => array("class_name" => '.error_message', "values" => "color: #C00;\n\tfont-weight: bold;\n\tfont-size: 10px;\n\tmargin-bottom: 10px;"), 1204 md5('.post_body') => array("class_name" => '.post_body', "values" => "padding: 5px;"), 1205 md5('.post_content') => array("class_name" => '.post_content', "values" => "padding: 5px 10px;"), 1206 md5('.invalid_field') => array("class_name" => '.invalid_field', "values" => "border: 1px solid #f30;\n\tcolor: #f30;"), 1207 md5('.valid_field') => array("class_name" => '.valid_field', "values" => "border: 1px solid #0c0;"), 1208 md5('.validation_error') => array("class_name" => '.validation_error', "values" => "background: url(images/invalid.gif) no-repeat center left;\n\tcolor: #f30;\n\tmargin: 5px 0;\n\tpadding: 5px;\n\tfont-weight: bold;\n\tfont-size: 11px;\n\tpadding-left: 22px;"), 1209 md5('.validation_success') => array("class_name" => '.validation_success', "values" => "background: url(images/valid.gif) no-repeat center left;\n\tcolor: #00b200;\n\tmargin: 5px 0;\n\tpadding: 5px;\n\tfont-weight: bold;\n\tfont-size: 11px;\n\tpadding-left: 22px;"), 1210 md5('.validation_loading') => array("class_name" => '.validation_loading', "values" => "background: url(images/spinner.gif) no-repeat center left;\n\tcolor: #555;\n\tmargin: 5px 0;\n\tpadding: 5px;\n\tfont-weight: bold;\n\tfont-size: 11px;\n\tpadding-left: 22px;"), 1211 ); 1212 1213 foreach($to_add as $class_id => $array) 1214 { 1215 if($already_parsed[$class_id]) 1216 { 1217 $already_parsed[$class_id]++; 1218 $class_id .= "_".$already_parsed[$class_id]; 1219 } 1220 else 1221 { 1222 $already_parsed[$class_id] = 1; 1223 } 1224 1225 $array['name'] = ""; 1226 $array['description'] = ""; 1227 1228 $parsed_css[$class_id] = $array; 1229 } 1230 1231 $css = ""; 1232 foreach($parsed_css as $class_id => $array) 1233 { 1234 if($array['name'] || $array['description']) 1235 { 1236 $theme['css'] .= "/* "; 1237 if($array['name']) 1238 { 1239 $array['css'] .= "Name: {$array['name']}"; 1240 1241 if($array['description']) 1242 { 1243 $array['css'] .= "\n"; 1244 } 1245 } 1246 1247 if($array['description']) 1248 { 1249 $array['css'] .= "Description: {$array['description']}"; 1250 } 1251 1252 $array['css'] .= " */\n"; 1253 } 1254 1255 $css .= "{$array['class_name']} {\n\t{$array['values']}\n}\n"; 1256 } 1257 1258 return $css; 1259 } 1260 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Oct 8 19:19:50 2013 | Cross-referenced by PHPXref 0.7.1 |