function xthreads_upload_attachments() { global $xta_cache, $threadfield_cache, $mybb, $db, $lang, $fid; // only ever execute this function once per page static $done = false; if ($done) { return; } $done = true; if (!$fid) { if ($GLOBALS['forum']['fid']) { $fid = $GLOBALS['forum']['fid']; } elseif ($GLOBALS['foruminfo']['fid']) { $fid = $GLOBALS['foruminfo']['fid']; } elseif ($mybb->input['pid']) { // editpost - not good to trust user input, but should be fine $post = get_post((int) $mybb->input['pid']); if ($post['pid']) { $fid = $post['fid']; } } elseif ($mybb->input['fid']) { // newthread $fid = (int) $mybb->input['fid']; } // we _should_ now have an fid } if (!isset($threadfield_cache)) { $threadfield_cache = xthreads_gettfcache($fid); } // remove uneditable fields xthreads_filter_tfeditable($threadfield_cache, $fid); // NOTE: modifies the global tfcache! if (empty($threadfield_cache)) { return; } if (!is_array($xta_cache)) { $xta_cache = array(); } // first, run through to see if we have already uploaded some attachments // this code totally relies on the posthash being unique... if ($GLOBALS['thread']['tid']) { $attachwhere = 'tid=' . (int) $GLOBALS['thread']['tid']; } else { $attachwhere = 'posthash="' . $db->escape_string($mybb->input['posthash']) . '"'; } $query = $db->simple_select('xtattachments', '*', $attachwhere); $attach_fields = array(); while ($attach = $db->fetch_array($query)) { $xta_cache[$attach['aid']] = $attach; $attach_fields[$attach['field']][] = $attach['aid']; } $db->free_result($query); @ignore_user_abort(true); $errors = array(); $xta_remove = $threadfield_updates = array(); foreach ($threadfield_cache as $k => &$v) { if ($v['inputtype'] != XTHREADS_INPUT_FILE && $v['inputtype'] != XTHREADS_INPUT_FILE_URL) { continue; } $aid =& $mybb->input['xthreads_' . $k]; if ($v['inputtype'] != XTHREADS_INPUT_FILE_URL || is_numeric($mybb->input['xthreads_' . $k])) { $singleval = xthreads_empty($v['multival']); // now, we're ignoring what the user sends us, totally... if ($attach_fields[$k]) { if ($singleval) { $aid = (int) reset($attach_fields[$k]); } else { $aid = array_unique(array_map('intval', $attach_fields[$k])); // re-ordering support if (is_array($mybb->input['xtaorder'])) { $aid_order = array_unique(array_map('intval', $mybb->input['xtaorder'])); if (count($aid) == count($aid_order) && $aid != $aid_order && !count(array_diff($aid, $aid_order))) { $aid = $aid_order; $threadfield_updates[$k] = implode(',', $aid); } } $aid = array_combine($aid, $aid); } } else { $aid = 0; } } // handle file upload $ul = null; if ($singleval) { if (!empty($_FILES['xthreads_' . $k]) && !xthreads_empty($_FILES['xthreads_' . $k]['name']) && is_string($_FILES['xthreads_' . $k]['name'])) { $ul = $_FILES['xthreads_' . $k]; } elseif ($v['inputtype'] == XTHREADS_INPUT_FILE && XTHREADS_ALLOW_URL_FETCH && !xthreads_empty($mybb->input['xtaurl_' . $k])) { // the preg_match is just a basic prelim check - the real URL checking is done later; we need this prelim check to stop it erroring out on the defalt "http://" string if (preg_match('~^[a-z0-9\\-]+\\://[^/?#]+(?:/.*)?$~', $mybb->input['xtaurl_' . $k])) { $ul = $mybb->input['xtaurl_' . $k]; } } !isset($ul) or $ul = array($ul); } else { $ul = array(); if (is_array($mybb->input['xtaurl_' . $k])) { $input_urls = $mybb->input['xtaurl_' . $k]; $input_key_match = true; // if URL input is an array, we'll match with equivalent file input keys } else { $input_urls = explode("\n", str_replace("\r", '', $mybb->input['xtaurl_' . $k])); $input_key_match = false; } if (!empty($_FILES['xthreads_' . $k]) && is_array($_FILES['xthreads_' . $k])) { foreach ($_FILES['xthreads_' . $k]['name'] as $file_k => $filename) { if (!xthreads_empty($filename)) { $file_v = array(); // why does PHP does this and make our life difficult? foreach ($_FILES['xthreads_' . $k] as $fvkey => $fvval) { $file_v[$fvkey] = $fvval[$file_k]; } if ($input_key_match && is_numeric($file_k) || preg_match('~^aid\\d+$~', $file_k)) { $ul[$file_k] = $file_v; } else { $ul[] = $file_v; } } } } if ($v['inputtype'] == XTHREADS_INPUT_FILE && XTHREADS_ALLOW_URL_FETCH && !empty($input_urls) && is_array($input_urls)) { foreach ($input_urls as $url_k => $url_v) { $url_v = trim($url_v); if (preg_match('~^[a-z0-9\\-]+\\://[a-z0-9_\\-@:.]+(?:/.*)?$~', $url_v)) { if ($input_key_match && is_numeric($url_k) || preg_match('~^aid\\d+$~', $url_k)) { isset($ul[$url_k]) or $ul[$url_k] = $url_v; } else { $ul[] = $url_v; } } } } } unset($mybb->input['xtaurl_' . $k], $_FILES['xthreads_' . $k]); // remove files from list first (so we can properly measure the correct final number of attachments when uploading) // fix the threadfield_updates array later on if ($singleval) { if (empty($ul) && $mybb->input['xtarm_' . $k] && $v['editable'] != XTHREADS_EDITABLE_REQ) { // user wants to remove attachment $xta_remove[$aid] = $aid; $aid = 0; } } elseif (!empty($mybb->input['xtarm_' . $k]) && is_array($mybb->input['xtarm_' . $k])) { foreach ($mybb->input['xtarm_' . $k] as $rm_aid => $rm_confirm) { if (!$rm_confirm) { continue; } // double-check they really do want to remove this $xta_remove[$rm_aid] = $rm_aid; unset($aid[$rm_aid]); } } // upload new stuff if (!empty($ul)) { require_once MYBB_ROOT . 'inc/xthreads/xt_upload.php'; $update_aid = is_array($aid) ? 0 : $aid; $failed_urls = array(); // list of any URLs that failed to fetch foreach ($ul as $ul_key => $ul_file) { // hard limit number of files to at least 20 if (!$singleval && is_array($aid)) { // hard limit if (strlen(implode(',', $aid)) >= 245) { if (!$lang->xthreads_xtaerr_error_attachhardlimit) { $lang->load('xthreads'); } $errors[] = $lang->sprintf($lang->xthreads_xtaerr_error_attachhardlimit, htmlspecialchars_uni($v['title'])); break; } // admin defined limit if ($v['multival_limit'] && count($aid) >= $v['multival_limit']) { if (!$lang->xthreads_xtaerr_error_attachnumlimit) { $lang->load('xthreads'); } $errors[] = $lang->sprintf($lang->xthreads_xtaerr_error_attachnumlimit, $v['multival_limit'], htmlspecialchars_uni($v['title'])); break; } } // allow updating a specific attachment in a multi-field thing $update_aid2 = $update_aid; if (!$update_aid2 && is_array($aid) && substr($ul_key, 0, 3) == 'aid') { $update_aid2 = (int) substr($ul_key, 3); if (!in_array($update_aid2, $aid)) { $update_aid2 = 0; } } $attachedfile = upload_xtattachment($ul_file, $v, $mybb->user['uid'], $update_aid2, $GLOBALS['thread']['tid']); if ($attachedfile['error']) { if (!$lang->xthreads_threadfield_attacherror) { $lang->load('xthreads'); } $errors[] = $lang->sprintf($lang->xthreads_threadfield_attacherror, htmlspecialchars_uni($v['title']), $attachedfile['error']); if (is_string($ul_file)) { $failed_urls[] = $ul_file; } } else { //unset($attachedfile['posthash'], $attachedfile['tid'], $attachedfile['downloads']); $xta_cache[$attachedfile['aid']] = $attachedfile; if ($singleval) { unset($mybb->input['xtarm_' . $k]); // since successful upload, don't tick remove box $aid = $attachedfile['aid']; } else { if (is_array($mybb->input['xtarm_' . $k])) { unset($mybb->input['xtarm_' . $k][$attachedfile['aid']]); } is_array($aid) or $aid = array(); // if no aid already set, it will be 0, so turn into array if necessary $aid[$attachedfile['aid']] = $attachedfile['aid']; } // if we were going to remove this file, don't if (isset($xta_remove[$attachedfile['aid']])) { unset($xta_remove[$attachedfile['aid']]); } if ($attachedfile['aid'] != $update_aid2) { // adding a new attachment $threadfield_updates[$k] = $singleval ? $aid : true; } } } // list failed URLs in textboxes if (!empty($failed_urls)) { $mybb->input['xtaurl_' . $k] = implode("\n", $failed_urls); unset($failed_urls); } } // fix threadfield update if removing an item and not already done if (!empty($xta_remove) && !isset($threadfield_updates[$k])) { $threadfield_updates[$k] = $singleval ? 0 : true; } // fix placeholder value if ($threadfield_updates[$k] === true) { $threadfield_updates[$k] = implode(',', $aid); } unset($aid); } if (!empty($xta_remove)) { $db->delete_query('xtattachments', 'aid IN (' . implode(',', $xta_remove) . ')'); foreach ($xta_remove as $aid) { xthreads_rm_attach_fs($xta_cache[$aid]); } } $is_editing = $GLOBALS['current_page'] == 'editpost.php'; // if editing post, also commit change to thread field immediately (waiting for user to submit is unreliable) if (($is_editing || $GLOBALS['thread']['tid'] && $GLOBALS['current_page'] == 'newthread.php') && !empty($threadfield_updates)) { xthreads_db_update_replace('threadfields_data', $threadfield_updates, 'tid', $GLOBALS['thread']['tid']); } @ignore_user_abort(false); if (!empty($errors)) { global $plugins; $is_mybb_18 = $mybb->version_code >= 1700; // MyBB 1.4 - 1.5 // and MyBB 1.6 is inconsistent (does different things on newthread/editpost)... if ($mybb->version_code < 1600 || $is_editing) { // can't find a better way to check other than to check version numbers global $theme, $templates; $errstr = '<li>' . implode('</li><li>', $errors) . '</li>'; if ($is_editing && $is_mybb_18) { // special workaround for MyBB 1.8 $GLOBALS['xt_attach_errors'] =& $errstr; $templates->cache['__xt_orig_error_attacherror'] = $templates->cache['error_attacherror']; function xthreads_upload_attachments_error() { global $theme, $templates, $attacherror, $mybb, $lang, $fid; if ($attacherror) { return; } // already handled by template cache hack $attachedfile = array('error' => '<ul>' . $GLOBALS['xt_attach_errors'] . '</ul>'); eval('$attacherror = "' . $templates->get('__xt_orig_error_attacherror') . '";'); } $plugins->add_hook('editpost_action_start', 'xthreads_upload_attachments_error'); } else { $attachedfile = array('error' => '<ul>' . $errstr . '</ul>'); eval('$GLOBALS[\'attacherror\'] .= "' . $templates->get('error_attacherror') . '";'); } // if there's going to be a MyBB attachment error, and it's not been evaluated yet, shove it in the template to force it through - safe since this function is guaranteed to run only once $templates->cache['error_attacherror'] = str_replace('{$attachedfile[\'error\']}', '<ul>' . strtr($errstr, array('\\' => '\\\\', '$' => '\\$', '{' => '\\{', '}' => '\\}')) . '<li>{$attachedfile[\'error\']}</li></ul>', $templates->cache['error_attacherror']); } elseif ($is_mybb_18) { // for MyBB 1.8 $GLOBALS['xt_attach_errors'] =& $errors; function xthreads_upload_attachments_error() { if (empty($GLOBALS['errors'])) { $GLOBALS['errors'] =& $GLOBALS['xt_attach_errors']; } else { $GLOBALS['errors'] = array_merge($GLOBALS['errors'], $GLOBALS['xt_attach_errors']); } } $plugins->add_hook('newthread_start', 'xthreads_upload_attachments_error'); } else { // for MyBB 1.6 if (empty($GLOBALS['errors'])) { $GLOBALS['errors'] =& $errors; } else { $GLOBALS['errors'] = array_merge($GLOBALS['errors'], $errors); } } $mybb->input['action'] = $is_editing ? 'editpost' : 'newthread'; // block the preview, since a failed upload can stuff it up // lower priority to go before inputdisp function (contention, the function checks for 'previewpost') $plugins->add_hook('newthread_start', 'xthreads_newthread_ulattach_blockpreview', 5); $plugins->add_hook('editpost_start', 'xthreads_editthread_ulattach_blockpreview', 5); } }
function xthreads_moderation_custom_do(&$tids, $editstr) { if (!$editstr) { return; } $edits = array(); // caching stuff static $threadfields = null; if (!isset($threadfields)) { $threadfields = xthreads_gettfcache(); } // grab all threadfields require_once MYBB_ROOT . 'inc/xthreads/xt_phptpl_lib.php'; foreach (explode("\n", str_replace("{\n}", "\r", str_replace("\r", '', $editstr))) as $editline) { $editline = trim(str_replace("\r", "\n", $editline)); list($n, $v) = explode('=', $editline, 2); if (!isset($v)) { continue; } // don't allow editing of file fields if (!isset($threadfields[$n]) || $threadfields[$n]['inputtype'] == XTHREADS_INPUT_FILE) { continue; } // we don't do much validation here as we trust admins, right? // this is just a prelim check (speed optimisation) - we'll need to check this again after evaluating conditionals $upperv = strtoupper($v); if (($upperv === '' || $upperv == 'NULL' || $upperv == 'NUL') && $threadfields[$n]['datatype'] != XTHREADS_DATATYPE_TEXT) { $edits[$n] = null; } else { $edits[$n] = $v; xthreads_sanitize_eval($edits[$n], array('VALUE' => null, 'TID' => null)); } } if (empty($edits)) { return; } $modfields = array_keys($edits); global $db; $query = $db->query(' SELECT t.tid, tfd.`' . implode('`, tfd.`', $modfields) . '` FROM ' . TABLE_PREFIX . 'threads t LEFT JOIN ' . TABLE_PREFIX . 'threadfields_data tfd ON t.tid=tfd.tid WHERE t.tid IN (' . implode(',', $tids) . ') '); //$query = $db->simple_select('threadfields_data', 'tid,`'.implode('`,`', $modfields).'`', 'tid IN ('.implode(',', $tids).')'); while ($thread = $db->fetch_array($query)) { $updates = array(); foreach ($edits as $n => $v) { if ($v !== null) { // TODO: allowing conditionals direct access to multivals? $v = trim(eval_str($v, array('VALUE' => $thread[$n], 'TID' => $thread['tid']))); if ($threadfields[$n]['datatype'] != XTHREADS_DATATYPE_TEXT) { $upperv = strtoupper($v); if ($upperv == '' || $upperv == 'NULL' || $upperv == 'NUL') { $v = null; } // TODO: intval/floatval here? } } if ($v !== $thread[$n]) { // we'll do some basic validation for multival fields if (!xthreads_empty($threadfields[$n]['multival'])) { $d = "\n"; if ($threadfields[$n]['inputtype'] == XTHREADS_INPUT_TEXT) { $d = ','; } $v = array_unique(array_map('trim', explode($d, str_replace("\r", '', $v)))); foreach ($v as $key => &$val) { if (xthreads_empty($val)) { unset($v[$key]); } } $v = implode($d, $v); } $updates[$n] = $v; } } if (!empty($updates)) { xthreads_db_update_replace('threadfields_data', $updates, 'tid', $thread['tid']); } } $db->free_result($query); }