/** Responds to a request to create a new user.
  *
  **/
 public function actionRegistration()
 {
     //We need at least a username, email, and password.
     if (empty($_REQUEST['username']) or empty($_REQUEST['password']) or empty($_REQUEST['email'])) {
         $this->sendAsJson(array('error' => 'insufficient data'));
         return;
     }
     $username = trim($_REQUEST['username']);
     $password = trim($_REQUEST['password']);
     $postdata = array('username' => $username, 'email' => $_REQUEST['email']);
     if (isset($_REQUEST['month']) and isset($_REQUEST['day']) and !empty($_REQUEST['year'])) {
         $postdata['birthday'] = $_REQUEST['year'] . '-' . str_pad($_REQUEST['month'], 2, '0', STR_PAD_LEFT) . '-' . str_pad($_REQUEST['day'], 2, '0', STR_PAD_LEFT);
     }
     if (!empty($_REQUEST['guardian'])) {
         $postdata['parentemail'] = $_REQUEST['guardian'];
     }
     $vboptions = vB5_Template_Options::instance()->getOptions();
     $vboptions = $vboptions['options'];
     // Coppa cookie check
     $coppaage = vB5_Cookie::get('coppaage', vB5_Cookie::TYPE_STRING);
     if ($vboptions['usecoppa'] and $vboptions['checkcoppa']) {
         if ($coppaage) {
             $dob = explode('-', $coppaage);
             $month = $dob[0];
             $day = $dob[1];
             $year = $dob[2];
             $postdata['birthday'] = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-' . str_pad($day, 2, '0', STR_PAD_LEFT);
         } else {
             vB5_Cookie::set('coppaage', $_REQUEST['month'] . '-' . $_REQUEST['day'] . '-' . $_REQUEST['year'], 365, 0);
         }
     }
     // Fill in ReCaptcha data
     $recaptchaData = array();
     if (!empty($_REQUEST['recaptcha_challenge_field'])) {
         $recaptchaData['recaptcha_challenge_field'] = $_REQUEST['recaptcha_challenge_field'];
     }
     if (!empty($_REQUEST['recaptcha_response_field'])) {
         $recaptchaData['recaptcha_response_field'] = $_REQUEST['recaptcha_response_field'];
     }
     if (!empty($recaptchaData)) {
         $_REQUEST['humanverify'] = $recaptchaData + (isset($_REQUEST['humanverify']) ? (array) $_REQUEST['humanverify'] : array());
     }
     $api = Api_InterfaceAbstract::instance();
     $data = array('userid' => 0, 'password' => $password, 'user' => $postdata, array(), array(), 'userfield' => !empty($_REQUEST['userfield']) ? $_REQUEST['userfield'] : false, array(), isset($_REQUEST['humanverify']) ? $_REQUEST['humanverify'] : '', array('registration' => true));
     // add facebook data
     if ($api->callApi('facebook', 'isFacebookEnabled') && $api->callApi('facebook', 'userIsLoggedIn')) {
         $fbUserInfo = $api->callApi('facebook', 'getFbUserInfo');
         $data['user']['fbuserid'] = $fbUserInfo['id'];
         $data['user']['fbname'] = $fbUserInfo['name'];
         $data['user']['timezoneoffset'] = $fbUserInfo['timezone'];
         $data['user']['fbjoindate'] = time();
         $fb_profilefield_info = $this->getFacebookProfileinfo($fbUserInfo);
         if (!empty($fb_profilefield_info['birthday']) and empty($data['user']['birthday'])) {
             $data['user']['birthday'] = $fb_profilefield_info['birthday'];
         }
         if (empty($data['userfield'])) {
             $data['userfield'] = array();
         }
         if ($vboptions['fb_userfield_biography']) {
             $data['userfield'] += array($vboptions['fb_userfield_biography'] => $fb_profilefield_info['biography']);
         }
         if ($vboptions['fb_userfield_location']) {
             $data['userfield'] += array($vboptions['fb_userfield_location'] => $fb_profilefield_info['location']);
         }
         if ($vboptions['fb_userfield_occupation']) {
             $data['userfield'] += array($vboptions['fb_userfield_occupation'] => $fb_profilefield_info['occupation']);
         }
     }
     // save data
     $response = $api->callApi('user', 'save', $data);
     if (!empty($response) and (!is_array($response) or !isset($response['errors']))) {
         // try to login
         $loginInfo = $api->callApi('user', 'login', array($username, $password, '', '', ''));
         if (!isset($loginInfo['errors']) or empty($loginInfo['errors'])) {
             // browser session expiration
             vB5_Cookie::set('sessionhash', $loginInfo['sessionhash'], 0, true);
             vB5_Cookie::set('password', $loginInfo['password'], 0);
             vB5_Cookie::set('userid', $loginInfo['userid'], 0);
             $urlPath = '';
             if (!empty($_POST['urlpath'])) {
                 $urlPath = base64_decode(trim($_POST['urlpath']), true);
             }
             if (!$urlPath or strpos($urlPath, '/auth/') !== false or strpos($urlPath, '/register') !== false or !vB5_Template_Runtime::allowRedirectToUrl($urlPath)) {
                 $urlPath = vB5_Template_Options::instance()->get('options.frontendurl');
             }
             $response = array('urlPath' => $urlPath);
         } else {
             if (!empty($loginInfo['errors'])) {
                 $response = array('errors' => $loginInfo['errors']);
             }
         }
         if ($api->callApi('user', 'usecoppa')) {
             $response['usecoppa'] = true;
             $response['urlPath'] = vB5_Route::buildUrl('coppa-form|bburl');
         } else {
             if ($vboptions['verifyemail']) {
                 $response['msg'] = 'registeremail';
                 $response['msg_params'] = array(vB5_String::htmlSpecialCharsUni($postdata['username']), $postdata['email'], vB5_Template_Options::instance()->get('options.frontendurl'));
             } else {
                 if ($vboptions['moderatenewmembers']) {
                     $response['msg'] = 'moderateuser';
                     $response['msg_params'] = array(vB5_String::htmlSpecialCharsUni($postdata['username']), vB5_Template_Options::instance()->get('options.frontendurl'));
                 } else {
                     $frontendurl = vB5_Template_Options::instance()->get('options.frontendurl');
                     $routeProfile = $api->callApi('route', 'getUrl', array('route' => 'profile', 'data' => array('userid' => $loginInfo['userid']), array()));
                     $routeuserSettings = $api->callApi('route', 'getUrl', array('route' => 'settings', 'data' => array('tab' => 'profile'), array()));
                     $routeAccount = $api->callApi('route', 'getUrl', array('route' => 'settings', 'data' => array('tab' => 'account'), array()));
                     $response['msg'] = 'registration_complete';
                     $response['msg_params'] = array(vB5_String::htmlSpecialCharsUni($postdata['username']), $frontendurl . $routeProfile, $frontendurl . $routeAccount, $frontendurl . $routeuserSettings, $frontendurl);
                 }
             }
         }
     }
     $this->sendAsJson(array('response' => $response));
 }
 /**
  * Callback for preg_replace_callback used in handle_bbcode_html
  */
 protected function handleBBCodeTagPregMatch2($matches)
 {
     return $this->handle_bbcode_html_tag(vB5_String::htmlSpecialCharsUni($matches[1]));
 }
