/** * Renders a list of form templates. * * This function returns a list of form templates, * wrapped in a multi-edit form widget. * * @param string $curname The selected form * @return string HTML */ function form_list($curname) { global $essential_forms, $form_types; $criteria = 1; $criteria .= callback_event('admin_criteria', 'form_list', 0, $criteria); $rs = safe_rows_start('*', 'txp_form', "{$criteria} order by field(type, " . join(',', quote_list(array_keys($form_types))) . ") asc, name asc"); if ($rs) { $prev_type = null; $group_out = array(); while ($a = nextRow($rs)) { extract($a); $active = $curname === $name; if ($prev_type !== $type) { if ($prev_type !== null) { $group_out = tag(n . join(n, $group_out) . n, 'ul', array('class' => 'switcher-list')); $out[] = wrapRegion($prev_type . '_forms_group', $group_out, 'form_' . $prev_type, $form_types[$prev_type], 'form_' . $prev_type); } $prev_type = $type; $group_out = array(); } if ($active) { $editlink = txpspecialchars($name); } else { $editlink = eLink('form', 'form_edit', 'name', $name, $name); } if (!in_array($name, $essential_forms)) { $modbox = span(checkbox('selected_forms[]', txpspecialchars($name), false), array('class' => 'switcher-action')); } else { $modbox = ''; } $group_out[] = tag(n . $modbox . $editlink . n, 'li', array('class' => $active ? 'active' : '')); } if ($prev_type !== null) { $group_out = tag(n . join(n, $group_out) . n, 'ul', array('class' => 'switcher-list')); $out[] = wrapRegion($prev_type . '_forms_group', $group_out, 'form_' . $prev_type, $form_types[$prev_type], 'form_' . $prev_type); } $methods = array('changetype' => array('label' => gTxt('changetype'), 'html' => formTypes('', false, 'changetype')), 'delete' => gTxt('delete')); $out[] = multi_edit($methods, 'form', 'form_multi_edit'); return form(join('', $out), '', '', 'post', '', '', 'allforms_form'); } }
/** * Generates a list of authors. * * @param array $atts * @param string $thing * @return string */ public static function renderAuthors($atts, $thing = null) { global $thisauthor, $txp_groups; extract(lAtts(array('break' => '', 'class' => '', 'form' => '', 'group' => '', 'label' => '', 'labeltag' => '', 'limit' => '', 'name' => '', 'offset' => '', 'sort' => 'name ASC', 'wraptag' => ''), $atts)); $sql = array('1 = 1'); $sql_limit = ''; $sql_sort = " ORDER BY " . doSlash($sort); if ($name) { $sql[] = "name IN (" . join(', ', quote_list(do_list($name))) . ")"; } if ($group !== '') { $privs = do_list($group); $groups = array_flip($txp_groups); foreach ($privs as &$priv) { if (isset($groups[$priv])) { $priv = $groups[$priv]; } } $sql[] = 'convert(privs, char) in (' . join(', ', quote_list($privs)) . ')'; } if ($limit !== '' || $offset) { $sql_limit = " LIMIT " . intval($offset) . ", " . ($limit === '' ? PHP_INT_MAX : intval($limit)); } $rs = safe_rows_start("user_id as id, name, RealName as realname, email, privs, last_access", 'txp_users', join(" AND ", $sql) . " {$sql_sort} {$sql_limit}"); if ($rs && numRows($rs)) { $out = array(); if ($thing === null && $form !== '') { $thing = fetch_form($form); } while ($a = nextRow($rs)) { $oldauthor = $thisauthor; $thisauthor = $a; $out[] = parse($thing); $thisauthor = $oldauthor; } unset($thisauthor); return doLabel($label, $labeltag) . doWrap($out, $wraptag, $break, $class); } return ''; }
/** * Processes multi-edit actions. * * Accessing requires 'admin.edit' privileges. */ function admin_multi_edit() { global $txp_user; require_privs('admin.edit'); $selected = ps('selected'); $method = ps('edit_method'); $changed = array(); $msg = ''; if (!$selected or !is_array($selected)) { return author_list(); } $names = safe_column('name', 'txp_users', "name IN (" . join(',', quote_list($selected)) . ") AND name != '" . doSlash($txp_user) . "'"); if (!$names) { return author_list(); } switch ($method) { case 'delete': $assign_assets = ps('assign_assets'); if (!$assign_assets) { $msg = array('must_reassign_assets', E_ERROR); } elseif (in_array($assign_assets, $names)) { $msg = array('cannot_assign_assets_to_deletee', E_ERROR); } elseif (remove_user($names, $assign_assets)) { $changed = $names; callback_event('authors_deleted', '', 0, $changed); $msg = 'author_deleted'; } break; case 'changeprivilege': if (change_user_group($names, ps('privs'))) { $changed = $names; $msg = 'author_updated'; } break; case 'resetpassword': foreach ($names as $name) { $passwd = generate_password(PASSWORD_LENGTH); if (change_user_password($name, $passwd)) { $email = safe_field('email', 'txp_users', "name = '" . doSlash($name) . "'"); if (send_new_password($passwd, $email, $name)) { $changed[] = $name; $msg = 'author_updated'; } else { return author_list(array(gTxt('could_not_mail') . ' ' . txpspecialchars($name), E_ERROR)); } } } break; } if ($changed) { return author_list(gTxt($msg, array('{name}' => txpspecialchars(join(', ', $changed))))); } author_list($msg); }
/** * Renders the list of preferences. * * Plugins may add their own prefs, for example by using plugin lifecycle events or * raising a (pre) callback on event=admin / step=prefs_list so they are installed * or updated when accessing the Preferences panel. Access to the prefs can be * controlled by using add_privs() on 'prefs.your-prefs-event-name'. * * @param string $message The feedback / error string to display */ function prefs_list($message = '') { global $prefs, $txp_user; extract($prefs); pagetop(gTxt('tab_preferences'), $message); $locale = setlocale(LC_ALL, $locale); echo hed(gTxt('tab_preferences'), 1, array('class' => 'txp-heading')); echo n . '<div id="prefs_container" class="txp-container">' . n . '<form method="post" class="prefs-form" action="index.php">' . n . '<div class="txp-layout-textbox">'; // TODO: remove 'custom' when custom fields are refactored. $core_events = array('site', 'admin', 'publish', 'feeds', 'comments', 'custom'); $joined_core = join(',', quote_list($core_events)); $sql = array(); $sql[] = 'prefs_id = 1 and event != "" and type in(' . PREF_CORE . ', ' . PREF_PLUGIN . ')'; $sql[] = "(user_name = '' or (user_name='" . doSlash($txp_user) . "' and name not in(\n select name from " . safe_pfx('txp_prefs') . " where user_name = ''\n )))"; if (!get_pref('use_comments', 1, 1)) { $sql[] = "event != 'comments'"; } $rs = safe_rows_start("*, FIELD(event,{$joined_core}) as sort_value", 'txp_prefs', join(' and ', $sql) . " ORDER BY sort_value = 0, sort_value, event, position"); $last_event = null; $out = array(); if (numRows($rs)) { while ($a = nextRow($rs)) { if (!has_privs('prefs.' . $a['event'])) { continue; } if ($a['event'] !== $last_event) { if ($last_event !== null) { echo wrapRegion('prefs_group_' . $last_event, join(n, $out), 'prefs_' . $last_event, $last_event, 'prefs_' . $last_event); } $last_event = $a['event']; $out = array(); } $label = ''; if (!in_array($a['html'], array('yesnoradio', 'is_dst'))) { $label = $a['name']; } // TODO: remove exception when custom fields move to meta store. $help = ''; if (strpos($a['name'], 'custom_') === false) { $help = $a['name']; } if ($a['html'] == 'text_input') { $size = INPUT_REGULAR; } else { $size = ''; } $out[] = inputLabel($a['name'], pref_func($a['html'], $a['name'], $a['val'], $size), $label, $help, array('id' => 'prefs-' . $a['name'])); } } if ($last_event === null) { echo graf(gTxt('no_preferences')); } else { echo wrapRegion('prefs_group_' . $last_event, join(n, $out), 'prefs_' . $last_event, $last_event, 'prefs_' . $last_event); } echo n . '</div>' . sInput('prefs_save') . eInput('prefs') . hInput('prefs_id', '1') . tInput(); if ($last_event !== null) { echo graf(fInput('submit', 'Submit', gTxt('save'), 'publish')); } echo n . '</form>' . n . '</div>'; }
/** * Renders the list of preferences. * * Plugins may add their own prefs, for example by using plugin lifecycle events * or raising a (pre) callback on event=admin / step=prefs_list so they are * installed or updated when accessing the Preferences panel. Access to the * prefs can be controlled by using add_privs() on 'prefs.your-prefs-event-name'. * * @param string $message The feedback / error string to display */ function prefs_list($message = '') { global $prefs, $txp_user; extract($prefs); pagetop(gTxt('tab_preferences'), $message); $locale = setlocale(LC_ALL, $locale); echo n . '<form class="prefs-form" id="prefs_form" method="post" action="index.php">'; // TODO: remove 'custom' when custom fields are refactored. $core_events = array('site', 'admin', 'publish', 'feeds', 'comments', 'custom'); $joined_core = join(',', quote_list($core_events)); $sql = array(); $sql[] = 'prefs_id = 1 and event != "" and type in(' . PREF_CORE . ', ' . PREF_PLUGIN . ')'; $sql[] = "(user_name = '' OR (user_name = '" . doSlash($txp_user) . "' AND name NOT IN (\n SELECT name FROM " . safe_pfx('txp_prefs') . " WHERE user_name = ''\n )))"; if (!get_pref('use_comments', 1, 1)) { $sql[] = "event != 'comments'"; } $rs = safe_rows_start("*, FIELD(event, {$joined_core}) AS sort_value", 'txp_prefs', join(" AND ", $sql) . " ORDER BY sort_value = 0, sort_value, event, position"); $last_event = null; $out = array(); $build = array(); $groupOut = array(); if (numRows($rs)) { while ($a = nextRow($rs)) { if (!has_privs('prefs.' . $a['event'])) { continue; } if ($a['event'] !== $last_event) { if ($last_event !== null) { $build[] = tag(hed(gTxt($last_event), 2, array('id' => 'prefs_group_' . $last_event . '-label')) . join(n, $out), 'section', array('class' => 'txp-prefs-group', 'id' => 'prefs_group_' . $last_event, 'aria-labelledby' => 'prefs_group_' . $last_event . '-label')); $groupOut[] = n . tag(href(gTxt($last_event), '#prefs_group_' . $last_event, array('data-txp-pane' => $last_event, 'data-txp-token' => form_token())), 'li'); } $last_event = $a['event']; $out = array(); } $label = ''; if (!in_array($a['html'], array('yesnoradio', 'is_dst'))) { $label = $a['name']; } // TODO: remove exception when custom fields move to meta store. $help = ''; if (strpos($a['name'], 'custom_') === false) { $help = $a['name']; } if ($a['html'] == 'text_input') { $size = INPUT_REGULAR; } else { $size = ''; } $out[] = inputLabel($a['name'], pref_func($a['html'], $a['name'], $a['val'], $size), $label, $help, array('class' => 'txp-form-field', 'id' => 'prefs-' . $a['name'])); } } if ($last_event === null) { echo graf(gTxt('no_preferences')); } else { $build[] = tag(hed(gTxt($last_event), 2, array('id' => 'prefs_group_' . $last_event . '-label')) . join(n, $out), 'section', array('class' => 'txp-prefs-group', 'id' => 'prefs_group_' . $last_event, 'aria-labelledby' => 'prefs_group_' . $last_event . '-label')); $groupOut[] = n . tag(href(gTxt($last_event), '#prefs_group_' . $last_event, array('data-txp-pane' => $last_event, 'data-txp-token' => form_token())), 'li') . n; echo hed(gTxt('tab_preferences'), 1, array('class' => 'txp-heading')) . n . '<div class="txp-layout-4col-cell-1alt">' . wrapGroup('all_preferences', n . tag(join($groupOut), 'ul', array('class' => 'switcher-list')), 'all_preferences'); if ($last_event !== null) { echo graf(fInput('submit', 'Submit', gTxt('save'), 'publish'), array('class' => 'txp-save')); } echo n . '</div>' . n . '<div class="txp-layout-4col-cell-2-3-4">' . join(n, $build) . n . '</div>' . sInput('prefs_save') . eInput('prefs') . hInput('prefs_id', '1') . tInput(); } echo n . '</form>'; }
/** * Processes multi-edit actions. */ function section_multi_edit() { global $txp_user, $all_pages, $all_styles; extract(psa(array('edit_method', 'selected'))); if (!$selected || !is_array($selected)) { return sec_section_list(); } $key = $val = ''; switch ($edit_method) { case 'delete': return section_delete(); break; case 'changepage': $val = ps('uses_page'); if (in_array($val, $all_pages, true)) { $key = 'page'; } break; case 'changecss': $val = ps('css'); if (in_array($val, $all_styles, true)) { $key = 'css'; } break; case 'changeonfrontpage': $key = 'on_frontpage'; $val = (int) ps('on_frontpage'); break; case 'changesyndicate': $key = 'in_rss'; $val = (int) ps('in_rss'); break; case 'changesearchable': $key = 'searchable'; $val = (int) ps('searchable'); break; } $sections = safe_column('name', 'txp_section', "name in (" . join(',', quote_list($selected)) . ")"); if ($key && $sections) { if (safe_update('txp_section', "{$key} = '" . doSlash($val) . "'", "name in (" . join(',', quote_list($sections)) . ")")) { sec_section_list(gTxt('section_updated', array('{name}' => join(', ', $sections)))); return; } } sec_section_list(); }
/** * Processes multi-edit actions. * * Accessing requires 'admin.edit' privileges. */ function admin_multi_edit() { global $txp_user; require_privs('admin.edit'); $selected = ps('selected'); $method = ps('edit_method'); $changed = array(); $msg = ''; if (!$selected or !is_array($selected)) { return author_list(); } $clause = ''; if ($method === 'resetpassword') { $clause = " AND last_access IS NOT NULL"; } elseif ($method === 'resendactivation') { $clause = " AND last_access IS NULL"; } $names = safe_column("name", 'txp_users', "name IN (" . join(',', quote_list($selected)) . ") AND name != '" . doSlash($txp_user) . "'" . $clause); if (!$names) { return author_list(); } switch ($method) { case 'delete': $assign_assets = ps('assign_assets'); if (!$assign_assets) { $msg = array('must_reassign_assets', E_ERROR); } elseif (in_array($assign_assets, $names)) { $msg = array('cannot_assign_assets_to_deletee', E_ERROR); } elseif (remove_user($names, $assign_assets)) { $changed = $names; callback_event('authors_deleted', '', 0, $changed); $msg = 'author_deleted'; } break; case 'changeprivilege': if (change_user_group($names, ps('privs'))) { $changed = $names; $msg = 'author_updated'; } break; case 'resetpassword': foreach ($names as $name) { send_reset_confirmation_request($name); $changed[] = $name; } $msg = 'password_reset_confirmation_request_sent'; break; case 'resendactivation': foreach ($names as $name) { send_account_activation($name); $changed[] = $name; } $msg = 'resend_activation_request_sent'; break; } if ($changed) { return author_list(gTxt($msg, array('{name}' => txpspecialchars(join(', ', $changed))))); } author_list($msg); }
/** * Gets an SQL clause for this method based on the criteria. * * @todo Case-sensitive searches * * @param string $search_term The value to search for * @param int $verbatim Whether the search term is in quotes (1) or not (0) */ public function getCriteria($search_term, $verbatim) { $clause = array(); foreach ($this->columns as $column) { if (isset($this->aliases[$column])) { foreach ($this->aliases[$column] as $dbval => $aliases) { $terms = do_list($search_term); foreach ($terms as $idx => $term) { if ($this->findAlias($term, $aliases, $this->type) !== false) { $terms[$idx] = $dbval; } } $search_term = join(',', $terms); } } if ($this->options['can_list']) { $operator = ' in '; $value = '(' . join(',', quote_list(do_list($search_term))) . ')'; } elseif ($this->type === 'boolean') { $clause[] = "convert(" . $column . ", char) = '" . $search_term . "'"; continue; } elseif ($verbatim && !$this->options['always_like']) { $operator = ' = '; $value = doQuote($search_term); } elseif ($this->type === 'find_in_set') { $clause[] = "find_in_set('" . $search_term . "', " . $column . " )"; continue; } elseif ($this->type === 'numeric') { $clause[] = "convert(" . $column . ", char) = '" . $search_term . "'"; continue; } else { $operator = ' like '; $value = doQuote("%{$search_term}%"); } $clause[] = join($operator, array($column, $value)); } return $clause; }
/** * Changes a user's group. * * On a successful run, will trigger a 'user.change_group > done' callback event. * * @param string|array $user Updated users * @param int $group The new group * @return bool FALSE on error * @since 4.6.0 * @package User * @example * if (change_user_group('john', 1)) * { * echo "'john' is now publisher."; * } */ function change_user_group($user, $group) { $levels = get_groups(); if (!$user || !isset($levels[$group])) { return false; } $names = join(',', quote_list((array) $user)); if (safe_update('txp_users', 'privs = ' . intval($group), "name in ({$names})") === false) { return false; } callback_event('user.change_group', 'done', 0, compact('user', 'group')); return true; }
function mem_form_select_section($atts) { extract(mem_form_lAtts(array('exclude' => '', 'sort' => 'name ASC', 'delimiter' => ','), $atts, false)); if (!empty($exclude)) { $exclusion = array_map('trim', split($delimiter, preg_replace('/[\\r\\n\\t\\s]+/', ' ', $exclude))); $exclusion = array_map('strtolower', $exclusion); if (count($exclusion)) { $exclusion = join($delimiter, quote_list($exclusion)); } } $where = empty($exclusion) ? '1=1' : 'LOWER(name) NOT IN (' . $exclusion . ')'; $sort = empty($sort) ? '' : ' ORDER BY ' . doSlash($sort); $rs = safe_rows('name, title', 'txp_section', $where . $sort); $items = array(); $values = array(); if ($rs) { foreach ($rs as $r) { $items[] = $r['title']; $values[] = $r['name']; } } unset($atts['exclude'], $atts['sort']); $atts['items'] = join($delimiter, $items); $atts['values'] = join($delimiter, $values); return mem_form_select($atts); }
* Textpattern is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Textpattern. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('TXP_UPDATE')) { exit("Nothing here. You can't access this file directly."); } safe_alter('textpattern', "MODIFY textile_body VARCHAR(32) NOT NULL DEFAULT '1'"); safe_alter('textpattern', "MODIFY textile_excerpt VARCHAR(32) NOT NULL DEFAULT '1'"); safe_update('txp_prefs', "name = 'pane_article_textfilter_help_visible'", "name = 'pane_article_textile_help_visible'"); // Rejig preferences panel. $core_ev = join(',', quote_list(array('site', 'admin', 'publish', 'feeds', 'custom', 'comments'))); // 1) Increase event column size. safe_alter('txp_prefs', "MODIFY event VARCHAR(255) NOT NULL DEFAULT 'publish'"); safe_alter('txp_prefs', "MODIFY html VARCHAR(255) NOT NULL DEFAULT 'text_input'"); // 2) Remove basic/advanced distinction. safe_update('txp_prefs', "type = '" . PREF_CORE . "'", "type = '" . PREF_PLUGIN . "' AND event IN ({$core_ev})"); // 3) Consolidate existing prefs into better groups. safe_update('txp_prefs', "event = 'site'", "name IN ('sitename', 'siteurl', 'site_slogan', 'production_status', 'gmtoffset', 'auto_dst', 'is_dst', 'dateformat', 'archive_dateformat', 'permlink_mode', 'doctype', 'logging', 'use_comments', 'expire_logs_after')"); // 4) Reorder existing prefs into a more logical progression. safe_update('txp_prefs', "position = '230'", "name = 'expire_logs_after'"); safe_update('txp_prefs', "position = '340'", "name = 'max_url_len'"); safe_update('txp_prefs', "position = '160'", "name = 'comments_sendmail'"); safe_update('txp_prefs', "position = '180'", "name = 'comments_are_ol'"); safe_update('txp_prefs', "position = '200'", "name = 'comment_means_site_updated'"); safe_update('txp_prefs', "position = '220'", "name = 'comments_require_name'"); safe_update('txp_prefs', "position = '240'", "name = 'comments_require_email'");
/** Add a FIELD() expression to the ORDER BY array. * For preserving an arbitrary sort order, e.g. '7,5,12,1' * Note that FIELD() is a MySQL-specific function (not standard SQL) * @param field Column name * @param list Comma-separated list, or array, of values in order */ public function order_by_field($field, $list) { if (is_string($list)) { $list = do_list($list); } if (count($list)) { $this->order_by[] = 'field(' . self::quote($field) . ', ' . implode(', ', quote_list(doSlash($list))) . ')'; } return $this; }
function section_list($atts, $thing = null) { global $sitename, $s, $thissection; extract(lAtts(array('active_class' => '', 'break' => br, 'class' => __FUNCTION__, 'default_title' => $sitename, 'exclude' => '', 'form' => '', 'include_default' => '', 'label' => '', 'labeltag' => '', 'sections' => '', 'sort' => '', 'wraptag' => '', 'offset' => '', 'limit' => ''), $atts)); $sql_limit = ''; $sql_sort = doSlash($sort); $sql = array(); $sql[] = 1; if ($limit !== '' || $offset) { $sql_limit = ' limit ' . intval($offset) . ', ' . ($limit === '' ? PHP_INT_MAX : intval($limit)); } if ($sections) { if ($include_default) { $sections .= ', default'; } $sections = join(',', quote_list(do_list($sections))); $sql[] = "name in ({$sections})"; if (!$sql_sort) { $sql_sort = "field(name, {$sections})"; } } else { if ($exclude) { $exclude = join(',', quote_list(do_list($exclude))); $sql[] = "name not in ({$exclude})"; } if (!$include_default) { $sql[] = "name != 'default'"; } if (!$sql_sort) { $sql_sort = 'name asc'; } } if ($include_default) { $sql_sort = "name != 'default', " . $sql_sort; } $rs = safe_rows_start('name, title', 'txp_section', join(' and ', $sql) . ' order by ' . $sql_sort . $sql_limit); if ($rs && ($last = numRows($rs))) { $out = array(); $count = 0; if (isset($thissection)) { $old_section = $thissection; } while ($a = nextRow($rs)) { ++$count; extract($a); if ($name == 'default') { $title = $default_title; } if ($form === '' && $thing === null) { $url = pagelinkurl(array('s' => $name)); $out[] = tag(txpspecialchars($title), 'a', (($active_class and 0 == strcasecmp($s, $name)) ? ' class="' . txpspecialchars($active_class) . '"' : '') . ' href="' . $url . '"'); } else { $thissection = array('name' => $name, 'title' => $title, 'is_first' => $count == 1, 'is_last' => $count == $last); if ($thing === null && $form !== '') { $out[] = parse_form($form); } else { $out[] = parse($thing); } } } $thissection = isset($old_section) ? $old_section : null; if ($out) { return doLabel($label, $labeltag) . doWrap($out, $wraptag, $break, $class); } } return ''; }
function smd_ebook_create() { global $smd_ebook_prefs, $img_dir; @(include_once txpath . '/lib/classTextile.php'); @(include_once txpath . '/publish.php'); // for parse() $textile = new Textile(); $template = smd_ebook_templates(); $msg = ''; $report = $toc = $ncx = $reps = $sheets = $lfout = array(); // Generate a temporary folder name to store all files in. // It's based on the passed params so if you're regenerating the same book // and don't alter the POST payload, you'll overwrite stuff in the previous // folder for this book. Just keeps the clutter down.. $ebook_folder = 'smd_ebook_' . substr(md5(serialize($_POST)), 0, 12); $ebook_path = get_pref('tempdir') . DS . $ebook_folder . DS; if (!is_readable($ebook_path)) { mkdir($ebook_path); } // Store the current type for next time this user creates a book $bType = ps('smd_ebook_type'); set_pref('smd_ebook_type', doSlash($bType), 'smd_ebook', PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE); $is_mobi = $bType === 'mobi'; $is_epub = $bType === 'zip'; $outfile = trim(ps('smd_ebook_pubfile')); // If used, it's without extension // Get Textile and encoding options $encoding = get_pref('smd_ebook_encoding', $smd_ebook_prefs['smd_ebook_encoding']['default']); $which = get_pref('smd_ebook_textile', $smd_ebook_prefs['smd_ebook_textile']['default']); $txt_description = in_list('description', $which); $txt_authornote = in_list('authornote', $which); // Build up a giant replacement table which is then substituted into // the various templates before passing to the generator // Set up the TOC wrappers $toc_wrap = get_pref('smd_ebook_toc_wraptag', $smd_ebook_prefs['smd_ebook_toc_wraptag']['default']); $toc_class = get_pref('smd_ebook_toc_class', $smd_ebook_prefs['smd_ebook_toc_class']['default']); $wrapit = $toc_wrap === 'ol' ? '#' : '*'; $article_cnt = $ncx_cnt = $elem_cnt = $img_cnt = 0; $article_refs = $article_spines = $guide_refs = $landmarks = $master_image_refs = array(); // Page break, stylesheets and heading references $pbr = get_pref('smd_ebook_page_break', $smd_ebook_prefs['smd_ebook_page_break']['default']); $css = get_pref('smd_ebook_stylesheet', $smd_ebook_prefs['smd_ebook_stylesheet']['default']); $hdg = get_pref('smd_ebook_heading_level', $smd_ebook_prefs['smd_ebook_heading_level']['default']); if ($css) { $css_list = quote_list(do_list($css)); $sheets = $css_list ? safe_rows('name, css', 'txp_css', "name IN (" . join(',', $css_list) . ")") : array(); } $sheetlist = $sheetcontent = array(); $sheet_count = 0; foreach ($sheets as $stylething) { $content = trim($stylething['css']); if ($is_mobi && $content) { // Inline style tags $sheetlist[] = '<style type="text/css">' . $content . '</style>'; } elseif ($is_epub && $content) { // External stylesheet references $sheetname = $stylething['name'] . '.css'; $sheetlist[] = '<link rel="stylesheet" media="screen" href="' . $sheetname . '" />'; $sheetcontent[$stylething['name']] = $content; // Add them to the manifest and listfile $article_refs[] = '<item id="smd_ebook_style_' . $sheet_count . '" media-type="text/css" href="' . $sheetname . '" />'; $lfout[] = $sheetname; } $sheet_count++; } $sheet = join(n, $sheetlist); // The values used in the 'doc' template $html_from = array('{smd_ebook_doctype}', '{smd_ebook_namespace}', '{smd_ebook_charset}', '{smd_ebook_encoding}', '{smd_ebook_title}', '{smd_ebook_chaptitle}', '{smd_ebook_stylesheet}', '{smd_ebook_contents}'); // Loop for each article in the collection foreach (ps('smd_ebook_articles') as $artid) { $article_cnt++; $id = str_replace('smd_ebook_article_', '', $artid); $row = safe_row('*, UNIX_TIMESTAMP(Posted) AS uPosted, UNIX_TIMESTAMP(LastMod) AS uModified', 'textpattern', "ID = '" . doSlash($id) . "'"); if ($row) { // Initialize a few things $note_content = ''; $image_list = array(); $reps['{smd_ebook_file_name}'] = $cur_file = $row['url_title'] . '.html'; $reps['{smd_ebook_encoding}'] = $encoding; // Each of the items starting !isset() are only ever loaded _once_ from the // first article in which they are found. // Begin by setting up the file names if (!isset($firstfile)) { $firstfile = $row['url_title'] . '.html'; $filebase = sanitizeForFile($outfile === '' ? $row['url_title'] : $outfile); $listfile = $filebase . '.smd'; $notefile = $filebase . '_notes.html'; $toc_file = $filebase . '_toc.html'; $ncx_file = $filebase . '.ncx'; $end_file = $filebase . '_end.html'; $opf_file = $filebase . '.opf'; $lmk_file = $filebase . '_landmark.html'; $cover_file = 'cover.html'; $container_file = 'container.xml'; $mimetype_file = 'mimetype'; $toc_ref = 'toc'; } // Populate the unique ID entries direcly into the .opf template, // as they're only used once each if (!isset($reps['{smd_ebook_uid_ref}'])) { $val = ps('smd_ebook_fld_uid'); if (strpos($val, 'SMD_FLD_') !== false) { $valfld = str_replace('SMD_FLD_', '', $val); $val = isset($row[$valfld]) ? $row[$valfld] : ''; } $uid = trim($val) === '' ? smd_ebook_uid() : $val; $is_isbn = smd_ebook_is_isbn($uid); $is_uuid = preg_match('/^[0-9a-f]{8,8}\\-[0-9a-f]{4,4}\\-[0-9a-f]{4,4}\\-[0-9a-f]{4,4}\\-[0-9a-f]{12,12}$/i', $uid) === 1; $full_uid = $reps['{smd_ebook_uid_ref}'] = $is_uuid ? 'urn:uuid:' . $uid : ($is_isbn ? 'urn:isbn:' . $uid : $uid); $template['opf'] = str_replace('{smd_ebook_uid_ref}', $uid ? ' unique-identifier="uid"' : '', $template['opf']); $template['opf'] = str_replace('{smd_ebook_md_uid}', $uid ? '<dc:identifier id="uid">' . $full_uid . '</dc:identifier>' : '', $template['opf']); $template['ncx'] = str_replace('{smd_ebook_dtb_uid}', $uid ? '<meta name="dtb:uid" content="' . $full_uid . '" />' : '', $template['ncx']); } // Language if (!isset($reps['{smd_ebook_md_lang}'])) { $lang = get_pref('language'); $reps['{smd_ebook_md_lang}'] = '<dc:language>' . $lang . '</dc:language>'; $reps['{smd_ebook_lang}'] = $lang; } // Author can come from: // 1) an article field // 2) the current logged in user // 3) user-supplied at book creation time // 4) hard-coded in plugin settings if (!isset($reps['{smd_ebook_md_creator}'])) { $val = ps('smd_ebook_fld_author'); if (strpos($val, 'SMD_FLD_') !== false) { $valfld = str_replace('SMD_FLD_', '', $val); $val = isset($row[$valfld]) ? $valfld === 'AuthorID' ? get_author_name($row[$valfld]) : $row[$valfld] : ''; } if ($val) { $reps['{smd_ebook_md_creator}'] = '<dc:creator>' . $val . '</dc:creator>'; $reps['{smd_ebook_creator}'] = $val; } } // Doctype, namespace and charset if (!isset($reps['{smd_ebook_doctype}'])) { if ($is_mobi) { $reps['{smd_ebook_doctype}'] = ''; $reps['{smd_ebook_namespace}'] = ''; $reps['{smd_ebook_charset}'] = '<meta http-equiv="Content-Type" content="text/html; charset=' . $encoding . '">'; } elseif ($is_epub) { $reps['{smd_ebook_doctype}'] = '<?xml version="1.0" encoding="' . $encoding . '"?>' . n . '<!DOCTYPE html>'; $reps['{smd_ebook_namespace}'] = ' xmlns="http://www.w3.org/1999/xhtml"'; $reps['{smd_ebook_charset}'] = '<meta charset="' . $encoding . '" />'; } } // Publication / modification date if (!isset($reps['{smd_ebook_md_date}'])) { $reps['{smd_ebook_md_date}'] = '<dc:date>' . strftime('%Y-%m-%dT%H:%M:%SZ', $row['uPosted']) . '</dc:date>'; } if (!isset($reps['{smd_ebook_md_modified}'])) { $reps['{smd_ebook_md_modified}'] = '<meta property="dcterms:modified">' . strftime('%Y-%m-%dT%H:%M:%SZ', $row['uModified']) . '</meta>'; } // Cover image if (!isset($reps['{smd_ebook_md_cover}'])) { if (isset($row['Image'])) { $img = safe_row('*', 'txp_image', "id='" . intval($row['Image']) . "'"); // Only GIFs, JPGs or PNGs need apply if ($img) { $mime_type = $img['ext'] === '.jpg' || $img['ext'] === '.jpeg' ? 'image/jpeg' : ($img['ext'] === '.gif' ? 'image/gif' : ($img['ext'] === '.png' ? 'image/png' : '')); if ($mime_type) { $img_file = $img['id'] . $img['ext']; $reps['{smd_ebook_md_cover}'] = '<meta name="cover" content="cover-image" />'; if ($is_epub) { $img_dest = 'cover' . $img['ext']; $ret = copy(get_pref('path_to_site') . DS . $img_dir . DS . $img_file, $ebook_path . $img_dest); if ($ret) { // Write the cover image HTML $fp = fopen($ebook_path . $cover_file, "wb"); $from = array('{smd_ebook_image_link}', '{smd_ebook_image_alt}'); $to = array($img_dest, $img['alt']); fwrite($fp, str_replace($from, $to, $template['cov'])); fclose($fp); $reps['{smd_ebook_manifest_cover}'] = '<item id="cover" media-type="application/xhtml+xml" href="cover.html" />' . n . t . t . '<item id="cover-image" properties="cover-image" media-type="' . $mime_type . '" href="' . $img_dest . '" />'; $lfout[] = $img_dest; } } else { $reps['{smd_ebook_manifest_cover}'] = '<item id="cover-image" media-type="' . $mime_type . '" href="' . get_pref('path_to_site') . DS . $img_dir . DS . $img_file . '" />'; } } } } } // The following values can either come from the given field or be used verbatim // Firstly the title, description, subject, publisher and chapter title $setMany = array('chaptitle'); foreach (array('title', 'description', 'subject', 'publisher', 'chaptitle') as $thingy) { if (in_array($thingy, $setMany) || !isset($reps['{smd_ebook_md_' . $thingy . '}'])) { $val = ps('smd_ebook_fld_' . $thingy); if (strpos($val, 'SMD_FLD_') !== false) { $valfld = str_replace('SMD_FLD_', '', $val); $val = isset($row[$valfld]) ? $row[$valfld] : ''; } if ($val) { // Textile the content? $content = isset($txt_[$thingy]) && $txt_[$thingy] ? trim($textile->TextileThis($val)) : trim($val); if (!in_array($thingy, $setMany)) { $reps['{smd_ebook_md_' . $thingy . '}'] = '<dc:' . $thingy . '>' . $content . '</dc:' . $thingy . '>'; } // There are two titles: one for the metadata and one raw so if the title // has just been found, populate the raw title too if ($thingy === 'title') { $reps['{smd_ebook_title}'] = $content; } elseif ($thingy === 'chaptitle') { // Chapter title has an associated heading level $reps['{smd_ebook_chaptitle}'] = '<h' . $hdg . '>' . $content . '</h' . $hdg . '>'; } } } } // Price (SRP) can also come from a field but it needs some special jiggery pokery if (!isset($reps['{smd_ebook_md_srp}'])) { $val = ps('smd_ebook_fld_srp'); if (strpos($val, 'SMD_FLD_') !== false) { $valfld = str_replace('SMD_FLD_', '', $val); $val = isset($row[$valfld]) ? $row[$valfld] : ''; } if ($val) { $parts = do_list($val, '|'); $parts[0] = $parts[0] ? $parts[0] : '0.00'; $parts[1] = isset($parts[1]) && $parts[1] ? $parts[1] : get_pref('smd_ebook_currency', $smd_ebook_prefs['smd_ebook_currency']['default']); $reps['{smd_ebook_md_srp}'] = '<SRP Currency="' . $parts[1] . '">' . $parts[0] . '</SRP>'; } } // Authornote is slightly different because it needs storing as a file, // and needs adding to the .ncx (but not to the ToC) if (!isset($reps['{smd_ebook_manifest_authornote}'])) { $val = ps('smd_ebook_fld_authornote'); if (strpos($val, 'SMD_FLD_') !== false) { $valfld = str_replace('SMD_FLD_', '', $val); $val = isset($row[$valfld]) ? $row[$valfld] : ''; } if ($val) { $reps['{smd_ebook_manifest_authornote}'] = '<item id="smd_ebook_notes" media-type="application/xhtml+xml" href="' . $notefile . '" />'; $reps['{smd_ebook_spine_authornote}'] = '<itemref idref="smd_ebook_notes" />'; $guide_refs['notes'] = '<reference type="notes" title="' . gTxt('smd_ebook_lbl_authornote') . '" href="' . $notefile . '" />'; $note_content = '<span id="smd_ebook_notes"></span>' . ($txt_authornote ? $textile->TextileThis($val) : $val); // While it's 99% likely the actual title used for the eventual book has been found, // there's a slim chance it hasn't. In that case, the current row's title is used as a fallback $note_title = isset($reps['{smd_ebook_title}']) ? $reps['{smd_ebook_title}'] : $row['Title']; $note_content = str_replace($html_from, array($reps['{smd_ebook_doctype}'], $reps['{smd_ebook_namespace}'], $reps['{smd_ebook_charset}'], $encoding, $note_title, '', $sheet, $note_content), $template['doc']); $fp = fopen($ebook_path . $notefile, "wb"); fwrite($fp, trim($note_content)); fclose($fp); $lfout[] = $notefile; // Add it to the .ncx $ncx_cnt++; $from = array('{smd_ebook_file_name}', '{smd_ebook_nav_label}', '{smd_ebook_nav_hash}', '{smd_ebook_nav_idx}'); $to = array($notefile, gTxt('smd_ebook_lbl_authornote'), 'smd_ebook_notes', $ncx_cnt); $ncx[] = str_replace($from, $to, $template['nav']); } } // Note: // 1) a full (well-formed, hopefully) HTML file (from <html>...</html>) is generated // here so the loadHTML() method is happy. The body will need reinjecting into // the template after the ToC has been generated. // 2) The current HTML file's title is used instead of the overall book title. // 3) parse() is called twice to simulate secondpass. TODO: fix this $chap_title = isset($reps['{smd_ebook_chaptitle}']) ? $reps['{smd_ebook_chaptitle}'] : ''; article_format_info($row); // Load article context $html_content = str_replace($html_from, array($reps['{smd_ebook_doctype}'], $reps['{smd_ebook_namespace}'], $reps['{smd_ebook_charset}'], $encoding, $row['Title'], $chap_title, $sheet, parse(parse($row['Body_html']))), $template['doc']); // Trawl through the HTML content, either: // a) pulling out the given ToC entries. // b) automatically creating ToC entries if the pref allows. // c) finding images to copy into the ePub file structure. $autotoc = get_pref('smd_ebook_auto_toc', $smd_ebook_prefs['smd_ebook_auto_toc']['default']); // Convert the list of heading numbers into a string of numbers suitable // for using inside square brackets of a regex $autohed = get_pref('smd_ebook_auto_toc_headings', $smd_ebook_prefs['smd_ebook_auto_toc_headings']['default']); $autohed = join('', do_list($autohed)); $doc = new DOMDocument(); // Use UNIX line endings to prevent appearing in the saved XML $html_content = preg_replace('/\\r\\n/', "\n", $html_content); $dom_ok = $doc->loadHTML($html_content); if ($dom_ok) { $items = $doc->getElementsByTagName('*'); $offset = $toc_cnt = 0; foreach ($items as $item) { if ($autotoc && !$item->hasAttribute('id') && preg_match('/h([' . $autohed . '])/i', $item->nodeName, $matches)) { // It's a heading. Make the anchor chain based on the heading level $anchor_parts = array_fill(0, $matches[1], 'sub'); $anchor = join('-', $anchor_parts) . ++$elem_cnt; $item->setAttribute('id', $anchor); } if ($item->hasAttribute('id')) { $ncx_cnt++; $toc_cnt++; $hashval = $item->getAttribute('id'); if (!isset($guide_refs['text']) && $toc_cnt === 1) { $guide_href = $firstfile . ($hashval && $is_mobi ? '#' . $hashval : ''); $guide_refs['text'] = '<reference type="text" title="' . gTxt('smd_ebook_lbl_text') . '" href="' . $guide_href . '" />'; $landmarks['bodymatter'] = href(gTxt('smd_ebook_lbl_text'), $guide_href, ' epub:type="bodymatter"'); } // mb_convert_encoding() seems to bypass the odd behaviour where apostrophes // would appear in the TOC as ’. This may actually be a band-aid to circumvent // problems with the encoding in DOMDocument: perhaps if appropriate encoding is // used there, this hack won't be necessary // $node = mb_convert_encoding(trim($item->nodeValue), 'HTML-ENTITIES', 'utf-8'); $node = trim($item->nodeValue); $from = array('{smd_ebook_file_name}', '{smd_ebook_nav_label}', '{smd_ebook_nav_hash}', '{smd_ebook_nav_idx}'); $to = array($cur_file, $node, $hashval, $ncx_cnt); $ncx[] = str_replace($from, $to, $template['nav']); // Now it's the turn of the HTML TOC. Utilise Textile here to // create the toc list from ul or ol syntax $hashBits = do_list($hashval, '-'); $indent = count($hashBits); if ($toc_cnt == 1 && $indent > 1) { // Doesn't start with h1 (begins h2, maybe) so scale back the indent. // Without this, Textile produces invalid markup. $offset = $indent - 1; } $toc_cls = $toc_cnt == 1 && $toc_class ? '(' . $toc_class . ')' : ''; $toc[] = str_pad('', max(1, $indent - $offset), $wrapit) . $toc_cls . ' ' . href($node, $cur_file . '#' . $hashval); } // For ePub books, images need to be extracted separately if ($is_epub && $item->nodeName === 'img') { $src = $item->getAttribute('src'); $bits = pathinfo($src); $mime_type = $bits['extension'] === 'jpg' || $bits['extension'] === 'jpeg' ? 'image/jpeg' : ($bits['extension'] === 'gif' ? 'image/gif' : ($bits['extension'] === 'png' ? 'image/png' : '')); if ($mime_type) { $img = safe_row('*', 'txp_image', "id='" . intval($bits['filename']) . "'"); $img['name'] = trim($img['name']); if ($img) { $ret = copy(get_pref('path_to_site') . DS . $img_dir . DS . $bits['basename'], $ebook_path . $bits['basename']); if ($ret) { if (!in_array($bits['basename'], $master_image_refs)) { $from = array('{smd_ebook_image_link}', '{smd_ebook_image_type}', '{smd_ebook_image_id}'); $to = array('images' . DS . $bits['basename'], $mime_type, 'image-' . $img_cnt); $article_refs[] = str_replace($from, $to, $template['img']); $master_image_refs[] = $bits['basename']; $img_cnt++; } // Add the file to the list of inline images, destined for the .smd file. // This list of images is merged with $lfout _after_ the chapter HTML // content, so a list of images in the chapter appear below it if (!in_array($bits['basename'], $image_list)) { $image_list[] = $bits['basename']; } } } } } } // Grab any changes just made to the DOM tree in case anchors have been added. // Note _only_ the <body> is extracted since the XML headers that come with a full // saveXML() get in the way. Also note that saveHTML() is not being used because its // 'node' parameter wasn't added until PHP 5.3.6 which would affect the plugin's // base requirements. // Hackish: remove the body tag wrapper with substr() so when the html_content // is shoved back into the template (which has a body tag already) there's no // tag duplication $html_content = substr($doc->saveXML($doc->getElementsByTagName('body')->item(0)), 6, -7); // Swap out any line break placeholders. Note that the line break is replaced twice: // once to get rid of any surrounding <p> tags that Textile may have introduced around // the marker, and again in case a few of them didn't get paragraph tags. $html_content = str_replace('<p>' . $pbr . '</p>', '<mbp:pagebreak />', $html_content); $html_content = str_replace($pbr, '<mbp:pagebreak />', $html_content); // Pass the extracted <body> tree into the doc template again so it regenerates // the full <html>...</html> document. $html_content = str_replace($html_from, array($reps['{smd_ebook_doctype}'], $reps['{smd_ebook_namespace}'], $reps['{smd_ebook_charset}'], $encoding, $row['Title'], '', $sheet, $html_content), $template['doc']); } else { trigger_error(gTxt('smd_ebook_malformed'), E_WARNING); } $ebook_item = 'smd_ebook_item_' . $article_cnt; // Guide items require special dispensation and can override built-in items // such as 'text' (a.k.a. bodymatter / start page / welcome / start) and 'toc', // but not cover image or notes. $valid_guide_refs = array('acknowledgments', 'appendix', 'afterword', 'bibliography', 'bodymatter', 'colophon', 'conclusion', 'contributors', 'copyright-page', 'dedication', 'epigraph', 'epilogue', 'errata', 'foreword', 'glossary', 'imprint', 'index', 'introduction', 'loi', 'lot', 'other-credits', 'preamble', 'preface', 'prologue', 'titlepage', 'toc'); $val = 'custom_' . get_pref('smd_ebook_fld_guide'); $val = isset($row[$val]) ? $row[$val] : ''; $guides = do_list($val); foreach ($guides as $guide_ref) { $guide_name = do_list($guide_ref, '|'); $guide_hash = do_list($guide_name[0], '#'); $guide_ref = $lmk_ref = strtolower($guide_hash[0]); if ($guide_ref && in_array($guide_ref, $valid_guide_refs)) { switch ($guide_ref) { case 'bodymatter': $guide_ref = 'text'; break; case 'toc': $toc_file = $cur_file; $toc_ref = $ebook_item; break; } $guide_title = isset($guide_name[1]) ? $guide_name[1] : gTxt('smd_ebook_lbl_' . str_replace('-', '_', $guide_ref)); $guide_href = $cur_file . (isset($guide_hash[1]) ? '#' . $guide_hash[1] : ''); $guide_refs[$guide_ref] = '<reference type="' . $guide_ref . '" title="' . $guide_title . '" href="' . $guide_href . '" />'; $landmarks[$lmk_ref] = href($guide_title, $guide_href, ' epub:type="' . $lmk_ref . '"'); } } // Write the final HTML document to the file system $fp = fopen($ebook_path . $cur_file, "wb"); fwrite($fp, trim($html_content)); fclose($fp); $lfout[] = $cur_file; $lfout = array_merge($lfout, $image_list); $article_refs[] = '<item id="' . $ebook_item . '" media-type="application/xhtml+xml" href="' . $row['url_title'] . '.html" />'; $article_spines[] = '<itemref idref="' . $ebook_item . '" />'; } } // Ensure any NULL replacements are cleared or throw errors $reps['{smd_ebook_opf_file}'] = !isset($reps['{smd_ebook_opf_file}']) ? '' : $reps['{smd_ebook_opf_file}']; $reps['{smd_ebook_doctype}'] = !isset($reps['{smd_ebook_doctype}']) ? '' : $reps['{smd_ebook_doctype}']; $reps['{smd_ebook_namespace}'] = !isset($reps['{smd_ebook_namespace}']) ? '' : $reps['{smd_ebook_namespace}']; $reps['{smd_ebook_charset}'] = !isset($reps['{smd_ebook_charset}']) ? '' : $reps['{smd_ebook_charset}']; $reps['{smd_ebook_chaptitle}'] = !isset($reps['{smd_ebook_chaptitle}']) ? '' : $reps['{smd_ebook_chaptitle}']; $reps['{smd_ebook_creator}'] = !isset($reps['{smd_ebook_creator}']) ? '' : $reps['{smd_ebook_creator}']; $reps['{smd_ebook_md_creator}'] = !isset($reps['{smd_ebook_md_creator}']) ? '' : $reps['{smd_ebook_md_creator}']; $reps['{smd_ebook_md_description}'] = !isset($reps['{smd_ebook_md_description}']) ? '' : $reps['{smd_ebook_md_description}']; $reps['{smd_ebook_md_subject}'] = !isset($reps['{smd_ebook_md_subject}']) ? '' : $reps['{smd_ebook_md_subject}']; $reps['{smd_ebook_md_publisher}'] = !isset($reps['{smd_ebook_md_publisher}']) ? '' : $reps['{smd_ebook_md_publisher}']; $reps['{smd_ebook_md_srp}'] = !isset($reps['{smd_ebook_md_srp}']) ? '' : $reps['{smd_ebook_md_srp}']; if (!isset($reps['{smd_ebook_md_cover}'])) { $reps['{smd_ebook_md_cover}'] = ''; $reps['{smd_ebook_manifest_cover}'] = ''; } if (!isset($reps['{smd_ebook_manifest_authornote}'])) { $reps['{smd_ebook_manifest_authornote}'] = ''; $reps['{smd_ebook_spine_authornote}'] = ''; } // All the replacements are set up so prepare for book generation // First, create the TOC and write it to the filesystem if ($toc_cnt > 0) { $reps['{smd_ebook_spine_toc}'] = '<itemref idref="' . $toc_ref . '" />'; if (!isset($guide_refs['toc'])) { $reps['{smd_ebook_manifest_toc}'] = '<item id="' . $toc_ref . '" media-type="application/xhtml+xml" href="' . $toc_file . '" />'; $guide_refs['toc'] = '<reference type="toc" title="' . gTxt('smd_ebook_lbl_toc') . '" href="' . $toc_file . '" />'; $landmarks['toc'] = href(gTxt('smd_ebook_lbl_toc'), $toc_file, ' epub:type="toc"'); $html_toc = $textile->TextileThis(join(n, $toc)); $final_toc = str_replace(array('{smd_ebook_toc_list}', '{smd_ebook_stylesheet}', '{smd_ebook_toc}'), array($html_toc, $sheet, gTxt('smd_ebook_lbl_toc')), $template['toc']); $fp = fopen($ebook_path . $toc_file, "wb"); fwrite($fp, trim($final_toc)); fclose($fp); $lfout[] = $toc_file; } else { $reps['{smd_ebook_manifest_toc}'] = ''; } } else { $reps['{smd_ebook_manifest_toc}'] = ''; $reps['{smd_ebook_spine_toc}'] = ''; } // Add the ncx waypoints to the reps array and generate the .ncx file if ($ncx_cnt > 0) { $reps['{smd_ebook_ncx_doctype}'] = $is_mobi ? '<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">' : ''; $reps['{smd_ebook_ncx_map}'] = join(n, $ncx); $reps['{smd_ebook_manifest_ncx}'] = '<item id="ncx" media-type="application/x-dtbncx+xml" href="' . $ncx_file . '" />'; // $reps['{smd_ebook_spine_ncx}'] = '<itemref idref="ncx" />'; $reps['{smd_ebook_spine_ncx_ref}'] = 'toc="ncx"'; $ncx_file_content = trim(strtr($template['ncx'], $reps)); $fp = fopen($ebook_path . $ncx_file, "wb"); fwrite($fp, $ncx_file_content); fclose($fp); $lfout[] = $ncx_file; } else { $reps['{smd_ebook_manifest_ncx}'] = ''; // $reps['{smd_ebook_spine_ncx}'] = ''; $reps['{smd_ebook_spine_ncx_ref}'] = ''; } if ($is_epub) { // Create supplemental files for ePub format // First the mimetype $fp = fopen($ebook_path . $mimetype_file, "wb"); fwrite($fp, 'application/epub+zip'); fclose($fp); // Then the container $fp = fopen($ebook_path . $container_file, "wb"); $from = array('{smd_ebook_opf_file}'); $to = array($opf_file); fwrite($fp, str_replace($from, $to, $template['inf'])); fclose($fp); // Then the landmarks $lmk_list = array(); foreach ($landmarks as $type => $landmark) { $lmk_list[] = tag($landmark, 'li'); } $fp = fopen($ebook_path . $lmk_file, "wb"); $from = array('{smd_ebook_guide}', '{smd_ebook_encoding}', '{smd_ebook_lang}', '{smd_ebook_landmarks}'); $to = array(gTxt('smd_ebook_guide'), $encoding, $lang, join(n . t, $lmk_list)); fwrite($fp, str_replace($from, $to, $template['lmk'])); fclose($fp); $article_refs[] = '<item id="landmarks" media-type="application/xhtml+xml" href="' . $lmk_file . '" />'; $lfout[] = $lmk_file; // Then any stylesheets foreach ($sheetcontent as $sheet => $content) { $fp = fopen($ebook_path . $sheet . '.css', "wb"); fwrite($fp, $content); fclose($fp); } // Transform .ncx to .end, if necessary, and generate manifest + spine entries. // END (Epub Navigation Document) supersedes the deprecated NCX in ePub3. if ($ncx_cnt > 0) { if (class_exists('XSLTProcessor')) { $xslt = new XSLTProcessor(); $xslt->importStylesheet(new SimpleXMLElement($template['ncx2end'])); $ebook_end = $xslt->transformToXml(new SimpleXMLElement($ncx_file_content)); } else { $ebook_end = str_replace(array('{smd_ebook_toc_list}', '{smd_ebook_toc}'), array($html_toc, gTxt('smd_ebook_lbl_toc')), $template['end']); } $fp = fopen($ebook_path . $end_file, "wb"); fwrite($fp, trim($ebook_end)); fclose($fp); $lfout[] = $end_file; } $reps['{smd_ebook_md_x}'] = ''; $reps['{smd_ebook_guide_cover}'] = '<reference type="cover" title="Cover" href="cover.html" />'; $reps['{smd_ebook_spine_cover}'] = '<itemref idref="cover" linear="no" />'; $reps['{smd_ebook_manifest_end}'] = '<item id="end" properties="nav" href="' . $end_file . '" media-type="application/xhtml+xml" />'; } elseif ($is_mobi) { $reps['{smd_ebook_md_x}'] = str_replace(array('{smd_ebook_encoding}', '{smd_ebook_md_srp}'), array($encoding, $reps['{smd_ebook_md_srp}']), $template['xmd']); $reps['{smd_ebook_guide_cover}'] = ''; $reps['{smd_ebook_spine_cover}'] = ''; $reps['{smd_ebook_manifest_end}'] = ''; $reps['{smd_ebook_landmark_nav}'] = ''; } // Build the remaining manifest replacements and generate the OPF $reps['{smd_ebook_guide_extras}'] = $guide_refs ? join(n . t . t, $guide_refs) : ''; $reps['{smd_ebook_manifest_items}'] = join(n . t . t, $article_refs); $reps['{smd_ebook_spine_items}'] = join(n, $article_spines); $opf_file_content = strtr($template['opf'], $reps); $fp = fopen($ebook_path . $opf_file, "wb"); fwrite($fp, trim($opf_file_content)); fclose($fp); $lfout[] = $opf_file; // Write the listfile, which contains a list of all the files used in this stage $fp = fopen($ebook_path . $listfile, "wb"); fwrite($fp, join(n, $lfout)); fclose($fp); // Hand off to Stage 2 to do the deed smd_ebook_generate($listfile, $opf_file, $bType, $ebook_folder); }
function zem_event_list($atts, $thing = NULL) { global $zem_thiseventcal, $pretext; extract(lAtts(array('wraptag' => '', 'class' => __FUNCTION__, 'break' => '', 'breakclass' => '', 'form' => 'zem_event_display', 'sort' => safe_pfx('zem_event_date') . '.event_date asc, ' . safe_pfx('zem_event_date') . '.event_time asc', 'date_from' => gps('date_from') ? gps('date_from') : 'today', 'date_to' => gps('date_to') ? gps('date_to') : '', 'date' => gps('date'), 'label' => '', 'labeltag' => '', 'limit' => '', 'category' => gps('c') ? gps('c') : @$pretext['c'], 'section' => '', 'all_categories' => gps('all_categories'), 'location' => gps('location'), 'all_locations' => gps('all_locations'), 'debug' => 0), $atts)); if ($thing === NULL) { $thing = fetch_form($form); } $where = safe_pfx('zem_event_calendar') . '.id=' . safe_pfx('zem_event_date') . '.event_id and ' . safe_pfx('zem_event_calendar') . '.article_id = ' . safe_pfx('textpattern') . '.ID and ' . safe_pfx('textpattern') . '.Status >= 4 and ' . safe_pfx('textpattern') . '.Posted <= now()'; if ($date) { @(list($y, $m, $d) = explode('-', $date)); if ($y and $m and $d) { $date_from = $date_to = "{$y}-{$m}-{$d}"; } elseif ($y and $m) { $date_from = "{$y}-{$m}-01"; $date_to = strftime('%Y-%m-%d', strtotime('-1 day', strtotime('+1 month', strtotime($date_from)))); } elseif ($y) { $date_from = "{$y}-01-01"; $date_to = "{$y}-12-31"; } elseif ($t = zem_strtotime($date)) { $date_from = strftime('%Y-%m-%d', $t); $date_to = strftime('%Y-%m-%d', $t); } } $w = zem_event_timeq($date_from, $date_to); if ($w) { $where .= ' and ' . join(' and ', $w); } if ($section) { $sections = do_list($section); $where .= " and textpattern.Section IN (" . join(',', quote_list($sections)) . ")"; } if ($location and !$all_locations) { // location could be an array if it came from gps() if (is_array($location)) { $locs = $location; } else { $locs = do_list($location); } $where .= " and location IN (" . join(',', quote_list($locs)) . ")"; } if ($q = gps('q')) { $where .= " and (name rlike '" . doSlash($q) . "' or description rlike '" . doSlash($q) . "')"; } if ($category and !$all_categories) { // category could be an array if it came from gps() if (is_array($category)) { $cats = $category; } else { $cats = do_list($category); } $cats_id = safe_column('id', 'txp_category', "type='event' and name IN (" . join(',', quote_list($cats)) . ")"); if (!$cats_id) { $cats_id = array(0); } $where = safe_pfx('zem_event_calendar') . ".id=" . safe_pfx('zem_event_category') . ".k1 and " . safe_pfx('zem_event_category') . ".k2 IN (" . join(',', quote_list($cats_id)) . ") and " . $where; $grand_total = safe_count('zem_event_calendar,zem_event_date,textpattern,zem_event_category', $where . ' group by ' . safe_pfx('zem_event_calendar') . '.id order by ' . $sort, $debug); $lim = zem_event_paginate($limit, $grand_total); $rs = safe_rows_start(safe_pfx('zem_event_calendar') . '.*, ' . safe_pfx('zem_event_date') . '.*, ' . safe_pfx('textpattern') . '.*, unix_timestamp(Posted) as uPosted', 'zem_event_calendar,zem_event_date,textpattern,zem_event_category', $where . ' group by ' . safe_pfx('zem_event_calendar') . '.id order by ' . $sort . $lim, $debug); } else { $grand_total = safe_count('zem_event_calendar,zem_event_date,textpattern', $where . ' order by ' . $sort, $debug); $lim = zem_event_paginate($limit, $grand_total); $rs = safe_rows_start('*, unix_timestamp(Posted) as uPosted', 'zem_event_calendar,zem_event_date,textpattern', $where . ' order by ' . $sort . $lim, $debug); } $out = array(); while ($row = nextRow($rs)) { article_push(); $zem_thiseventcal = $row; populateArticleData($row); $out[] = parse($thing); $zem_thiseventcal = NULL; article_pop(); } return doTag($label, $labeltag, $class) . doWrap($out, $wraptag, $break, $class, $breakclass); }