/** * smd_admin_themes * * A Textpattern CMS plugin for creating, editing, testing, installing and distributing admin-side themes * -> Employ images, CSS, JS, HTML/PHP in your theme * -> Use constants in your CSS files * -> Base your theme on an existing theme to tweak it to your taste * -> Export a theme package for distribution to the community in either .tar, .tgz, .bz2 or .zip format (requires smd_crunchers plugin) * -> Import / install other themes from the "community repository":http://textgarden.org/administration-themes * -> Administrator can set a default theme for all users, or enable per-user / per-role themes * * @author Stef Dawson * @link http://stefdawson.com/ */ if (@txpinterface == 'admin') { // Silence warnings because, although the plugin requires smd_crunchers for the // import/export features, it'll function in a reduced capacity without it @require_plugin('smd_crunchers'); global $smd_at_event, $smd_at_fullev, $smd_at_adm_event, $smd_at_privs, $smd_core_themes, $smd_at_prefs, $prefs, $smd_at_feedurl, $smd_at_theme_repo, $smd_at_styles; $smd_at_event = 'smd_at'; $smd_at_fullev = 'smd_admin_themes'; $smd_at_adm_event = 'smd_admat'; $smd_at_privs = '1'; $smd_at_feedurl = 'http://textgarden.org/admin-themes-feed'; $smd_at_theme_repo = 'http://textgarden.org/administration-themes'; $smd_at_styles = array( 'common' => '.smd_clear { clear:both; } .smd_smallprint { font-size:75%; text-align:center; margin-top:-12px; } .smd_hidden { display:none; margin:5px; }', 'control' => '#smd_control { margin:0 auto; } #smd_control td .publish { margin-left:50px; } #smd_control .upload-form { padding:0; } .smd_at_update { font-weight:bold; color:#990000; }', 'edit' => '#smd_at_images { margin:0px 6px -4px 0; } .smd_skin_filetypes { margin:12px 0; }', 'grid' => '.smd_grid { width:960px; margin:0 auto; } .smd_cell { float:left; padding:10px; margin:10px; border:1px ridge #ccc; width:{tw}px; height:{th}px; } .smd_cell.active { border:1px solid red; } .smd_cell:hover { background:#e2dfce; color:#80551e; } .smd_cell:hover a { color:black; } .smd_auth,.smd_actions { text-align:center; margin:5px 0; }', 'list' => '.smd_at_list tr.active, .smd_at_list tr.active a { background-color:#e2dfce; color:#80551e; }', 'setup' => '.smd_fakebtn { color:#555; cursor:pointer; } #smd_at_radio input { float:left; clear:both; } #smd_at_radio label { float:left; }', ); // Thumbnail dimensions $smd_core_themes = array('classic','remora'); add_privs($smd_at_event, $smd_at_privs); add_privs('plugin_prefs.'.$smd_at_fullev, $smd_at_privs); register_tab("extensions", $smd_at_event, smd_at_gTxt('tab_name')); register_callback('smd_at_options', 'plugin_prefs.'.$smd_at_fullev); register_callback('smd_at_welcome', 'plugin_lifecycle.'.$smd_at_fullev); register_callback('smd_at', $smd_at_event); register_callback('smd_at_per', 'admin_side', 'theme_name'); register_callback('smd_at_link', 'prefs_ui', 'theme_name'); register_callback('smd_at_upload_form', $smd_at_event.'_ui', 'upload_form'); if (isset($prefs['smd_at_system']) && $prefs['smd_at_system'] == 2) { add_privs($smd_at_adm_event, '2,3,4,5,6'); register_tab("admin", $smd_at_adm_event, smd_at_gTxt('skinner')); register_callback('smd_admat', $smd_at_adm_event); } // Double array: 1st is a list of current prefs and their defaults, // 2nd is a list of legacy prefs that may need removing in future (defaults: all empty) $smd_at_prefs = array( array( 'smd_at_case_sort' => '1', 'smd_at_crush' => 'zip', 'smd_at_tw' => '260', 'smd_at_th' => '150', 'smd_at_filename_format' => '{theme}', 'smd_at_layout' => '1', 'smd_at_system' => '0', 'smd_at_global_skin' => '', 'smd_at_group_list' => '', 'smd_at_user_list' => '', ), array( 'smd_at_file_path' => '', ) ); } // ------------------------ function smd_at($event, $step) { if(!$step or !in_array($step, array( 'smd_at_clone', 'smd_at_delete', 'smd_at_delete_image', 'smd_at_edit', 'smd_at_export', 'smd_at_import', 'smd_at_newfile', 'smd_at_prefs_install', 'smd_at_prefs_remove', 'smd_at_prefs_update', 'smd_at_rename', 'smd_at_save', 'smd_at_setup', 'smd_at_switch', 'smd_at_upload_image', 'smd_at_change_pageby', ))) { smd_at_list(''); } else $step(); } // ------------------------------------------------------------- function smd_at_welcome($event, $step) { $msg = ''; switch ($step) { case 'installed': smd_at_prefs_install(0); break; case 'deleted': smd_at_prefs_remove(0); break; } return $msg; } // ------------------------------------------------------------- function smd_at_link() { global $smd_at_event; return href(smd_at_gTxt('manage_lbl'), '?event='.$smd_at_event); } // A stub that can be called without a $message function smd_at_display() { smd_at_list(''); } // ------------------------ function smd_at_list($message='') { global $smd_at_event, $prefs, $smd_at_prefs, $theme, $smd_core_themes, $smd_at_fullev, $smd_at_feedurl, $smd_at_theme_repo, $smd_at_styles; $at_prefs = smd_at_get_prefs(); extract(gpsa(array('step', 'page', 'crit', 'search_method', 'skin'))); $message = ($message) ? $message : gps('message'); $layout = (isset($at_prefs['smd_at_layout'])) ? $at_prefs['smd_at_layout'] : 1; $stylerule = ($layout==0) ? 'list' : 'grid'; $tw = (isset($at_prefs['smd_at_tw']) ? $at_prefs['smd_at_tw'] : $smd_at_prefs[0]['smd_at_tw']) + 10; $th = (isset($at_prefs['smd_at_th']) ? $at_prefs['smd_at_th'] : $smd_at_prefs[0]['smd_at_th']) + 80; $stylereps = array( '{tw}' => $tw, '{th}' => $th, ); $helpLink = "?event=plugin&step=plugin_help&name=$smd_at_fullev#usage"; $pageby = (gps('qty')) ? gps('qty') : ((cs('smd_at_pageby')) ? cs('smd_at_pageby') : 15); $max_file_size = 150 * 1024; // a bit arbitrary! $curr_skin = (smd_at_exists($theme->name)) ? $theme->name : 'classic'; $skin_list = smd_at_read_skins(); $crushers = smd_at_crush_options('compress'); $uncrushers = smd_at_crush_options('decompress'); $bases = array(); // Get a list of ancestor themes (i.e. dependencies) foreach ($skin_list as $skina) { $manifest = smd_at_read_skinfo($skina); if ($manifest) { if ($manifest['based_on']) { $bases[] = $manifest['based_on']; } } } $bases = array_unique($bases); pagetop(smd_at_gTxt('manage_lbl'),$message); // Handle paging $total = count($skin_list); $limit = max(@$pageby, 15); list($page, $offset, $numPages) = pager($total, $limit, $page); $skin_list = array_slice($skin_list, $offset, $limit); $qs = array( "event" => $smd_at_event, "page" => $page, ); echo << function smd_getcrush(skin) { skinHandle = "crush_"+skin; jQuery('.smd_popup').each(function() { box = jQuery(this); if (box.attr("id") == skinHandle) { if (box.css("display") == "none") { box.slideDown('fast'); } else { box.slideUp('fast'); } } else { box.slideUp('fast'); } }); jQuery('#'+skinHandle+' a').click(function() { jQuery(this).attr("href", "?event={$smd_at_event}&skin="+skin+"&step=smd_at_export&crush="+jQuery('#'+skinHandle+' input[type=\'radio\']:checked').val()+"&page="+{$page}); }); return false; } function smd_getval(skin, step) { skinHandle = "pop_"+skin; jQuery('.smd_popup').each(function() { box = jQuery(this); if (box.attr("id") == skinHandle) { if (box.css("display") == "none") { box.slideDown('fast'); jQuery('#'+skinHandle+' input').focus(); } else { box.slideUp('fast'); } } else { box.slideUp('fast'); } }); jQuery('#'+skinHandle+' a').click(function() { jQuery(this).attr("href", "?event={$smd_at_event}&skin="+((skin=='new')? '' : skin)+"&step="+step+"&new_skin="+jQuery('#'+skinHandle+' input').val()+"&page="+{$page}); }); jQuery('#'+skinHandle+' input').keyup(function () { this.value = this.value.replace(/[^a-zA-Z0-9_\x7f-\xff]/g,''); while (this.value.length > 0 && this.value.substring(0,1).match(/[0-9]/)) { this.value = this.value.substring(1); } }); return false; } EOJS; // Inject styles echo ''; // List the available skins echo startTable('smd_control','','',5); echo tr( (($uncrushers) ? tda(upload_form(smd_at_gTxt('install_skin'), '', 'smd_at_import', $smd_at_event, '', $max_file_size).br.'
'.smd_at_gTxt('supported_import', array('{types}' => (($uncrushers) ? join(', ', $uncrushers) : gTxt('none')) )).'
', ' colspan="2"') : '') .tda(form(fInput('submit', 'submit', smd_at_gTxt('setup'), 'publish').sInput('smd_at_setup').eInput($smd_at_event)),' colspan="6" style="border:0;height:50px"') ); echo endTable(); echo '
'; $newbtn = ' '.''.smd_at_gTxt('new').'' . '
'.smd_at_gTxt('new_skin').' ['.smd_at_gTxt('confirm').']
'; $repobtn = ' '.''.smd_at_gTxt('find_theme').''; $hdrow = hed(smd_at_gTxt('list_title'). ' '. smd_at_gTxt('help_link', array('{link}' => $helpLink)), 3); if ($layout=="0") { echo startTable('list','','smd_at_list',5); echo tr( tda($hdrow.$repobtn.$newbtn, ' colspan="7"') ); echo tr( assHead( smd_at_gTxt('skin_gbl'), 'author', gTxt('version'), 'description', smd_at_gTxt('actions') ) ); } else { echo '
'.$hdrow.$repobtn.$newbtn.'
'; } if( is_callable('fsockopen') ) $transport = 'fsock'; elseif( is_callable('curl_init') ) { $transport = 'curl'; } else { $transport = ''; } $feed = ''; switch ($transport) { case 'curl': $c = curl_init(); curl_setopt($c, CURLOPT_URL, $smd_at_feedurl); curl_setopt($c, CURLOPT_REFERER, hu); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); curl_setopt($c, CURLOPT_VERBOSE, false); curl_setopt($c, CURLOPT_TIMEOUT, 10); $feed = curl_exec($c); break; case 'fsock': $url = parse_url($smd_at_feedurl); $fp = @fsockopen ($url['host'], 80, $errno, $errstr, 5); if ($fp) { $qry = 'GET '.$smd_at_feedurl; $qry .= " HTTP/1.0\r\n\r\n"; fputs($fp, $qry); stream_set_timeout($fp, 10); $info = stream_get_meta_data($fp); $hdrs = true; while ((!feof($fp)) && (!$info['timed_out'])) { $line = fgets($fp, 8192); $line = preg_replace("[\r\n]", "", $line); if ($hdrs == false) { $feed .= $line."\n"; } if (strlen($line) == 0) $hdrs = false; } if ($info['timed_out']) { $feed = ''; } fclose($fp); } else { $feed = ''; } break; default: $feed = ''; } $smd_at_vlast = ''; $feeds = array(); if (isset($prefs['smd_at_versions']) && isset($prefs['smd_at_vlast'])) { if (time() - $prefs['smd_at_vlast'] < 86400) { // Last updated within the last day, so read the cached version numbers $feeds = unserialize($prefs['smd_at_versions']); } } if (!$feeds || gps('read_versions')) { $feeds = smd_at_feed($feed); $installed_feed = array(); $feeds = (is_array($feeds)) ? $feeds : array(); // Only record the version info from themes that are installed foreach($skin_list as $skin_name) { $feed_skin_name = str_replace('_', '-', $skin_name); // Unsanitize the url_title if (array_key_exists($feed_skin_name, $feeds)) { $installed_feed[$feed_skin_name] = $feeds[$feed_skin_name]; } } set_pref('smd_at_vlast', time(), $smd_at_event, PREF_HIDDEN, 'text_input'); set_pref('smd_at_versions', serialize($installed_feed), $smd_at_event, PREF_HIDDEN, 'text_input'); } foreach ($skin_list as $skin_name) { // Read each manifest and set up the labels $skinfo = smd_at_read_skinfo($skin_name); $feed_skin_name = str_replace('_', '-', $skin_name); // Unsanitize for url_title matching $thumbnail = smd_at_get_thumb($skinfo, 1); $this_skin = ($skinfo) ? (($skin_name == $curr_skin) ? strong($skinfo['dname']) : $skinfo['dname']) : $skin_name; $is_gbl = (isset($at_prefs['smd_at_global_skin']) && $skin_name == $at_prefs['smd_at_global_skin']) ? smd_at_gTxt('is_global') : ''; $thumrow = ''.$this_skin.$is_gbl.(($skinfo && $skinfo['based_on']) ? smd_at_gTxt('based_on', array('{skin}' => $skinfo['based_on'])) : '').(($thumbnail) ? '
'.$thumbnail.'
' : '').'
'; $authrow = (($skinfo && strpos($skinfo['author_uri'], "http://") === 0) ? ''.$skinfo['author'].'' : (($skinfo) ? $skinfo['author'] : '-')); $actrow = '['.gTxt('edit').']' . ' ' .(($skinfo) ? '['.smd_at_gTxt('clone').']' : '') . ' ' .(($skinfo && $crushers) ? '['.smd_at_gTxt('export').']' : '') . ' ' .((in_array($skin_name, $smd_core_themes) || in_array($skin_name, $bases)) ? '' : '['.gTxt('delete').']') . '
'.smd_at_gTxt('new_cloneskin').'  ['.strong(smd_at_gTxt('confirm')).']
' . '
'.smd_at_gTxt('crush_format').''.radioSet($crushers, $skin_name.'_crush', ((isset($at_prefs['smd_at_crush'])) ? $at_prefs['smd_at_crush'] : '') ).' ['.strong(smd_at_gTxt('confirm')).']
'; $verow = ($skinfo) ? ((isset($feeds[$feed_skin_name]) && isset($feeds[$feed_skin_name]['summary']) && version_compare($feeds[$feed_skin_name]['summary'],$skinfo['version'])===1) ? ''.smd_at_gTxt('version').$skinfo['version'].'' : smd_at_gTxt('version').$skinfo['version']) : ''; if ($layout==0) { echo tr( td($thumrow) .td($authrow, 100) .td($verow) .td((($skinfo)? $skinfo['description'] : '-').(($skinfo && $skinfo['help']) ? ' '.href('['.gTxt('help').']', $skinfo['help']) : ''), 250) .td($actrow,260) , (($skin_name == $curr_skin) ? ' class="active"' : '') ); } else { echo '
'; echo '
'.$thumrow.'
'; echo '
'.$verow.smd_at_gTxt('by').$authrow.'
'; echo '
'.$actrow.'
'; echo '
'; } } if ($layout=="0") { echo ''; echo endTable(); } else { echo '
'; } echo '
'; echo n.nav_form($smd_at_event, $page, $numPages, NULL, NULL, NULL, NULL); echo n.pageby_form($smd_at_event, $pageby); } function smd_at_feed($feed) { global $feeds; $feed=preg_replace("/>"."[[:space:]]+"."<", $feed); // Kill whitespace in feed $xmlparser = xml_parser_create(); xml_set_element_handler($xmlparser, "smd_at_feed_start_tag", "smd_at_feed_end_tag"); xml_set_character_data_handler($xmlparser, "smd_at_feed_tag_contents"); xml_parse($xmlparser, $feed); xml_parser_free($xmlparser); return $feeds; } function smd_at_feed_start_tag($parser, $name, $attribs) { global $smd_at_feedin, $smd_at_feedtag; if ($name == "ENTRY") { $smd_at_feedin = true; } if ($smd_at_feedin) { $smd_at_feedtag = $name; } } function smd_at_feed_end_tag($parser, $name) { global $smd_at_feedin, $smd_at_feedtag, $smd_at_feedname; if ($name == "ENTRY") { $smd_at_feedin = false; $smd_at_feedname = ''; } } function smd_at_feed_tag_contents($parser, $data) { global $smd_at_feedin, $smd_at_feedtag, $smd_at_feedname, $feeds; if ($smd_at_feedin) { if ($smd_at_feedtag == "NAME") { $feeds[$data] = ''; $smd_at_feedname = $data; } if (isset($feeds[$smd_at_feedname])) { if ($smd_at_feedtag == "TITLE" || $smd_at_feedtag == "SUMMARY" || $smd_at_feedtag == "UPDATED" || $smd_at_feedtag == "ID" || $smd_at_feedtag == "LINK") { $feeds[$smd_at_feedname][strtolower($smd_at_feedtag)] = $data; } } } } // ------------------------ // Remove hard spaces in the upload_form so Opera and IE wrap the buttons function smd_at_upload_form($event, $step, $data, $args) { return str_replace(' ', ' ', $data); } // ------------------------ function smd_at_edit($message='') { global $smd_at_event, $smd_at_styles; extract(doSlash(gpsa(array('skin', 'new_skin', 'file')))); $at_prefs = smd_at_get_prefs(); $layout = (isset($at_prefs['smd_at_layout'])) ? $at_prefs['smd_at_layout'] : 1; $editable = array("ssc", "css", "js", "php"); $imagable = array("jpg", "jpeg", "gif", "png"); $max_file_size = 2 * 1024 * 1024; $skinfo = smd_at_read_skinfo($skin); $file = (empty($new_skin)) ? $file : $new_skin; $files = smd_at_listdir(THEME.$skin); sort($files); // Split the list into its directory groups $allfiles = array(); foreach ($files as $currfile) { $file_info = pathinfo($currfile); // Ignore any rogue Subversion files if (strpos($file_info['dirname'], '/.svn') === false) { $file_type = (isset($file_info['extension'])) ? $file_info['extension'] : 'other'; $allfiles[$file_type][] = $currfile; } } if (gps('smd_at_edits')) { $contents = str_replace('\r\n',' ',gps('smd_at_edits')); // newline workaround } else { $contents = ''; if ($file && file_exists(THEME.$skin.DS.$file)) { $contents = file(THEME.$skin.DS.$file); $contents = join("", $contents); } } // Begin rendering the page pagetop(smd_at_gTxt('edit_lbl'),$message); echo ''; $skin_list = smd_at_read_skins(); // selectInput needs both index and value to be the same in this case $skinsel = array(); foreach($skin_list as $key1 => $value1) { $skinsel[$value1] = $skin_list[$key1]; } $skinChange = '
' . eInput($smd_at_event) . sInput('smd_at_edit') . selectInput('skin', $skinsel, $skin, 0, 1) . '
'; $fileList = hed(smd_at_gTxt('skin_files'),2) . $skinChange . startTable('list', 'left', 'smd_skin_filetypes'); $fileList .= '
'.smd_at_gTxt('new_file').''; $fileList .= ' '.smd_at_gTxt((($layout=="0") ? 'skin_list' : 'skin_grid')).'
'; $others = array(); foreach ($editable as $filetype) { if (isset($allfiles[$filetype])) { foreach ($allfiles[$filetype] as $idx => $currfile) { if ($idx == 0) { $fileList .= tr(td(hed(smd_at_gTxt($filetype),3))); } $justfile = basename($currfile); $basefile = array_shift(explode ('.',$justfile)); $edit = ''.(($file == $justfile) ? strong($justfile) : $justfile).''; $del = ($basefile == $skin) ? '' : dLink($smd_at_event, 'smd_at_delete', 'file', $justfile, 1, 'skin', $skin); $fileList .= tr(td($edit).td($del)); } } else { // Some other file type if (!$others) { foreach ($allfiles as $ftype => $fls) { if (!in_array($ftype, array_merge($editable, $imagable))) { foreach ($allfiles[$ftype] as $idx => $currfile) { $justfile = basename($currfile); $basefile = array_shift(explode ('.',$justfile)); $edit = ''.(($file == $justfile) ? strong($justfile) : $justfile).''; $del = ($basefile == $skin) ? '' : dLink($smd_at_event, 'smd_at_delete', 'file', $justfile, 1, 'skin', $skin); $others[] = tr(td($edit).td($del)); } } } } } } $fileList .= ($others) ? tr(td(hed(smd_at_gTxt('other_files'),3))). join(n, $others) : ''; $fileList .= endTable(); // Make the list of images for this skin $qs = array( "event" => $smd_at_event, "step" => 'smd_at_delete_image', ); $sel[] = '
'; $sel[] = ''; $sel[] = dLink($smd_at_event, 'smd_at_delete_image', 'skin', $skin, 1); $sel[] = '
'; $sel = join('',$sel); // Image portion $imgbit = hed(smd_at_gTxt('skin_images'), 2) . startTable('list', 'left'); $imgbit .= tr(td($sel)); $imgbit .= tr(td(upload_form(smd_at_gTxt('upload_image'), '', 'smd_at_upload_image', $smd_at_event, $skin, $max_file_size, '', ''), 150)); $imgbit .= endTable(); // Render the rest of the page echo startTable('edit'). tr( tdtl($imgbit). td( hed((($skinfo) ? $skinfo['dname'] : $skin),1). form( ''.br .''.br .fInput('submit','',gTxt('save'),'publish') .eInput($smd_at_event).sInput('smd_at_save') .hInput('skin',$skin) .hInput('file',$file) ) ). tdtl($fileList) ). endTable(); } // ------------------------ // Just clear out the var so the save function treats the content as a new file function smd_at_newfile() { $_POST['file'] = ''; $message=''; smd_at_edit($message); } // ------------------------ function smd_at_clone() { global $txp_user; extract(doSlash(gpsa(array('skin', 'new_skin')))); $msg=''; $new_skin = trim($new_skin); $new_skin = rawurlencode($new_skin); if (empty($new_skin)) { $msg = smd_at_gTxt('name_empty'); } // Check the new name isn't already in use $themes = smd_at_read_skins(); if (in_array($new_skin, $themes)) { $msg = smd_at_gTxt('skin_exists', array('{name}' => $new_skin)); } // No errors, so make a new dir and create the PHP file if ($msg=='') { $content = ''; $destdir = THEME.$new_skin.DS; $res = mkdir($destdir); $based = ''; if ($skin) { // Based on an existing theme. No need for any functions other than manifest() $based = "theme::based_on('".$skin."');"; $extends = $skin.'_theme'; $srcss = THEME.$skin.DS.'textpattern.css'; if (file_exists($srcss) && filesize($srcss) > 0) { $import = join('', file($srcss)); } else { $import = '@import url("../classic/textpattern.css");'; } $content = '{'; } else { // Brand spanking new theme. Clone 'classic' functions then add new manifest() $extends = 'theme'; $import = ''; $src = txpath.DS.THEME.'classic'.DS.'classic.php'; $grab = false; $fp = file($src); foreach($fp as $line) { $line = rtrim($line); if (strpos($line, 'function manifest()') !== false) { $grab = false; // unnecessary? break; } if ($grab) { $content .= $line.n; } if (strpos($line, 'class classic_theme extends theme') !== false) { $grab = true; } } } // manifest fields $manuser = get_author_name($txp_user); $mantitle = ucwords($new_skin); // Write the new PHP file $buf = << '{$mantitle}', 'author' => '{$manuser}', 'author_uri' => '', 'version' => '1.0', 'description' => '', 'help' => '', ); } } ?> EOF; $fp = fopen($destdir.$new_skin.'.php', "wb"); fwrite($fp, $buf); fclose($fp); // Create the CSS file $fp = fopen($destdir.'textpattern.css', "wb"); fwrite($fp, $import); fclose($fp); if ($skin) { $msg = smd_at_gTxt('skin_cloned', array('{name}' => $new_skin));; } else { $msg = smd_at_gTxt('skin_created', array('{name}' => $new_skin));; } // $msg = smd_at_gTxt('skin_not_created', array('{name}' => $new_skin));; } smd_at_list($msg); } // ------------------------ function smd_at_crush_options($type='compress') { global $plugins; $smd_crushers = array(); if (is_array($plugins) && in_array('smd_crunchers', $plugins)) { $crunchers = smd_crunch_capabilities($type); foreach (array('tar', 'gzip', 'zip', 'bzip2') as $cm) { if (in_array($cm, $crunchers)) { $smd_crushers[$cm] = smd_at_gTxt('c_'.$cm); } } } return $smd_crushers; } // ------------------------ function smd_at_export() { global $prefs, $smd_at_prefs; extract(doSlash(gpsa(array('skin','crush')))); $message=''; chdir(THEME); $skinfo = smd_at_read_skinfo($skin); $reps = array( '{theme}' => $skin, '{version}' => $skinfo['version'], ); $out = strtr(get_pref('smd_at_filename_format', $smd_at_prefs[0]['smd_at_filename_format']), $reps); // Check the passed compression format is valid $crushers = smd_at_crush_options('compress'); if ($crush=='' || $crush=='undefined' || !array_key_exists($crush, $crushers)) { $msg = array(smd_at_gTxt('unsupported_compressiontype', array('{crush}' => $crush)), E_WARNING); smd_at_list($message); return; } switch($crush) { case 'zip': $zip = new smd_crunch_zip_file($out.'.zip'); break; case 'bzip2': $zip = new smd_crunch_bzip_file($out.'.tbz2'); break; case 'tar': $zip = new smd_crunch_tar_file($out.'.tar'); break; case 'gzip': $zip = new smd_crunch_gzip_file($out.'.tgz'); break; } $zip->set_options(array('basedir' => txpath.DS.THEME, 'overwrite' => 1, 'inmemory' => 1)); $zip->add_files($skin.DS.'*.*'); $zip->exclude_files($skin.DS.'*.svn*'); $zip->create_archive(); $zip->download_file(); } // ------------------------ function smd_at_import() { global $prefs; $name = $_FILES['thefile']['name']; $file = get_uploaded_file($_FILES['thefile']['tmp_name'], txpath.DS.THEME.basename($_FILES['thefile']['tmp_name'])); $basedir = txpath.DS.THEME; $uncrushers = smd_at_crush_options('decompress'); $message=''; $info = explode ('.',$name); $lastpart = count($info)-1; $ext = $info[$lastpart]; if ($name && $file) { if ($ext == 'zip' && array_key_exists('zip', $uncrushers)) { $zip = new smd_crunch_dUnzip2($file); // $zip->debug = true; $zip->getList(); $validArchive = smd_at_check_archive($zip->compressedList, $ext); if ($validArchive) { $zip->unzipAll($basedir); $zip->close(); $message = smd_at_gTxt('import_ok'); } else { $message = array(smd_at_gTxt('import_failed'), E_WARNING); } } else if ($ext == 'tgz' && array_key_exists('gzip', $uncrushers)) { $zip = new smd_crunch_gzip_file(basename($file)); $zip->set_options(array('basedir' => $basedir, 'overwrite' => 1)); $zip->extract_files(); $message = smd_at_gTxt('import_ok'); } else if ($ext == 'tbz2' && array_key_exists('bzip2', $uncrushers)) { $zip = new smd_crunch_bzip_file(basename($file)); $zip->set_options(array('basedir' => $basedir, 'overwrite' => 1)); $zip->extract_files(); $message = smd_at_gTxt('import_ok'); } else if ($ext == 'tar') { $zip = new smd_crunch_tar_file(basename($file)); $zip->set_options(array('basedir' => $basedir, 'overwrite' => 1)); $zip->extract_files(); $message = smd_at_gTxt('import_ok'); } else { $message = array(smd_at_gTxt('unsupported_compressiontype', array('{crush}' => $ext)), E_WARNING); } // Remove the temporary file unlink($file); } else { $message = smd_at_gTxt('empty_info'); } smd_at_list($message); } // Check the directory structure of the archive for various items/attributes. // Each time one of the conditions is met, the vcnt is incrememented. // Once the whole archive has been read, if the vcnt matches the expectedCounts for // that type, the archive is valid. function smd_at_check_archive($struct, $type='zip') { $expectedCounts = array('zip' => 1, 'tgz' => 0, 'tbz2' => 0, 'tar' => 0); switch ($type) { case "zip": $vcnt = 0; foreach ($struct as $entry => $info) { $parts = pathinfo($info['file_name']); if (isset($parts['extension']) && $parts['extension'] == 'php') { $dirbits = explode(DS, $parts['dirname']); $numdirs = count($dirbits); if (($parts['filename'] == $dirbits[$numdirs-1]) && $numdirs==1) { $vcnt++; } } } break; } return ($expectedCounts[$type] == $vcnt) ? true : false; } // ------------------------ function smd_at_upload_image() { global $file_max_upload_size; extract(doSlash(gpsa(array('id')))); $_POST['skin'] = $skin = $id; $name = $_FILES['thefile']['name']; $file = get_uploaded_file($_FILES['thefile']['tmp_name']); $message=''; if ($file === false) { $message = gTxt('file_upload_failed') ." $name - ".upload_get_errormsg($_FILES['thefile']['error']); smd_at_edit($message); exit(); } $size = filesize($file); if ($file_max_upload_size < $size) { unlink($file); $message = gTxt('file_upload_failed') ." $name - ".upload_get_errormsg(UPLOAD_ERR_FORM_SIZE); smd_at_edit($message); exit(); } $skinfo = smd_at_read_skinfo($skin); $new_skin = $skinfo['name']; $newpath = $skinfo['path']; if ($new_skin && $file) { if(shift_uploaded_file($file, $newpath.$name)) { $message = smd_at_gTxt('upload_success'); } else { unlink($file); $message = smd_at_gTxt('upload_failed'); } } else { $message = smd_at_gTxt('empty_info'); } smd_at_edit($message); } // ------------------------ function smd_at_delete_image() { global $smd_core_themes; extract(doSlash(gpsa(array('skin')))); $file_list = gps('smd_at_images'); $message=''; if (empty($file_list)) { $message = smd_at_gTxt('empty_info'); } else { if (in_array($skin, $smd_core_themes)) { $message = smd_at_gTxt('core_theme_file', array('{skin}' => $skin)); } else { foreach ($file_list as $image) { $path = txpath.DS.$image; unlink($path); } } } smd_at_edit($message); } // ------------------------ function smd_at_delete() { global $smd_core_themes, $theme, $smd_at_privs, $smd_at_event; extract(doSlash(gpsa(array('skin', 'file')))); $message=''; if ($file) { // Editing a skin, thus deleting a single file if (in_array($skin, $smd_core_themes)) { $message = smd_at_gTxt('core_theme_file', array('{skin}' => $skin)); smd_at_edit($message); } else { $del = unlink(THEME.$skin.DS.$file); if ($del) { $message = gTxt('file_deleted', array('{name}' => $file)); $_POST['file'] = ''; // Clear the variable so the filename doesn't appear on the edit page } else { $message = smd_at_gTxt('file_not_deleted', array('{name}' => $file)); } } smd_at_edit($message); } else { if (in_array($skin, $smd_core_themes)) { $message = smd_at_gTxt('core_theme', array('{skin}' => $skin)); smd_at_list($message); } else { // Viewing the skin list, thus deleting the whole skin $skindir = THEME.$skin; if (!smd_rmdir_recursive($skindir)) { $message = smd_at_gTxt('delete_failed'); } else { $message = smd_at_gTxt('skin_deleted', array('{name}' => $skin)); } // Handle situations if current skin has been deleted if (!file_exists(txpath.DS.THEME.$skin)) { $at_prefs = smd_at_get_prefs(); // Reset the global skin in case it's been deleted $gbl_skin = (isset($at_prefs['smd_at_global_skin'])) ? $at_prefs['smd_at_global_skin'] : ''; $gbl_skin = ($gbl_skin != '' && file_exists(txpath.DS.THEME.$gbl_skin)) ? $gbl_skin : 'classic'; set_pref('smd_at_global_skin', $gbl_skin, $smd_at_event, PREF_HIDDEN, 'text_input'); set_pref('theme_name', $gbl_skin); $skin = get_pref('smd_skin'); if (!file_exists(txpath.DS.THEME.$skin)) { $skin = $gbl_skin; } set_pref('smd_skin', $skin, 'smd_at', PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); } $_GET['skin'] = $skin; $_GET['nextstep'] = 'smd_at_list'; smd_at_switch($message); } } } // ------------------------ function smd_at_save() { extract(doSlash(gpsa(array('skin','new_skin','file','smd_at_edits')))); $editable = array("ssc", "css", "js", "php"); $message = $extraMsg = ''; $file = trim($file); $new_skin = trim($new_skin); $msglev = 0; // New file if (!$file && $new_skin) { $file = $new_skin; $ext = array_pop(explode ('.',$file)); if (strtolower($file) != 'readme' && ($ext == '' || !in_array($ext, $editable))) { $message = smd_at_gTxt('unsupported_filetype').smd_at_gTxt('unsupported_fudge'); $msglev = E_WARNING; $_POST['file'] = $file; } } if ($message=='') { $fname = sanitizeForFile($file); $new_skin = rawurlencode($new_skin); $_POST['new_skin'] = $new_skin; $smd_at_edits = doStrip(str_replace('\r\n',' ',$smd_at_edits)); // newline workaround if ($fname) { $filepath = THEME.$skin.DS; if (!file_exists($filepath.$fname)) { $ret = touch($filepath.$fname); if ($ret === false) { $message = smd_at_gTxt('mkdir_failed', array('{name}' => $fname)); } } // Assuming no errors so far... if ($message == '') { $fh = fopen($filepath.$fname, 'wb'); fwrite($fh, $smd_at_edits); fclose($fh); if ($fname != $new_skin) { // Rename the file $res = rename($filepath.$fname, $filepath.$new_skin); // If the renamed file is the theme's PHP file, try to rename the main skin dir too $filebits = explode('.', $new_skin); $oldfilebits = explode('.', $fname); $ext = array_pop($filebits); if ($res && $ext == 'php' && ($oldfilebits[0] == $skin)) { $new_filepath = THEME.$filebits[0].DS; $ren = rename($filepath, $new_filepath); $filepath = ($ren) ? $new_filepath : $filepath; $_POST['skin'] = $filebits[0]; } $extraMsg = ($res) ? smd_at_gTxt('renamed', array('{name}' => $new_skin)) : smd_at_gTxt('rename_failed'); } // Process any css replacements $filebits = explode ('.',(($fname==$new_skin)? $fname : $new_skin)); $cssfile = $filebits[0]; $ext = array_pop($filebits); if ($ext == 'ssc') { $replacements = array(); $num_rep1 = preg_match_all('/(\@[A-Za-z0-9_]+):\s*(.*);/', $smd_at_edits, $matches1); $num_rep2 = preg_match_all('/(\@[A-Za-z0-9_]+):\s*\{(.*?)\}/s', $smd_at_edits, $matches2); // Simple replacements foreach($matches1 as $idx => $reparr) { if ($idx == 0) { // Remove the var definitions foreach ($reparr as $rep) { $smd_at_edits = str_replace($rep, '',$smd_at_edits); } } if ($idx == 1) { foreach ($reparr as $jdx => $rep) { $replacements[$rep] = $matches1[2][$jdx]; } } } // Multi-line replacements foreach($matches2 as $idx => $reparr) { if ($idx == 0) { // Remove the var definitions foreach ($reparr as $rep) { $smd_at_edits = str_replace($rep, '',$smd_at_edits); } } if ($idx == 1) { foreach ($reparr as $jdx => $rep) { $replacements[$rep.';'] = trim($matches2[2][$jdx]); } } } // Generate and write the new css file $smd_at_edits = trim(strtr($smd_at_edits, $replacements)); $fh = fopen($filepath.$cssfile.'.css', 'wb'); fwrite($fh, $smd_at_edits); fclose($fh); if ($fname != $new_skin) { $oldfname = explode ('.', $fname); $oldfname = $oldfname[0].'.css'; $res = @unlink($filepath.$oldfname); } } $message = smd_at_gTxt('file_saved', array('{name}' => $fname)).br.$extraMsg; } } } smd_at_edit(array($message, $msglev)); } // ------------------------ // PREFS // ------------------------ function smd_at_get_prefs() { global $smd_at_prefs; $prefkeys = doQuote(implode("','",array_keys($smd_at_prefs[0]))); $rs = safe_rows('name,val','txp_prefs','name in ('.$prefkeys.')'); $at_prefs = array(); foreach ($rs as $prefarr) { $at_prefs[$prefarr['name']] = $prefarr['val']; } return $at_prefs; } // ------------------------ function smd_at_options($event, $step, $message='') { smd_at_setup($message); } // ------------------------ function smd_at_setup($message='') { global $prefs, $smd_at_prefs, $smd_at_event, $theme, $smd_core_themes, $smd_at_styles; $numReqPrefs = count($smd_at_prefs[0]); $at_prefs = smd_at_get_prefs(); $numRows = count($at_prefs); pagetop(gTxt('prefs'),$message); // TODO: use get_groups(); $levels = array( // 1 => gTxt('publisher'), 2 => gTxt('managing_editor'), 3 => gTxt('copy_editor'), 4 => gTxt('staff_writer'), 5 => gTxt('freelancer'), 6 => gTxt('designer'), ); $numLevs = count($levels); // Split the user skin list $uskins = array(); if (isset($at_prefs['smd_at_user_list'])) { $uskins = explode(',', $at_prefs['smd_at_user_list']); } $layout = (isset($at_prefs['smd_at_layout'])) ? $at_prefs['smd_at_layout'] : 1; $clrBtn = ' ['.smd_at_gTxt('clear').']'; $btnSet = fInput('submit', 'submit', smd_at_gTxt('set'), 'publish'); $btnSave = fInput('submit', 'submit', gTxt('save'), 'publish'); $btnInstall = '
'.fInput('submit', 'submit', gTxt('install'), 'publish').'
'; $btnRemove = '
'.fInput('submit', 'submit', gTxt('delete'), 'smallerbox').'
'; $btnList = ' '.smd_at_gTxt((($layout=="0") ? 'skin_list' : 'skin_grid')).'
'; $btnStyle = ' style="border:0;height:25px"'; $radBtns = array( smd_at_gTxt('per_site'), smd_at_gTxt('per_group'), smd_at_gTxt('per_user'), ); echo startTable('list'); if ($numRows == $numReqPrefs) { // Prefs all installed $skin_list = smd_at_read_skins(); // selectInput needs both index and value to be the same in this case $skinsel = array(); foreach($skin_list as $key1 => $value1) { if (smd_at_exists($value1)) { $skinsel[$value1] = $skin_list[$key1]; } } $gbl_skin = (isset($at_prefs['smd_at_global_skin']) && $at_prefs['smd_at_global_skin'] != '') ? $at_prefs['smd_at_global_skin'] : $theme->name; $crushers = smd_at_crush_options('compress'); echo tr(tda(strong(smd_at_gTxt('prefs_title')), ' colspan="2"') . tda($btnRemove.$btnList, $btnStyle) ); echo '
'; echo tr(tda(smd_at_gTxt('case_sort')).tda(yesnoRadio('smd_at_case_sort', $at_prefs['smd_at_case_sort']), ' width="240" id="smd_at_case_sort"')); if ($crushers) { echo tr(tda(smd_at_gTxt('crush_type')).tda(radioSet($crushers, 'smd_at_crush', $at_prefs['smd_at_crush']), ' width="240" id="smd_at_crush"')); } echo tr(tda(smd_at_gTxt('layout')).tda(radioSet(array(smd_at_gTxt('layout_list'), smd_at_gTxt('layout_grid')), 'smd_at_layout', $at_prefs['smd_at_layout']), ' width="240" id="smd_at_layout"')); echo tr(tda(smd_at_gTxt('thumbsize')).tda(fInput('text', 'smd_at_tw', $at_prefs['smd_at_tw'],'','','',4).smd_at_gTxt('times').fInput('text', 'smd_at_th', $at_prefs['smd_at_th'],'','','',4))); echo tr(tda(smd_at_gTxt('filename_format')).tda(fInput('text', 'smd_at_filename_format', $at_prefs['smd_at_filename_format']))); echo tr( td(smd_at_gTxt('global_skin')) .td(selectInput('smd_at_global_skin', $skinsel, $gbl_skin, 0, 0)) ); echo tr(tda(smd_at_gTxt('skin_system')).tda(radioSet($radBtns, 'smd_at_system', $at_prefs['smd_at_system']), ' width="240" id="smd_at_radio"')); // Option 1 $priv_list = ''; // Note the hidden skin group box; jQuery keeps track of any list changes and keeps it updated $sela = smd_at_gTxt('skin_groups'); $selb = hInput('smd_at_group_list', $at_prefs['smd_at_group_list']); $selb .= ''; echo tr(tda($sela, ' class="smd_at_sel1 smd_hidden"') . tda($selb.$priv_list, ' width="400" class="smd_at_sel1 smd_hidden"')); // Option 2 $sela = smd_at_gTxt('allowed_skins'); $selb = ''.$clrBtn; echo tr(tda($sela, ' class="smd_at_sel2 smd_hidden"') . tda($selb, ' width="240" class="smd_at_sel2 smd_hidden"')); echo tr(tda($btnSave, $btnStyle)); echo '
'; } else if ($numRows > 0 && $numRows < $numReqPrefs) { echo tr(tda(strong(smd_at_gTxt('prefs_some')).br.br .smd_at_gTxt('prefs_some_explain').br.br .smd_at_gTxt('prefs_some_options'), ' colspan="2"')); echo tr(tda($btnRemove,$btnStyle) . tda($btnInstall, $btnStyle)); } else { echo tr(tda(smd_at_gTxt('prefs_not_installed'), ' colspan="2"')); echo tr(tda($btnInstall, $btnStyle)); } echo endTable(); echo << // Concatenate checkbox options for storage function smd_at_prefswap(selValue) { for (idx=0; idx < 3; idx++) { if (idx==selValue) { jQuery(".smd_at_sel"+idx).show(); } else { jQuery(".smd_at_sel"+idx).hide(); } } } jQuery(function() { jQuery("input[name='smd_at_system']").change(function() { smd_at_prefswap(this.value); }); jQuery('#smd_clr').click(function() { jQuery('#smd_at_user_list option').attr("selected", ""); }); jQuery("#smd_at_grps").change(function() { selskin = jQuery(this).val(); // Clear the old privs list and grab the current list from the hidden field jQuery("#smd_at_privs option").attr("selected",false); var grplist = jQuery("input[name='smd_at_group_list']").val(); var grps = grplist.split(','); var cnt = grps.length; for (var idx = 0; idx < cnt; idx++) { privs = grps[idx].split(':'); skinid = privs.shift(); if (skinid == selskin) { numPrivs = privs.length; for (jdx = 0; jdx < numPrivs; jdx++) { jQuery("#smd_at_privs option[value='"+privs[jdx]+"']").attr("selected", true); } } } }); jQuery("#smd_at_privs").change(function() { selskin = jQuery("#smd_at_grps option:selected").val(); var out = []; var privs = []; privs.push(selskin); jQuery("#smd_at_privs option:selected").each(function() { privs.push(jQuery(this).val()); }); if (privs.length == 1) { privs.pop(); } else { out.push(privs.join(":")); } var hidlist = jQuery("input[name='smd_at_group_list']").val(); var prevlist = hidlist.split(','); var prevcnt = prevlist.length; jQuery("#smd_at_grps option").each(function() { currskin = jQuery(this).val(); if (currskin != selskin) { for (var idx = 0; idx < prevcnt; idx++) { prevprivs = prevlist[idx].split(':'); skinid = prevprivs.shift(); // Generate the priv list for the currently selected skin, removing any duplicate privs set elsewhere if (skinid == currskin) { tmpout = []; tmpout.push(skinid); for (thispriv in prevprivs) { if (jQuery.inArray(prevprivs[thispriv], privs) < 0) { tmpout.push(prevprivs[thispriv]); } } if (tmpout.length > 1) { out.push(tmpout.join(":")); } } } } }); jQuery("input[name='smd_at_group_list']").val(out); }); // Display a tooltip showing the currently-assigned skin when hovering over a privilege level jQuery("#smd_at_privs option").hover(function() { hovitem = jQuery(this).val(); grps = jQuery("input[name='smd_at_group_list']").val(); items = grps.split(","); for (idx = 0; idx < items.length; idx++) { skinz = items[idx].split(":"); for (jdx = 1; jdx < skinz.length; jdx++) { if (skinz[jdx] == hovitem) { jQuery(this).attr("title", skinz[0]); break; } } } }, function() { jQuery(this).attr("title", ""); }); smd_at_prefswap(jQuery("input[name='smd_at_system']:checked").val()); jQuery("#smd_at_privs").change(); }); EOJS; echo ''; } // ------------------------ // Split a multi-char preference value into an array of keys/values. // Note: value is passed in and not read directly from the prefs array in this function - intentionally function smd_at_pref_explode($val) { $order = array_values(array(smd_at_gTxt('c_gzip'),smd_at_gTxt('c_bzip'),smd_at_gTxt('c_zip'))); $onoff = array_values(preg_split('//', $val, -1, PREG_SPLIT_NO_EMPTY)); $out = array(); foreach($order as $key1 => $value1) { $out[(string)$value1] = $onoff[$key1]; } return $out; } // ------------------------------------------------------------- function smd_at_prefs_install($showpane='1') { global $smd_at_prefs; $message = ''; foreach ($smd_at_prefs[0] as $pref => $dflt) { if (!fetch('name','txp_prefs','name',$pref)) { $id = safe_insert('txp_prefs','prefs_id=1, name='.doQuote(doSlash($pref)).', val='.doQuote(doSlash($dflt)).',event="smd_adskin"'); } } if ($showpane) { $message = smd_at_gTxt('prefs_installed'); smd_at_setup($message); } } // ------------------------------------------------------------- function smd_at_prefs_remove($showpane='1') { global $smd_at_prefs; $message = ''; foreach (array_merge($smd_at_prefs[0],$smd_at_prefs[1]) as $pref => $dflt) { if (fetch('name','txp_prefs','name',$pref)) { $id = safe_delete('txp_prefs','name='.doQuote(doSlash($pref))); } } if ($showpane) { $message = smd_at_gTxt('prefs_deleted'); smd_at_setup($message); } } // ------------------------------------------------------------- function smd_at_prefs_update() { global $smd_plugin_prefs, $smd_at_prefs, $smd_at_event; $message = ''; $post = array(); $post['smd_at_global_skin'] = gps('smd_at_global_skin'); $post['smd_at_system'] = gps('smd_at_system'); $post['smd_at_layout'] = gps('smd_at_layout'); $post['smd_at_case_sort'] = gps('smd_at_case_sort'); $post['smd_at_tw'] = gps('smd_at_tw'); $post['smd_at_th'] = gps('smd_at_th'); $post['smd_at_filename_format'] = gps('smd_at_filename_format'); if (gps('smd_at_user_list')) { $post['smd_at_user_list'] = join(',', gps('smd_at_user_list')); } else { $post['smd_at_user_list'] = ''; } if (gps('smd_at_group_list')) { $post['smd_at_group_list'] = gps('smd_at_group_list'); } if (gps('smd_at_crush')) { $post['smd_at_crush'] = gps('smd_at_crush'); } // Set the system-wide theme to the global theme if ($post['smd_at_global_skin']) { set_pref('theme_name', $post['smd_at_global_skin']); } foreach ($smd_at_prefs[0] as $pref => $dflt) { if (isset($post[$pref])) { set_pref($pref, $post[$pref], $smd_at_event, 2); } } $message = gTxt('preferences_saved'); smd_at_setup($message); } // ------------------------ // UTILITY FUNCTIONS // ------------------------ function smd_at_get_privs($user='') { global $txp_user; $user = ($user) ? $user : $txp_user; $privs = safe_field("privs", "txp_users", "name='".doSlash($user)."'"); return $privs; } // ------------------------ function smd_at_exists($name) { $instance = theme::factory($name); return $instance; } // If per-user skin support is OFF, the global skin wins. // One exception: if the user is an admin, allow the current skin to prevail so they may switch skins with impunity // and not affect the global skin function smd_at_per() { global $smd_at_privs, $prefs, $theme, $txp_user; $at_prefs = smd_at_get_prefs(); $skinSys = isset($at_prefs['smd_at_system']) ? $at_prefs['smd_at_system'] : 0; $privs = smd_at_get_privs(); $gbl_skin = (isset($at_prefs['smd_at_global_skin']) && $at_prefs['smd_at_global_skin'] != '') ? $at_prefs['smd_at_global_skin'] : ''; $gbl_skin = (smd_at_exists($gbl_skin)) ? $gbl_skin : 'classic'; switch ($skinSys) { case 0: // Global forced skin (except admins) if (!in_array($privs, explode(',', $smd_at_privs))) { return $gbl_skin; } else { // Admins $adm_skin = get_pref('smd_skin', (($gbl_skin) ? $gbl_skin : $theme->name)); return (smd_at_exists($adm_skin)) ? $adm_skin : 'classic'; } break; case 1: // Group-level forced skins if (!in_array($privs, explode(',', $smd_at_privs))) { $forceSkins = (isset($at_prefs['smd_at_group_list'])) ? $at_prefs['smd_at_group_list'] : (($gbl_skin) ? $gbl_skin : $theme->name); // Split the group skin list $gskins = array(); $gopts = explode(',', $forceSkins); foreach ($gopts as $grpdef) { if ($grpdef == '') continue; $privgrp = explode(":", $grpdef); $skinid = array_shift($privgrp); if (in_array($privs, $privgrp)) { return (smd_at_exists($skinid)) ? $skinid : 'classic';; } } return ($gbl_skin) ? $gbl_skin : $theme->name; } else { // Admins $adm_skin = get_pref('smd_skin', (($gbl_skin) ? $gbl_skin : $theme->name)); return (smd_at_exists($adm_skin)) ? $adm_skin : 'classic'; } break; case 2: // Per-user skin. If user is non-admin, check the skin is in the allowed list if (!in_array($privs, explode(',', $smd_at_privs))) { $validSkins = (isset($at_prefs['smd_at_user_list'])) ? $at_prefs['smd_at_user_list'] : (($gbl_skin) ? $gbl_skin : $theme->name); $validSkins = do_list($validSkins); $the_skin = get_pref('smd_skin', (($gbl_skin) ? $gbl_skin : $theme->name)); return (in_array($the_skin, $validSkins) && smd_at_exists($the_skin)) ? $the_skin : ''; } else { // Admins $adm_skin = get_pref('smd_skin', (($gbl_skin) ? $gbl_skin : $theme->name)); return (smd_at_exists($adm_skin)) ? $adm_skin : 'classic'; } break; } } // ------------------------ // Read and return the manifest, if it exists and add a few other niceties to the array for later function smd_at_read_skinfo($skin) { global $smd_core_themes; $this_theme = theme::factory($skin); $skinfo = array(); if ($this_theme) { $skinfo = $this_theme->manifest(); $skinfo['dname'] = empty($skinfo['title']) ? ucwords($this_theme->name) : $skinfo['title']; $skinfo['name'] = $this_theme->name; $skinfo['path'] = $this_theme->url; $skinfo['phpfile'] = $this_theme->path($skin); $skinfo['based_on'] = ''; $contents = file_get_contents($skinfo['phpfile']); if (($pos = strpos($contents, 'theme::based_on')) !== false) { $begpos = strpos($contents, "'", $pos)+1; $endpos = strpos($contents, "'", $begpos); $base = substr($contents, $begpos, $endpos-$begpos); $skinfo['based_on'] = (in_array($base, $smd_core_themes)) ? '' : $base; } unset($this_theme); } return $skinfo; } // ------------------------ // Grab the thumbnail filename if it exists, optionally return a formatted img tag function smd_at_get_thumb($skin, $img=0) { global $smd_at_prefs; $at_prefs = smd_at_get_prefs(); $tw = isset($at_prefs['smd_at_tw']) ? $at_prefs['smd_at_tw'] : $smd_at_prefs[0]['smd_at_tw']; $th = isset($at_prefs['smd_at_th']) ? $at_prefs['smd_at_th'] : $smd_at_prefs[0]['smd_at_th']; $use_thumb = ($tw == 0 || $th == 0) ? false : true; // An array means the manifest has already been read and is being passed into this function if (!is_array($skin)) { $skin = smd_at_read_skinfo($skin); } if ($skin) { $skindir = $skin['path']; $thumbname = glob($skindir.'screenshot.*'); return ($img && $use_thumb && $thumbname && file_exists($thumbname[0])) ? ''.((isset($skin['name'])) ? $skin['name'] : '').' thumbnail' : ''; } else { return ''; } } // ------------------------ function smd_at_read_skins() { $skin_list = theme::names(); $skin_list = smd_at_sort($skin_list); return $skin_list; } // Sort the skins but keep 'classic' at the top function smd_at_sort($skin_list) { global $prefs; $caseSense = isset($prefs['smd_at_case_sort']) ? $prefs['smd_at_case_sort'] : 1; if($caseSense) { natsort($skin_list); } else { natcasesort($skin_list); } $dflt_found = false; foreach ($skin_list as $skin_idx => $skin_name) { if ($skin_name == "classic") { unset($skin_list[$skin_idx]); $dflt_found = true; break; } } if ($dflt_found) { array_unshift($skin_list,"classic"); } return $skin_list; } // ------------------------ function smd_at_listdir($start_dir='.') { $files = array(); if (is_dir($start_dir)) { $fh = opendir($start_dir); while (($file = readdir($fh)) !== false) { // loop through the files, skipping . and .., and recursing if necessary if (strcmp($file, '.')==0 || strcmp($file, '..')==0) continue; $filepath = $start_dir . '/' . $file; if ( is_dir($filepath) ) { $files = array_merge($files, smd_at_listdir($filepath)); } else { array_push($files, $filepath); } } closedir($fh); } else { // false if the function was called with an invalid non-directory argument $files = false; } return $files; } // ------------------------ // Recursive rmdir() courtesy of the php manual user comments function smd_rmdir_recursive($filepath) { if (is_dir($filepath) && !is_link($filepath)) { if ($dh = opendir($filepath)) { while (($sf = readdir($dh)) !== false) { if ($sf == '.' || $sf == '..') continue; if (!smd_rmdir_recursive($filepath.'/'.$sf)) { break; // Doh, can't delete } } closedir($dh); } return rmdir($filepath); } return unlink($filepath); } // ------------------------ // From PHP manual comments. Thanks puremango function smd_at_is_dir($dir) { // bypasses open_basedir restrictions of is_dir and fileperms $tmp_cmd = `ls -dl $dir`; $dir_flag = $tmp_cmd[0]; if($dir_flag!="d") { // not d; use next char (first char might be 's' and is still directory) $dir_flag = $tmp_cmd[1]; } return ($dir_flag=="d"); } // ------------------------ function smd_at_change_pageby() { setcookie('smd_at_pageby', gps('qty')); smd_at_list(''); } // ------------------------ function smd_at_switch($message='') { global $smd_at_privs; extract(doSlash(gpsa(array('skin', 'event', 'nextstep')))); $url = "?event=".$event."&step=".$nextstep; if ($skin && smd_at_exists($skin)) { set_pref('smd_skin', $skin, $event, PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); $url .= '&message=' . (($message) ? $message : smd_at_gTxt('skin_switched', array('{skin}' => $skin))); } else { $url .= '&message=' . $message; } // Since the headers have been sent, double-declutch in order to show the skin this first time echo << window.location.href="{$url}"; EOS; exit; } // All-user skin chooser function smd_admat($event, $step) { if(!$step or !in_array($step, array( 'smd_at_switch', ))) { smd_at_chooser(''); } else $step(); } // ------------------------ function smd_at_chooser($message='') { global $prefs, $smd_at_adm_event, $theme, $smd_at_styles; $at_prefs = smd_at_get_prefs(); $layout = (isset($at_prefs['smd_at_layout'])) ? $at_prefs['smd_at_layout'] : 1; $tw = (isset($at_prefs['smd_at_tw']) ? $at_prefs['smd_at_tw'] : $smd_at_prefs[0]['smd_at_tw']) + 10; $th = (isset($at_prefs['smd_at_th']) ? $at_prefs['smd_at_th'] : $smd_at_prefs[0]['smd_at_th']) + 80; $stylereps = array( '{tw}' => $tw, '{th}' => $th, ); $message = ($message) ? $message : gps('message'); pagetop(smd_at_gTxt('skinner'),$message); $curr_skin = $theme->name; $allowed = ''; if (isset($prefs['smd_at_user_list'])) { $allowed = explode(",",$prefs['smd_at_user_list']); foreach ($allowed as $idx => $valid) { if ($valid == '') { unset($allowed[$idx]); } } } $allowed = smd_at_sort($allowed); $hdrow = hed(smd_at_gTxt('avail_title'), 3); if ($layout=="0") { echo ''; echo startTable('list', '', 'smd_at_list'); echo tr( tda($hdrow, ' colspan="3"') ); echo tr( assHead( smd_at_gTxt('skin_gbl'), 'author', 'description' )); } else { echo ''; echo '
'.$hdrow.'
'; } foreach ($allowed as $skin_name) { $skinfo = smd_at_read_skinfo($skin_name); if ($skinfo) { $thumbnail = smd_at_get_thumb($skinfo, 1); $this_skin = ($skin_name == $curr_skin) ? strong($skinfo['dname']) : $skinfo['dname']; $thumrow = ''.$this_skin.(($thumbnail) ? '
'.$thumbnail.'
' : '').'
'; $authrow = (strpos($skinfo['author_uri'], "http://") === 0) ? ''.$skinfo['author'].'' : $skinfo['author']; if ($layout=="0") { echo tr( td($thumrow) .td($authrow, 100) .td($skinfo['description'], 250) , (($skin_name == $curr_skin) ? ' class="active"' : '') ); } else { echo '
'; echo '
'.$thumrow.'
'; echo '
'.smd_at_gTxt('by').$authrow.'
'; echo '
'; } } } if ($layout=="0") { echo endTable(); } else { echo '
'; } } // ------------------------ // Plugin-specific replacement strings - localise as required function smd_at_gTxt($what, $atts = array()) { $lang = array( 'actions' => 'Actions', 'allowed_skins' => 'Allowed themes:', 'apply_skin' => 'Apply this theme', 'avail_title' => 'Available themes', 'based_on' => ' (based on {skin})', 'by' => ' by ', 'c_bzip2' => 'BZip', 'c_gzip' => 'GZip', 'c_tar' => 'Tar', 'c_zip' => 'Zip', 'case_sort' => 'Case-sensitive theme list:', 'clear' => 'none', 'clone' => 'Base', 'confirm' => 'Go', 'core_theme' => 'Cannot delete {skin}: core theme', 'core_theme_file' => 'Cannot delete files from {skin}: core theme', 'crush_type' => 'Export compression type:', 'crush_format' => 'Format:', 'ssc' => 'Styleplates', 'css' => 'Styles', 'delete_confirm' => 'Really delete theme {skin}?', 'delete_failed' => 'Delete failed. Try removing it manually', 'edit_lbl' => 'Theme editor', 'empty_info' => 'You should choose a file first :-)', 'export' => 'Export', 'filename_format' => 'Filename format: ', 'file_not_deleted' => 'File {name} '.strong("not").' deleted', 'file_saved' => 'File {name} saved', 'find_theme' => 'Browse themes', 'global_skin' => 'Default theme:', 'help_link' => '(Help)', 'img' => 'Images', 'import_failed' => 'Installation failed :-( Check the archive structure', 'import_success' => 'Installation successful. Files extracted: {num}', 'import_ok' => 'Installation successful.', 'install_skin' => 'Install theme', 'invalid_file' => 'Rogue file detected: {name}', 'is_global' => ' [DEFAULT]', 'js' => 'Scripts', 'layout' => 'Layout method:', 'layout_list' => 'List', 'layout_grid' => 'Grid', 'list_title' => 'Installed themes', 'manage_lbl' => 'Theme manager', 'mkdir_failed' => 'Cannot create directory for file {name}. Try manually creating it.', 'name_empty' => 'You should give your theme a name :-)', 'new' => 'Create new theme', 'new_file' => 'New file', 'new_cloneskin' => 'Name of copy:', 'new_skin' => 'Theme name:', 'other_files' => 'Others', 'php' => 'Code', 'per_group' => 'One theme per privilege level', 'per_site' => 'One theme for everyone', 'per_user' => 'One theme per user', 'prefs_deleted' => 'Preferences deleted', 'prefs_installed' => 'Preferences installed', 'prefs_not_installed' => 'Preferences not installed.', 'prefs_some' => 'Not all preferences available.', 'prefs_some_explain' => 'It is either because this is a different version of the'.br.'plugin to one you had before or the preferences'.br.'have become corrupt somehow.', 'prefs_some_options' => 'Choose '.gTxt('delete').' to remove them all or '.gTxt('install').' to add'.br.'any new ones, leaving all existing preferences untouched.', 'prefs_title' => 'Admin theme preferences', 'rename_failed' => 'Rename failed', 'renamed' => 'Renamed to {name}', 'set' => 'Set', 'setup' => 'Preferences', 'skin_gbl' => 'Theme', 'skin_cloned' => 'Theme extended as {name}', 'skin_created' => 'Theme {name} created', 'skin_deleted' => 'Theme {name} deleted', 'skin_exists' => 'Theme {name} already exists', 'skin_files' => 'Theme files', 'skin_groups' => 'Group themes:', 'skin_images' => 'Theme images', 'skin_list' => 'Theme list', 'skin_grid' => 'Theme grid', 'skin_not_created' => 'Theme {name} could not be created. Try manually making the directory', 'skin_not_found' => 'The theme {name} could not be found', 'skin_switched' => 'Theme switched to {skin}', 'skin_system' => 'Theme system:', 'skinner' => 'Theme', 'supported_import' => 'Supported import types: {types}', 'tab_name' => 'Admin themes', 'thumbsize' => 'Thumb dimensions:', 'times' => ' x ', 'unsupported_compressiontype' => '{crush} is not a supported compression format in this installation', 'unsupported_filetype' => 'File is not of a supported type', 'unsupported_fudge' => ': hit Save again to store anyway', 'update' => 'Update', 'upload_failed' => 'Image could not be uploaded', 'upload_image' => 'Upload image', 'upload_success' => 'Image uploaded successfully', 'version' => 'v', ); return strtr($lang[$what], $atts); }