示例#3
0
 /**
  * Callback for preg_replace_callback in handle_bbcode_img
  */
 protected function attachReplaceCallback($matches)
 {
     $currentUserid = vb::getUserContext()->fetchUserId();
     $align = $matches[1];
     $showOldImage = false;
     $tempid = false;
     // used if this attachment hasn't been saved yet (ex. going back & forth between source mode & wysiwyg on a new content)
     $filedataid = false;
     $attachmentid = false;
     // Same as before: are we looking at a legacy attachment?
     if (preg_match('#^n(\\d+)$#', $matches[2], $matches2)) {
         // if the id has 'n' as prefix, it's a nodeid
         $attachmentid = intval($matches2[1]);
     } else {
         if (preg_match('#^temp_(\\d+)_(\\d+)_(\\d+)$#', $matches[2], $matches2)) {
             // if the id is in the form temp_##_###_###, it's a temporary id that links to hidden inputs that contain
             // the stored settings that will be saved when it becomes a new attachment @ post save.
             $tempid = $matches2[0];
             $filedataid = intval($matches2[1]);
         } else {
             // it's a legacy attachmentid, get the new id
             if (isset($this->oldAttachments[intval($matches[2])])) {
                 // key should be nodeid, not filedataid.
                 $attachmentid = $this->oldAttachments[intval($matches[2])]['nodeid'];
                 //$showOldImage = $this->oldAttachments[intval($matches[2])]['cangetattachment'];
             }
         }
     }
     if ($attachmentid === false and $tempid === false) {
         // No data match was found for the attachment, so just return nothing
         return '';
     } else {
         if ($attachmentid and !empty($this->attachments["{$attachmentid}"])) {
             // attachment specified by [attach] tag belongs to this post
             $attachment =& $this->attachments["{$attachmentid}"];
             $filedataid = $attachment['filedataid'];
             // TODO: This doesn't look right to me. I feel like htmlSpecialCharsUni should be outside of the
             // fetchCensoredText() call, but don't have time to verify this right now...
             $attachment['filename'] = $this->fetchCensoredText(vB5_String::htmlSpecialCharsUni($attachment['filename']));
             if (empty($attachment['extension'])) {
                 $attachment['extension'] = strtolower(file_extension($attachment['filename']));
             }
             $attachment['filesize_humanreadable'] = vb_number_format($attachment['filesize'], 1, true);
             $settings = array();
             if (!empty($attachment['settings']) and strtolower($align) == 'config') {
                 $settings = unserialize($attachment['settings']);
                 // TODO: REPLACE USE OF unserialize() above WITH json_decode
                 // ALSO UPDATE createcontent controller's handleAttachmentUploads() to use json_encode() instead of serialize()
             } elseif (!empty($matches['settings'])) {
                 // Currently strictly used by VBV-12051, replacing [IMG]...?filedataid=...[/IMG] with corresponding attachment image
                 // when &type=[a-z]+ or &thumb=1 is part of the image url, that size is passed into us as settings from handle_bbcode_img()
                 // Nothing else AFAIK should be able to pass in the settings, but if we do add this as a main feature,
                 // we should be sure to scrub this well (either via the regex pattern or actual cleaning) to prevent xss.
                 if (isset($matches['settings']['size'])) {
                     // This cleaning is not strictly necessary since the switch-case below that uses this restricts the string to a small set, so xss is not possible.
                     $size = Api_InterfaceAbstract::instance()->callApi('filedata', 'sanitizeFiletype', array($matches['settings']['size']));
                     $settings['size'] = $size;
                 }
             }
             $type = isset($settings['size']) ? $settings['size'] : '';
             // <IMG > OR <A > CHECK
             $params = array($attachment['extension'], $type);
             $isImage = Api_InterfaceAbstract::instance()->callApi('content_attach', 'isImage', $params);
             /*
             	The only reason we do permission checks here is to make the rendered result look nicer, NOT for
             	security.
             	If they have no permission to see an image, any image tags will just show a broken image,
             	so we show a link with the filename instead.
             */
             if (!isset($this->userImagePermissions[$currentUserid][$attachment['parentid']]['cangetimageattachment'])) {
                 $canDownloadImages = vb::getUserContext()->getChannelPermission('forumpermissions2', 'cangetimgattachment', $attachment['parentid']);
                 $this->userImagePermissions[$currentUserid][$attachment['parentid']] = $canDownloadImages;
             }
             if (!isset($this->userImagePermissions[$currentUserid][$attachment['parentid']]['canseethumbnails'])) {
                 $canDownloadImages = vb::getUserContext()->getChannelPermission('forumpermissions', 'canseethumbnails', $attachment['parentid']);
                 $this->userImagePermissions[$currentUserid][$attachment['parentid']] = $canDownloadImages;
             }
             $hasPermission = ($this->userImagePermissions[$currentUserid][$attachment['parentid']]['cangetimageattachment'] or $this->userImagePermissions[$currentUserid][$attachment['parentid']]['canseethumbnails']);
             $useImageTag = ($this->options['do_imgcode'] and $isImage and $hasPermission);
             // Special override for 'canseethumbnails'
             if ($useImageTag and !$this->userImagePermissions[$currentUserid][$attachment['parentid']]['cangetimageattachment']) {
                 $settings['size'] = 'thumb';
             }
             // OUTPUT LOGIC
             $link = vB5_Template_Options::instance()->get('options.frontendurl') . '/filedata/fetch?';
             if (!empty($attachment['nodeid'])) {
                 $link .= "id={$attachment['nodeid']}";
                 $id = 'attachment' . $attachment['nodeid'];
             } else {
                 $link .= "filedataid={$attachment['filedataid']}";
                 $id = 'filedata' . $attachment['filedataid'];
             }
             if (!empty($attachment['resize_dateline'])) {
                 $link .= "&d={$attachment['resize_dateline']}";
             } else {
                 $link .= "&d={$attachment['dateline']}";
             }
             // image_x_y_z isn't strictly for images ATM. If that changes, we can check $isImage here and select
             // a different phrase for the title.
             $title = vB5_Template_Phrase::instance()->register(array('image_x_y_z', $attachment['filename'], intval($attachment['counter']), $attachment['filesize_humanreadable']));
             $filename = $attachment['filename'];
             // html escaped above.
             if ($useImageTag) {
                 $fullsize = false;
                 // Set if $settings['size'] is 'full' or 'fullsize'
                 $customLink = false;
                 // If $settings['link'] is 1 and $settings['url'] isn't empty, it becomes an array of data-attributes to set in the img bits
                 $imgclass = array();
                 $imgbits = array('border' => 0, 'src' => vB_String::htmlSpecialCharsUni($link));
                 $title_text = !empty($settings['title']) ? vB_String::htmlSpecialCharsUni($settings['title']) : '';
                 $description_text = !empty($settings['description']) ? vB_String::htmlSpecialCharsUni($settings['description']) : '';
                 if ($title_text) {
                     $imgbits['title'] = $title_text;
                     $imgbits['data-title'] = $title_text;
                     // only set this if $setting['title'] is not empty
                 } else {
                     if ($description_text) {
                         $imgbits['title'] = $description_text;
                     }
                 }
                 if ($description_text) {
                     $imgbits['description'] = $description_text;
                     $imgbits['data-description'] = $description_text;
                 }
                 $alt_text = !empty($settings['description']) ? vB_String::htmlSpecialCharsUni($settings['description']) : $title_text;
                 // vB4 used to use description for alt text. vB5 seems to expect title for it, for some reason. Here's a compromise
                 if ($alt_text) {
                     $imgbits['alt'] = $alt_text;
                 } else {
                     $imgbits['alt'] = vB5_Template_Phrase::instance()->register(array('image_larger_version_x_y_z', $attachment['filename'], $attachment['counter'], $attachment['filesize_humanreadable'], $attachment['filedataid']));
                 }
                 // See VBV-14079 -- This requires the forumpermissions.canattachmentcss permission.
                 // @TODO Do we want to escape this in some fashion?
                 $styles = !empty($settings['styles']) ? $settings['styles'] : false;
                 if ($styles) {
                     $imgbits['style'] = $styles;
                     $imgbits['data-styles'] = vB_String::htmlSpecialCharsUni($styles);
                 } else {
                     if (!$settings and $align and $align != '=CONFIG') {
                         $imgbits['style'] = "float:{$align}";
                     }
                 }
                 // TODO: WHAT IS THIS CAPTION???
                 //						if ($settings['caption'])
                 //						{
                 //							$caption_tag = "<p class=\"caption $size_class\">$settings[caption]</p>";
                 //						}
                 if (!empty($attachment['nodeid'])) {
                     $imgbits['data-attachmentid'] = $attachment['nodeid'];
                     // used by ckeditor.js's dialogShow handler for Image Dialogs
                 }
                 // image size
                 if (isset($settings['size'])) {
                     // For all the supported sizes, refer to vB_Api_Filedata::SIZE_{} class constants
                     switch ($settings['size']) {
                         case 'icon':
                             // AFAIK vB4 didn't have this size, but vB5 does.
                             $type = $settings['size'];
                             break;
                         case 'thumb':
                             // I think 'thumbnail' was used mostly in vB4. We lean towards the usage of 'thumb' instead in vB5.
                         // I think 'thumbnail' was used mostly in vB4. We lean towards the usage of 'thumb' instead in vB5.
                         case 'thumbnail':
                             $type = 'thumb';
                             break;
                         case 'small':
                             // AFAIK vB4 didn't have this size, but vB5 does.
                         // AFAIK vB4 didn't have this size, but vB5 does.
                         case 'medium':
                         case 'large':
                             $type = $settings['size'];
                             break;
                         case 'full':
                             // I think 'fullsize' was used mostly in vB4. We lean towards the usage of 'full' instead in vB5.
                         // I think 'fullsize' was used mostly in vB4. We lean towards the usage of 'full' instead in vB5.
                         case 'fullsize':
                         default:
                             // @ VBV-5936 we're changing the default so that if settings are not specified, we're displaying the fullsize image.
                             $type = 'full';
                             $fullsize = true;
                             break;
                     }
                     if (!empty($type)) {
                         $imgbits['src'] .= '&amp;type=' . $type;
                         $imgbits['data-size'] = $type;
                     }
                 }
                 // alignment setting
                 if (isset($settings['alignment'])) {
                     switch ($settings['alignment']) {
                         case 'left':
                         case 'center':
                         case 'right':
                             $imgclass[] = 'align_' . $settings['alignment'];
                             $imgbits['data-alignment'] = $settings['alignment'];
                         default:
                             break;
                     }
                 }
                 if (!empty($imgclass)) {
                     $imgbits['class'] = "bbcode-attachment " . implode(' ', $imgclass);
                 } else {
                     $imgbits['class'] = "bbcode-attachment " . 'thumbnail';
                 }
                 /*
                 					We still need to check customURL stuff before we can build the <IMG > tag!
                 */
                 $hrefbits = array('id' => $id, 'class' => 'bbcode-attachment js-slideshow__gallery-node');
                 // if user specified link settings, we need to override the above slidesshow gallery stuff.
                 // link: 0 = default, 1 = url, 2 = none
                 if (!empty($settings['link'])) {
                     if ($settings['link'] == 1 and !empty($settings['linkurl'])) {
                         $settings['linkurl'] = vB_String::htmlSpecialCharsUni($settings['linkurl']);
                         // escape html-special-chars that might break the anchor or img tag
                         // custom URL
                         $linkinfo = $this->handle_bbcode_url('', $settings['linkurl'], true);
                         if ($linkinfo['link'] and $linkinfo['link'] != 'http://') {
                             $customLink = array('data-link' => $settings['link'], 'data-linkurl' => $settings['linkurl']);
                             $hrefbits['href'] = $linkinfo['link'];
                             // linktarget: 0 = self, 1 = new window
                             if (!empty($settings['linktarget'])) {
                                 $customLink['data-linktarget'] = $settings['linktarget'];
                                 $hrefbits['target'] = '_blank';
                             }
                             // below will always occur if it's an external link.
                             if ($linkinfo['nofollow']) {
                                 $hrefbits["rel"] = "nofollow";
                             }
                         }
                     } else {
                         if ($settings['link'] == 2) {
                             // DO NOT SURROUND BY ANCHOR TAG IF LINK TYPE IS "None"
                             $hrefbits = array();
                         }
                     }
                 } else {
                     if ($fullsize) {
                         // Do not link for a full sized image. There's no bigger image to view.
                         $hrefbits = array();
                     } else {
                         $hrefbits['href'] = vB_String::htmlSpecialCharsUni($link);
                         // Use lightbox only if it's not fullsize and one of these extensions
                         // Not sure why we have the extension list here. Maybe the plugin only supported
                         // certain file types in vB4?
                         $lightbox_extensions = array('gif', 'jpg', 'jpeg', 'jpe', 'png', 'bmp');
                         $lightbox = in_array(strtolower($attachment['extension']), $lightbox_extensions);
                         if ($lightbox) {
                             /* Lightbox doesn't work in vB5 for non-gallery attachments yet.
                              */
                             $hrefbits["rel"] = 'Lightbox_' . $this->containerid;
                             //$imgbits["class"] .= ' js-lightbox group-by-parent-' . $attachment['parentid'];
                         } else {
                             $hrefbits["rel"] = "nofollow";
                         }
                     }
                 }
                 if (!empty($customLink)) {
                     foreach ($customLink as $name => $value) {
                         $imgbits[$name] = $value;
                     }
                 }
                 /*
                 					MAKE THE IMG & ANCHOR TAGS
                 */
                 $imgtag = '';
                 foreach ($imgbits as $tag => $value) {
                     $imgtag .= "{$tag}=\"{$value}\" ";
                 }
                 $atag = '';
                 foreach ($hrefbits as $tag => $value) {
                     $atag .= "{$tag}=\"{$value}\" ";
                 }
                 // If we're showing this in the editor, we don't want the surrounding anchor tags. quick work-around.
                 // Also handle the image setting link= "none"
                 if ($this->turnOffSurroundingAnchor or empty($hrefbits)) {
                     $insertHtml = "<img {$imgtag}/>";
                 } else {
                     $insertHtml = "<a {$atag}><img {$imgtag}/></a>";
                 }
                 if (isset($settings['alignment']) && $settings['alignment'] == 'center') {
                     return "<div class=\"img_align_center" . ($fullsize ? ' size_fullsize' : '') . "\">{$insertHtml}</div>";
                 } else {
                     return ($fullsize ? '<div class="size_fullsize">' : '') . $insertHtml . ($fullsize ? '</div>' : '');
                 }
             } else {
                 // Just display a link.
                 $link = vB5_String::htmlSpecialCharsUni($link);
                 return "<a href=\"" . $link . "\" title=\"" . $title . "\">{$filename}</a>";
             }
         } else {
             // if we have a temporaryid, then we're probably editing a post with a new attachment that doesn't have
             // a node created for the attachment yet. Let's return a basic image tag and let JS handle fixing it.
             if ($tempid and $filedataid) {
                 if (isset($this->filedatas[$filedataid]) and !$this->filedatas[$filedataid]['isImage']) {
                     return "<a class=\"bbcode-attachment\" href=\"" . vB5_Template_Options::instance()->get('options.frontendurl') . "/filedata/fetch?filedataid={$filedataid}\" data-tempid=\"" . $tempid . "\" >" . vB5_Template_Phrase::instance()->register(array('attachment')) . "</a>";
                 } else {
                     return "<img class=\"bbcode-attachment js-need-data-att-update\" src=\"" . vB5_Template_Options::instance()->get('options.frontendurl') . "/filedata/fetch?filedataid={$filedataid}\" data-tempid=\"" . $tempid . "\" />";
                 }
             } else {
                 // We don't know how to handle this. It'll just be replaced by a link via attachReplaceCallbackFinal later.
                 return $matches[0];
             }
         }
     }
 }
示例#4
0
 /**
  * Performs the actual merging, using edited input from UI.
  * @param type $data - Contains pairs (value, name) from edit form in addition to the following fields:
  *						* mergePosts - posts to be merged
  *						* destnodeid - target post
  *						* destauthorid - author to be used
  *						* contenttype - target contenttype
  */
 public function mergePosts($input)
 {
     $this->inlinemodAuthCheck();
     $cleaner = vB::getCleaner();
     $data = array();
     foreach ($input as $i) {
         $name = $cleaner->clean($i['name'], vB_Cleaner::TYPE_NOHTML);
         //mostly the data is either integer or string, although the possibility exists of a
         //'url_image'
         switch ($name) {
             case 'nodeid':
             case 'url_nopreview':
             case 'nodeuserid':
             case 'filedataid':
             case 'destauthorid':
             case 'destnodeid':
                 $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_UINT);
                 break;
             case 'title':
             case 'text':
             case 'reason':
                 $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_STR);
                 break;
             case 'url_title':
             case 'authorname':
             case 'url':
             case 'url_image':
             case 'url_meta':
                 $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_NOHTML);
                 break;
             case 'mergePosts':
                 if (!is_array($i['value'])) {
                     $i['value'] = explode(',', $i['value']);
                 }
                 $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_ARRAY_UINT);
                 break;
             case 'filedataid[]':
                 //The filedata records are passed as
                 //input[xx][name]	filedataid[]
                 //input[xx][value]	<integer>
                 $value = $cleaner->clean($i['value'], vB_Cleaner::TYPE_UINT);
                 if (!isset($data['filedataid'])) {
                     $data['filedataid'] = array();
                 }
                 if (!isset($data['filedataid'][$value])) {
                     $data['filedataid'][$value] = '';
                 }
                 continue;
             default:
                 //The title records are passed as
                 //input[xx][name]	title_<filedataid>
                 //input[xx][value]	<title>
                 if (empty($name)) {
                     continue;
                 }
                 if (substr($name, 0, 6) == 'title_') {
                     $filedataid = substr($name, 6);
                     $filedataid = $cleaner->clean($filedataid, vB_Cleaner::TYPE_UINT);
                     if ($filedataid) {
                         if (!isset($data['filedataid'])) {
                             $data['filedataid'] = array();
                         }
                         $data['filedataid'][$filedataid] = $cleaner->clean($i['value'], vB_Cleaner::TYPE_NOHTML);
                     }
                     continue;
                 } else {
                     if (preg_match('#^videoitems\\[([\\d]+)#', $name, $matches)) {
                         if (!isset($data['videoitems'])) {
                             $data['videoitems'] = array();
                         }
                         $videoitems[] = array('videoitemid' => intval($matches[1]), 'url' => $i['value']);
                     } else {
                         if (preg_match('^videoitems\\[new^', $name, $matches)) {
                             if (!isset($data['videoitems'])) {
                                 $data['videoitems'] = array();
                             }
                             foreach ($matches as $video) {
                                 $data['videoitems'][] = array('url' => $video['url']);
                             }
                         }
                     }
                 }
                 continue;
         }
         if (isset($data[$name])) {
             if (!is_array($data[$name])) {
                 $data[$name] = array($data[$name]);
             }
             $data[$name][] = $value;
         } else {
             $data[$name] = $value;
         }
     }
     if (empty($data['mergePosts'])) {
         throw new vB_Exception_Api('please_select_at_least_one_post');
     }
     // check that the user has permission
     $nodesToCheck = $data['mergePosts'];
     $nodesToCheck[] = $data['destnodeid'];
     foreach ($nodesToCheck as $key => $nodeid) {
         // this is here just in case for some reason, a nodeid is 0. Shouldn't happen, but
         // I don't want getChannelPermission to go bonkers from it.
         if (empty($nodeid)) {
             unset($nodesToCheck[$key]);
             continue;
         }
         if (!vB::getUserContext()->getChannelPermission('moderatorpermissions', 'canmanagethreads', $nodeid)) {
             // perhaps we could generate a list of unmergeable nodes and return a warning instead, but
             // I don't think there's a real use case where a moderator can manage only *some* of the
             // nodes they're trying to merge. I think that would require multiple channels being involved, and
             // we don't have a UI for that so I can't test it. As such I'm just going to throw an exception if
             // *any* of the nodes fail the check.
             throw new vB_Exception_Api('no_permission');
         }
     }
     // validate that selected nodes can be merged
     $mergeInfo = $this->validateMergePosts($data['mergePosts']);
     if (isset($mergeInfo['error'])) {
         throw new vB_Exception_Api($mergeInfo['error']);
     }
     // validate form fields
     if (empty($data['destnodeid']) || !array_key_exists($data['destnodeid'], $mergeInfo['destnodes'])) {
         throw new vB_Exception_Api('invalid_data');
     }
     if (empty($data['destauthorid']) || !array_key_exists($data['destauthorid'], $mergeInfo['destauthors'])) {
         throw new vB_Exception_Api('invalid_data');
     }
     $destnode = $this->library->getNodeFullContent($data['destnodeid']);
     $destnode = array_pop($destnode);
     if ($destnode['starter'] != $destnode['nodeid'] and $destnode['starter'] != $destnode['parentid']) {
         if (isset($data['tags'])) {
             unset($data['tags']);
         }
     }
     $type = vB_Types::instance()->getContentTypeClass($destnode['contenttypeid']);
     $response = vB_Library::instance("content_{$type}")->mergeContent($data);
     if ($response) {
         $sources = array_diff($data['mergePosts'], array($data['destnodeid']));
         $origDestnode = $destnode;
         if (!empty($destnode['rawtext'])) {
             $origRawText = $destnode['rawtext'];
         } else {
             if (!empty($destnode['content']['rawtext'])) {
                 $origRawText = $destnode['content']['rawtext'];
             } else {
                 $origRawText = '';
             }
         }
         $destnode = $this->getNode($data['destnodeid']);
         if (!empty($destnode['rawtext'])) {
             $rawText = $destnode['rawtext'];
         } else {
             if (!empty($destnode['content']['rawtext'])) {
                 $rawText = $destnode['content']['rawtext'];
             } else {
                 $rawText = '';
             }
         }
         $destnode = $this->getNode($data['destnodeid']);
         $loginfo = array('nodeid' => $destnode['nodeid'], 'nodetitle' => $destnode['title'], 'nodeusername' => $destnode['authorname'], 'nodeuserid' => $destnode['userid']);
         // move children to target node
         $children = vB::getDbAssertor()->assertQuery('vBForum:closure', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'parent' => $sources, 'depth' => 1));
         $childrenIds = array();
         foreach ($children as $child) {
             $childrenIds[] = $child['child'];
         }
         if (!empty($childrenIds)) {
             $this->moveNodes($childrenIds, $data['destnodeid'], false, false, false);
         }
         // remove merged nodes
         $this->deleteNodes($sources, true, null, false);
         //  Dont log the deletes
         $loginfo['action'] = array('merged_nodes' => implode(',', $sources));
         $vboptions = vB::getDatastore()->getValue('options');
         if (vB_Api::instanceInternal('user')->hasPermissions('genericoptions', 'showeditedby') and $destnode['publishdate'] > 0 and $destnode['publishdate'] < vB::getRequest()->getTimeNow() - $vboptions['noeditedbytime'] * 60 or !empty($data['reason'])) {
             $userinfo = vB::getCurrentSession()->fetch_userinfo();
             if ($vboptions['postedithistory']) {
                 $record = vB::getDbAssertor()->getRow('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'original' => 1, 'nodeid' => $destnode['nodeid']));
                 // insert original post on first edit
                 if (empty($record)) {
                     vB::getDbAssertor()->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $origDestnode['nodeid'], 'userid' => $origDestnode['userid'], 'username' => $origDestnode['authorname'], 'dateline' => $origDestnode['publishdate'], 'pagetext' => $origRawText, 'original' => 1));
                 }
                 // insert the new version
                 vB::getDbAssertor()->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $destnode['nodeid'], 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'dateline' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'pagetext' => $rawText));
             }
             vB::getDbAssertor()->assertQuery('editlog_replacerecord', array('nodeid' => $destnode['nodeid'], 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'timenow' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'hashistory' => intval($vboptions['postedithistory'])));
         }
         vB_Library_Admin::logModeratorAction($loginfo, 'node_merged_by_x');
         return true;
     }
 }
示例#5
0
 /**
  * updates a record
  *
  *	@param	mixed		array of nodeid's
  *	@param	mixed		array of permissions that should be checked.
  *
  * 	@return	boolean
  */
 public function update($nodeid, $data)
 {
     $channelContentTypeId = vB_Types::instance()->getContentTypeId('vBForum_Channel');
     // Verify prefixid
     if ($this->contenttypeid != $channelContentTypeId and isset($data['prefixid'])) {
         $this->verifyPrefixid($data['prefixid']);
     } else {
         // Channel can't have a prefix
         unset($data['prefixid']);
     }
     // Verify post iconid
     if ($this->contenttypeid != $channelContentTypeId and isset($data['iconid'])) {
         $this->verifyPostIconid($data['iconid']);
     } else {
         // Channels can't have a post icon
         unset($data['iconid']);
     }
     $timeNow = vB::getRequest()->getTimeNow();
     $userContext = vB::getUserContext();
     //If this user doesn't have the featured permission and they are trying to set it,
     //Let's just quietly unset it.
     if (isset($data['featured'])) {
         if (!$userContext->getChannelPermission('moderatorpermissions', 'cansetfeatured', $data['parentid'])) {
             unset($data['featured']);
         }
     }
     //We can't allow directly setting parentid. That should only happen through the node api move function
     //And there are number of other fields that shouldn't be changed here. We have methods for the ones that can be changed at all.
     foreach (array('open', 'showopen', 'approved', 'showapproved', 'protected') as $field) {
         if (isset($data[$field])) {
             unset($data[$field]);
         }
     }
     if (isset($data['parentid'])) {
         //Only allow for articles.
         $content = $this->nodeApi->getNodeFullContent($nodeid);
         $content = array_pop($content);
         // you can't move it to the category it's already in
         if ($data['parentid'] != $content['parentid']) {
             // only allow this for articles (currently)
             if ($content['channeltype'] == 'article') {
                 if (!$userContext->getChannelPermission('forumpermissions', 'canmove', $data['parentid']) and !$userContext->getChannelPermission('moderatorpermissions', 'canmassmove', $data['parentid'])) {
                     throw new vB_Exception_Api('no_permission');
                 }
                 //If we got here, we're O.K. to move. let's do that now.
                 vB_Library::instance('node')->moveNodes($nodeid, $data['parentid']);
             }
         }
         unset($data['parentid']);
     }
     //We need to see if we need to update.
     $prior = vB_Library::instance('node')->getNodeBare($nodeid);
     if ($this->contenttypeid != $channelContentTypeId) {
         $content = $this->getFullContent($nodeid);
     }
     if (isset($data['publish_now']) and !empty($data['publish_now'])) {
         $data['publishdate'] = vB::getRequest()->getTimeNow();
     }
     if (empty($data['htmltitle']) and !empty($data['title'])) {
         $data['htmltitle'] = vB_String::htmlSpecialCharsUni(vB_String::stripTags($data['title']), false);
     }
     if (empty($data['urlident']) and !empty($data['title'])) {
         $data['urlident'] = vB_String::getUrlIdent($data['title']);
     }
     // Do not change publishdate or showpublished status unless it was explicitly set while calling update().
     if ((!isset($data['publishdate']) or empty($data['publishdate']) and $data['publishdate'] !== 0) and !empty($prior['publishdate'])) {
         $data['publishdate'] = $prior['publishdate'];
     }
     if ($this->isPublished($data)) {
         $published = 1;
     } else {
         $published = 0;
     }
     $nodevals = array();
     if ($published != $prior['showpublished']) {
         $nodevals['showpublished'] = $published;
     }
     // set default node options
     if ((empty($data['nodeoptions']) or !is_numeric($data['nodeoptions'])) and $prior['contenttypeid'] != $channelContentTypeId) {
         $parentFullContent = vB_Library::instance('node')->getNodeFullContent($prior['parentid']);
         if (!empty($parentFullContent[$prior['parentid']]['channeltype'])) {
             $data['nodeoptions'] = self::$defaultNodeOptions[$parentFullContent[$prior['parentid']]['channeltype']];
         } else {
             $data['nodeoptions'] = self::$defaultNodeOptions['default'];
         }
         // Add or remove any nodeoptions that have been explicitly passed in.
         // This would have otherwise happened in updateNodeOptions/setNodeOptions
         // (which is where it happens when adding a node as opposed to updating
         // a node), but since $data['nodeoptions'] is now defined, setNodeOptions
         // won't take care of setting these (it will just apply the int
         // nodeoptions value).
         $baseNodeOptions = vB_Api::instanceInternal('node')->getOptions();
         foreach ($baseNodeOptions as $baseOptionKey => $baseOptionVal) {
             if (isset($data[$baseOptionKey])) {
                 if (intval($data[$baseOptionKey])) {
                     $data['nodeoptions'] = $data['nodeoptions'] | intval($baseOptionVal);
                 } else {
                     $data['nodeoptions'] = $data['nodeoptions'] & ~intval($baseOptionVal);
                 }
             }
         }
     }
     //node table data.
     $data[vB_dB_Query::TYPE_KEY] = vB_dB_Query::QUERY_UPDATE;
     $data['nodeid'] = $nodeid;
     $data['lastupdate'] = $timeNow;
     //If the field passed is in the $nodeFields array then we update the node table.
     foreach ($data as $field => $value) {
         if (in_array($field, $this->nodeFields)) {
             $nodevals[$field] = $value;
         }
     }
     $index = empty($data['noIndex']);
     unset($data['noIndex']);
     // Update the content-type specific data
     if (!is_array($this->tablename)) {
         $tables = array($this->tablename);
     } else {
         $tables = $this->tablename;
     }
     $success = true;
     foreach ($tables as $table) {
         $structure = $this->assertor->fetchTableStructure('vBForum:' . $table);
         if (empty($structure) or empty($structure['structure'])) {
             throw new vB_Exception_Api('invalid_query_parameters');
         }
         $queryData = array();
         $queryData[vB_dB_Query::TYPE_KEY] = vB_dB_Query::QUERY_UPDATE;
         $queryData['nodeid'] = $nodeid;
         foreach ($structure['structure'] as $fieldname) {
             if (isset($data[$fieldname])) {
                 $queryData[$fieldname] = $data[$fieldname];
             }
         }
         //Now we have at least a query type and a nodeid. We put those in above. So if we don't
         //have at least one other value there's no reason to try an update.
         if (count($queryData) > 2) {
             $success = $success and $this->assertor->assertQuery('vBForum:' . $table, $queryData);
         }
     }
     if ($success) {
         // Handle Attachments
         // The text library (and most derivatives) can have attachments,
         // for the others, this is a no-op.
         $this->handleAttachments('update', $nodeid, $data);
         //Clear cached query info that would be significantly impacted
         $events = array('fUserContentChg_' . $prior['userid']);
         if ($prior['starter']) {
             $starterNodeInfo = vB_Library::instance('node')->getNodeBare($prior['starter']);
             $events[] = 'fUserContentChg_' . $starterNodeInfo['userid'];
         } else {
             if ($prior['parentid']) {
                 $starterNodeInfo = vB_Library::instance('node')->getNodeBare($prior['parentid']);
                 $events[] = 'fUserContentChg_' . $starterNodeInfo['userid'];
             }
         }
         $this->nodeApi->clearCacheEvents($nodeid);
         vB_Cache::instance()->allCacheEvent($events);
         if (isset($nodevals['publishdate']) and $nodevals['publishdate'] > $timeNow) {
             if (empty($nodevals['unpublishdate']) or $nodevals['unpublishdate'] > $nodevals['publishdate']) {
                 $nodevals['nextupdate'] = $nodevals['publishdate'];
             }
         } else {
             if (isset($nodevals['unpublishdate']) and $nodevals['unpublishdate'] > $timeNow) {
                 $nodevals['nextupdate'] = $nodevals['unpublishdate'];
             }
         }
         // handle approved
         if (isset($nodevals['approved'])) {
             if ($nodevals['approved']) {
                 $approved = 1;
                 $queryName = 'approveNode';
             } else {
                 $approved = 0;
                 $queryName = 'unapproveNode';
             }
             // set approved to parent...
             $this->assertor->assertQuery('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, 'nodeid' => $nodeid, 'approved' => $approved));
             // and handle showapproved
             $this->assertor->assertQuery('vBForum:' . $queryName, array('nodeid' => $nodeid));
             unset($nodevals['approved']);
         }
         if (isset($nodevals)) {
             $nodevals[vB_dB_Query::TYPE_KEY] = vB_dB_Query::QUERY_UPDATE;
             $nodevals['nodeid'] = $nodeid;
             $success = $this->assertor->assertQuery('vBForum:node', $nodevals);
         }
         //We need to compare the current publishdate and unpublishdate values against the
         // parent.
         //But we can skip this if neither publish or unpublishdate is set
         $updateParents = false;
         if ($published != $prior['showpublished']) {
             $updateParents = true;
             //We are concerned about two possibilities. It could have gone from published to unpublished.
             //In either case we change by totalcount +1 (for ourselves.
             //Remember that published is always unpublished.
             //From unpublished to published.
             if ($published) {
                 $nodeUpdates = $this->nodeLibrary->publishChildren($nodeid);
                 // if $nodeUpdates is empty, that means no change was made to this node or its descendants,
                 // and no parent count changes are necessary. If it's not empty but doesn't have totalcount set,
                 // that means it possibly failed with a DB error. In such a case, we will just not update the
                 // counts but continue updating the node.
                 if (!empty($nodeUpdates) and isset($nodeUpdates['totalcount'])) {
                     // text-counts only change by 1 (or 0 for non-text types), because it only affects the immediate parent
                     $textChange = $this->textCountChange;
                     $textUnPubChange = -1 * $textChange;
                     // Note, below assumes that a DB had been diligent about
                     // keeping track of the count fields correctly.
                     $totalPubChange = $nodeUpdates['totalcount'] - $prior['totalcount'] + $textChange;
                     // we add the text change because self counts for ancestors' total counts
                     $totalUnPubChange = -1 * $totalPubChange;
                 } else {
                     $updateParents = false;
                 }
             } else {
                 $nodeUpdates = $this->nodeLibrary->unpublishChildren($nodeid);
                 if (!empty($nodeUpdates) and isset($nodeUpdates['totalunpubcount'])) {
                     $textUnPubChange = $this->textCountChange;
                     $textChange = -1 * $textUnPubChange;
                     $totalUnPubChange = $nodeUpdates['totalunpubcount'] - $prior['totalunpubcount'] + $textUnPubChange;
                     $totalPubChange = -1 * $totalUnPubChange;
                 } else {
                     $updateParents = false;
                 }
             }
             vB_Library::instance('node')->clearChildCache($nodeid);
         }
         //update the parent count if necessary
         if ($updateParents) {
             vB_Library::instance('node')->updateParentCounts($nodeid, $textChange, $textUnPubChange, $totalPubChange, $totalUnPubChange, $published);
         }
         //update viewperms from childs if needed, do we want this channel specific?
         if (isset($nodevals['viewperms']) and isset($prior['viewperms']) and $nodevals['viewperms'] != $prior['viewperms']) {
             vB_Api::instanceInternal('node')->setNodePerms($nodeid, array('viewperms' => $nodevals['viewperms']));
         }
         if ($index) {
             vB_Api::instanceInternal('Search')->index($nodeid);
         }
         // update user tags
         $tags = !empty($data['tags']) ? explode(',', $data['tags']) : array();
         $tagRet = vB_Api::instanceInternal('tags')->updateUserTags($nodeid, $tags);
         $this->updateNodeOptions($nodeid, $data);
         // Update childs nodeoptions
         $this->assertor->assertQuery('vBForum:updateChildsNodeoptions', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'parentid' => $nodeid));
         $this->nodeApi->clearCacheEvents(array($nodeid, $prior['parentid']));
         $loginfo = array('nodeid' => $prior['nodeid'], 'nodetitle' => $prior['title'], 'nodeusername' => $prior['authorname'], 'nodeuserid' => $prior['userid']);
         $extra = array();
         if ($nodevals !== null && isset($nodevals['title'])) {
             if ($prior['title'] != $nodevals['title']) {
                 $extra = array('newtitle' => $nodevals['title']);
             }
         }
         vB_Library_Admin::logModeratorAction($loginfo, 'node_edited_by_x', $extra);
         $updateEditLog = true;
         if (!vB::getUserContext()->hasPermission('genericoptions', 'showeditedby') and (isset($content[$nodeid]['edit_reason']) and $data['reason'] == $content[$nodeid]['edit_reason'] or !isset($content[$nodeid]['edit_reason']) and empty($data['reason']))) {
             $updateEditLog = false;
         }
         // Clear autosave table of this items entry
         if (vB::getCurrentSession()->get('userid') and !empty($data['rawtext'])) {
             $this->assertor->delete('vBForum:autosavetext', array('userid' => vB::getCurrentSession()->get('userid'), 'nodeid' => $nodeid, 'parentid' => $content[$nodeid]['parentid']));
         }
         // Log edit by info
         if ($updateEditLog and $this->contenttypeid != $channelContentTypeId and isset($content[$nodeid]['rawtext']) and isset($data['rawtext']) and $content[$nodeid]['rawtext'] != $data['rawtext'] and !empty($data['publishdate']) and $prior['publishdate'] and (!empty($data['reason']) or $data['publishdate'] < vB::getRequest()->getTimeNow() - $this->options['noeditedbytime'] * 60)) {
             $userinfo = vB::getCurrentSession()->fetch_userinfo();
             // save the postedithistory
             if ($this->options['postedithistory']) {
                 $record = $this->assertor->getRow('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'original' => 1, 'nodeid' => $nodeid));
                 // insert original post on first edit
                 if (empty($record)) {
                     $this->assertor->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $nodeid, 'userid' => $content[$nodeid]['userid'], 'username' => $content[$nodeid]['authorname'], 'dateline' => $data['publishdate'], 'pagetext' => $content[$nodeid]['rawtext'], 'original' => 1));
                 }
                 // insert the new version
                 $this->assertor->assertQuery('vBForum:postedithistory', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'nodeid' => $nodeid, 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'dateline' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'pagetext' => isset($data['rawtext']) ? $data['rawtext'] : ''));
             }
             $this->assertor->assertQuery('editlog_replacerecord', array('nodeid' => $nodeid, 'userid' => $userinfo['userid'], 'username' => $userinfo['username'], 'timenow' => vB::getRequest()->getTimeNow(), 'reason' => isset($data['reason']) ? vB5_String::htmlSpecialCharsUni($data['reason']) : '', 'hashistory' => intval($this->options['postedithistory'])));
         }
         return true;
     }
     $this->nodeApi->clearCacheEvents(array($nodeid, $prior['parentid']));
     return false;
 }
示例#6
0
 public static function vBVar($value)
 {
     return vB5_String::htmlSpecialCharsUni($value);
 }