Simple Machines Forum кросс-пост и авторизация.
Захотелось мне добавить на сайт http://maps.ck.ua систему комментариев. Да не простую, а золотую необычную. Т.е. чтобы можно было постить и просматривать комментарии и с сайта и с более удобного для просмотра большого количества тем и текста — с форума.
В качестве форума был выбран любимый SMF, 2 версии. Почему любимый — потому что симпатичный дизайн, очень, очень удобная админка и просто хорошая скорость работы.
Официального API для SMF 2.х еще нету, поэтому пришлось использовать чужие наработки в этом плане, плюс немного фантазии и чтения кода форума.
Приведенные ниже строчки фактически являются хардкодингом. Поэтому если вас воротит от этого слова, этот пост можно пропускать.
Итак, авторизация:
function login($_user,$_password ) { require_once ("...../forum/SSI.php"); require_once("...../forum/Sources/Subs-Auth.php"); $db_prefix="ИМЯ_БД.ПРЕФИКС_ТАБЛИЦ; $request = mysql_query(" SELECT ID_MEMBER FROM {$db_prefix}members WHERE member_name = '$_user' LIMIT 1"); if ($request == false && mysql_num_rows($request) < 1) { mysql_free_result($request); setLoginCookie(-3600, 0); return "no user name"; } $get_password_hash = mysql_query (" SELECT passwd, ID_MEMBER, ID_GROUP, lngfile, is_activated, email_address, additional_groups, member_name, password_salt FROM {$db_prefix}members WHERE member_name = '$_user' LIMIT 1"); $user_settings = mysql_fetch_assoc($get_password_hash); $hash_input_password = sha1(strtolower($user_settings['member_name']) . un_htmlspecialchars(stripslashes($_password))); if ($user_settings['passwd']==$hash_input_password) { $cookielength = 3153600; setLoginCookie( $cookielength, $user_settings['ID_MEMBER'], sha1($user_settings['passwd'] . $user_settings['password_salt'])); } else{ setLoginCookie(-3600, 0); return "no user"; } return "ok"; }
Аутентификация, загрузка из куки.
function smf_authenticateUser($mode="all"){ require_once ("...../forum/SSI.php"); require_once("...../forum//Sources/Subs-Auth.php"); global $smf_connection, $smf_settings, $smf_user_info; $ID_MEMBER = 0; if (isset($_COOKIE[$cookiename])) { $_COOKIE[$cookiename] = stripslashes($_COOKIE[$cookiename]); // Fix a security hole in PHP 4.3.9 and below... if (preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~',$_COOKIE[$cookiename]) == 1) { list ($ID_MEMBER, $password) = @unserialize($_COOKIE[$cookiename]); $ID_MEMBER = !empty($ID_MEMBER) && strlen($password) > 0 ? (int) $ID_MEMBER : 0; } else return "no cookie"; } else return "no cookie2"; //echo $ID_MEMBER; // Don't even bother if they have no authentication data. if ($ID_MEMBER != 0){ $request = mysql_query(" SELECT * FROM {$db_prefix}members WHERE ID_MEMBER = $ID_MEMBER LIMIT 1"); // Did we find 'im? If not, junk it. if (mysql_num_rows($request) != 0) { // The base settings array. $smf_user_info = mysql_fetch_assoc($request); if (strlen($password) == 40) $check = sha1($smf_user_info['passwd'] . $smf_user_info['password_salt']) == $password; else $check = false; // Wrong password or not activated - either way, you're going nowhere. $ID_MEMBER = $check && ($smf_user_info['is_activated'] == 1 || $smf_user_info['is_activated'] == 11) ? $smf_user_info['id_member'] : 0; } else { $ID_MEMBER = 0; mysql_free_result($request); return "no user in mysql"; } } else { return "no cookie"; } if(!empty($ID_MEMBER)){ $cookielength = 3153600; setLoginCookie( $cookielength, $smf_user_info['id_member'], sha1($smf_user_info['passwd'] . $smf_user_info['password_salt'])); } else return "no member"; // A few things to make life easier... $smf_user_info['id'] = &$smf_user_info['id_member']; $smf_user_info['username'] = &$smf_user_info['member_name']; $smf_user_info['name'] = &$smf_user_info['real_name']; $smf_user_info['email'] = &$smf_user_info['email_address']; $smf_user_info['messages'] = &$smf_user_info['instant_messages']; $smf_user_info['unread_messages'] = &$smf_user_info['unread_messages']; $smf_user_info['is_guest'] = $ID_MEMBER == 0; $smf_user_info['is_admin'] = in_array(1, $smf_user_info['id_group']); $smf_user_info['status']="ok"; if (empty($smf_user_info['time_format'])) $smf_user_info['time_format'] = $smf_settings['time_format']; if($mode=="all") return $smf_user_info; if($mode=="check") return "ok"; if($mode=="json") return json_encode($smf_user_info); if($mode=="id") return $smf_user_info['id']; }
Постинг в форум. Если topic пустой или равен 0, создается новая тема.
function post_to_smf($board,$topic,$subject,$txt,$oid,$otype,$maptype) { $ID_MEMBER = smf_authenticateUser("id"); if($ID_MEMBER==0 or !is_numeric($ID_MEMBER)) return "wrong poster"; require_once("......../forum/SSI.php"); require_once("......../forum/Sources/Subs-Post.php"); global $smcFunc; $TOPIC=$topic; $ID_BOARD=$board; $excerpt = stripcslashes(trim(strip_tags($txt))); $words = preg_split("/(?<=(\.|!|\?)+)\s/", $excerpt, -1, PREG_SPLIT_NO_EMPTY); foreach ( $words as $word ) { $new_text = $text . substr($excerpt, 0, strpos($excerpt, $word) + strlen($word)); $excerpt = substr($excerpt, strpos($excerpt, $word) + strlen($word), strlen($excerpt)); if ( ( strlen($text) != 0 ) && ( strlen($new_text) > $max_length ) ) { break; } $text = $new_text; } $post_text = $text; $msgOptions = array( 'id' => 0, 'subject' => stripcslashes($subject), 'body' => $post_text, 'smileys_enabled' => true, ); $topicOptions = array( 'id' => $TOPIC, 'board' => $ID_BOARD, 'mark_as_read' => true, ); $posterOptions = array( 'id' => $ID_MEMBER, ); $out=Array(); $top=createPost2($msgOptions, $topicOptions, $posterOptions); if($top) { $out["status"]="ok"; $out["topicid"]=$top; return json_encode($out); } else { $out["status"]="error create post"; return json_encode($out); } } function createPost2(&$msgOptions, &$topicOptions, &$posterOptions) { global $user_info, $txt, $modSettings, $smcFunc, $context; // Set optional parameters to the default value. $msgOptions['icon'] = empty($msgOptions['icon']) ? 'xx' : $msgOptions['icon']; $msgOptions['smileys_enabled'] = !empty($msgOptions['smileys_enabled']); $msgOptions['attachments'] = empty($msgOptions['attachments']) ? array() : $msgOptions['attachments']; $msgOptions['approved'] = isset($msgOptions['approved']) ? (int) $msgOptions['approved'] : 1; $topicOptions['id'] = empty($topicOptions['id']) ? 0 : (int) $topicOptions['id']; $topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null; $topicOptions['lock_mode'] = isset($topicOptions['lock_mode']) ? $topicOptions['lock_mode'] : null; $topicOptions['sticky_mode'] = isset($topicOptions['sticky_mode']) ? $topicOptions['sticky_mode'] : null; $posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id']; $posterOptions['ip'] = empty($posterOptions['ip']) ? $user_info['ip'] : $posterOptions['ip']; $smcFunc['db_query']('', 'SET NAMES UTF8',null); // We need to know if the topic is approved. If we're told that's great - if not find out. if (!$modSettings['postmod_active']) $topicOptions['is_approved'] = true; elseif (!empty($topicOptions['id']) && !isset($topicOptions['is_approved'])) { $request = $smcFunc['db_query']('', ' SELECT approved FROM {db_prefix}topics WHERE id_topic = {int:id_topic} LIMIT 1', array( 'id_topic' => $topicOptions['id'], ) ); list ($topicOptions['is_approved']) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); } // If nothing was filled in as name/e-mail address, try the member table. if (!isset($posterOptions['name']) || $posterOptions['name'] == '' || (empty($posterOptions['email']) && !empty($posterOptions['id']))) { if (empty($posterOptions['id'])) { $posterOptions['id'] = 0; $posterOptions['name'] = $txt['guest_title']; $posterOptions['email'] = ''; } elseif ($posterOptions['id'] != $user_info['id']) { $request = $smcFunc['db_query']('', ' SELECT member_name, email_address FROM {db_prefix}members WHERE id_member = {int:id_member} LIMIT 1', array( 'id_member' => $posterOptions['id'], ) ); // Couldn't find the current poster? if ($smcFunc['db_num_rows']($request) == 0) { trigger_error('createPost(): Invalid member id ' . $posterOptions['id'], E_USER_NOTICE); $posterOptions['id'] = 0; $posterOptions['name'] = $txt['guest_title']; $posterOptions['email'] = ''; } else list ($posterOptions['name'], $posterOptions['email']) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); } else { $posterOptions['name'] = $user_info['name']; $posterOptions['email'] = $user_info['email']; } } // It's do or die time: forget any user aborts! $previous_ignore_user_abort = ignore_user_abort(true); $new_topic = empty($topicOptions['id']); // Insert the post. $smcFunc['db_insert']('', '{db_prefix}messages', array( 'id_board' => 'int', 'id_topic' => 'int', 'id_member' => 'int', 'subject' => 'string-255', 'body' => (!empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] > 65534 ? 'string-' . $modSettings['max_messageLength'] : 'string-65534'), 'poster_name' => 'string-255', 'poster_email' => 'string-255', 'poster_time' => 'int', 'poster_ip' => 'string-255', 'smileys_enabled' => 'int', 'modified_name' => 'string', 'icon' => 'string-16', 'approved' => 'int', ), array( $topicOptions['board'], $topicOptions['id'], $posterOptions['id'], $msgOptions['subject'], $msgOptions['body'], $posterOptions['name'], $posterOptions['email'], time(), $posterOptions['ip'], $msgOptions['smileys_enabled'] ? 1 : 0, '', $msgOptions['icon'], $msgOptions['approved'], ), array('id_msg') ); $msgOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}messages', 'id_msg'); // Something went wrong creating the message... if (empty($msgOptions['id'])) return false; // Fix the attachments. if (!empty($msgOptions['attachments'])) $smcFunc['db_query']('', ' UPDATE {db_prefix}attachments SET id_msg = {int:id_msg} WHERE id_attach IN ({array_int:attachment_list})', array( 'attachment_list' => $msgOptions['attachments'], 'id_msg' => $msgOptions['id'], ) ); // Insert a new topic (if the topicID was left empty. if ($new_topic) { $smcFunc['db_insert']('', '{db_prefix}topics', array( 'id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int', 'id_last_msg' => 'int', 'locked' => 'int', 'is_sticky' => 'int', 'num_views' => 'int', 'id_poll' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', ), array( $topicOptions['board'], $posterOptions['id'], $posterOptions['id'], $msgOptions['id'], $msgOptions['id'], $topicOptions['lock_mode'] === null ? 0 : $topicOptions['lock_mode'], $topicOptions['sticky_mode'] === null ? 0 : $topicOptions['sticky_mode'], 0, $topicOptions['poll'] === null ? 0 : $topicOptions['poll'], $msgOptions['approved'] ? 0 : 1, $msgOptions['approved'], ), array('id_topic') ); $topicOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic'); // The topic couldn't be created for some reason. if (empty($topicOptions['id'])) { // We should delete the post that did work, though... $smcFunc['db_query']('', ' DELETE FROM {db_prefix}messages WHERE id_msg = {int:id_msg}', array( 'id_msg' => $msgOptions['id'], ) ); return false; } // Fix the message with the topic. $smcFunc['db_query']('', ' UPDATE {db_prefix}messages SET id_topic = {int:id_topic} WHERE id_msg = {int:id_msg}', array( 'id_topic' => $topicOptions['id'], 'id_msg' => $msgOptions['id'], ) ); // There's been a new topic AND a new post today. trackStats(array('topics' => '+', 'posts' => '+')); updateStats('topic', true); updateStats('subject', $topicOptions['id'], $msgOptions['subject']); //What if we want to export new topics out to a CMS? if (isset($modSettings['integrate_create_topic']) && is_callable($modSettings['integrate_create_topic'])) call_user_func(strpos($modSettings['integrate_create_topic'], '::') === false ? $modSettings['integrate_create_topic'] : explode('::', $modSettings['integrate_create_topic']), $msgOptions, $topicOptions, $posterOptions); } // The topic already exists, it only needs a little updating. else { $countChange = $msgOptions['approved'] ? 'num_replies = num_replies + 1' : 'unapproved_posts = unapproved_posts + 1'; // Update the number of replies and the lock/sticky status. $smcFunc['db_query']('', ' UPDATE {db_prefix}topics SET ' . ($msgOptions['approved'] ? 'id_member_updated = {int:poster_id}, id_last_msg = {int:id_msg},' : '') . ' ' . $countChange . ($topicOptions['lock_mode'] === null ? '' : ', locked = {int:locked}') . ($topicOptions['sticky_mode'] === null ? '' : ', is_sticky = {int:is_sticky}') . ' WHERE id_topic = {int:id_topic}', array( 'poster_id' => $posterOptions['id'], 'id_msg' => $msgOptions['id'], 'locked' => $topicOptions['lock_mode'], 'is_sticky' => $topicOptions['sticky_mode'], 'id_topic' => $topicOptions['id'], ) ); // One new post has been added today. trackStats(array('posts' => '+')); } // Creating is modifying...in a way. //!!! Why not set id_msg_modified on the insert? $smcFunc['db_query']('', ' UPDATE {db_prefix}messages SET id_msg_modified = {int:id_msg} WHERE id_msg = {int:id_msg}', array( 'id_msg' => $msgOptions['id'], ) ); // Increase the number of posts and topics on the board. if ($msgOptions['approved']) $smcFunc['db_query']('', ' UPDATE {db_prefix}boards SET num_posts = num_posts + 1' . ($new_topic ? ', num_topics = num_topics + 1' : '') . ' WHERE id_board = {int:id_board}', array( 'id_board' => $topicOptions['board'], ) ); else { $smcFunc['db_query']('', ' UPDATE {db_prefix}boards SET unapproved_posts = unapproved_posts + 1' . ($new_topic ? ', unapproved_topics = unapproved_topics + 1' : '') . ' WHERE id_board = {int:id_board}', array( 'id_board' => $topicOptions['board'], ) ); // Add to the approval queue too. $smcFunc['db_insert']('', '{db_prefix}approval_queue', array( 'id_msg' => 'int', ), array( $msgOptions['id'], ), array() ); } // Mark inserted topic as read (only for the user calling this function). if (!empty($topicOptions['mark_as_read']) && !$user_info['is_guest']) { // Since it's likely they *read* it before replying, let's try an UPDATE first. if (!$new_topic) { $smcFunc['db_query']('', ' UPDATE {db_prefix}log_topics SET id_msg = {int:id_msg} WHERE id_member = {int:current_member} AND id_topic = {int:id_topic}', array( 'current_member' => $posterOptions['id'], 'id_msg' => $msgOptions['id'], 'id_topic' => $topicOptions['id'], ) ); $flag = $smcFunc['db_affected_rows']() != 0; } if (empty($flag)) { $smcFunc['db_insert']('ignore', '{db_prefix}log_topics', array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), array($topicOptions['id'], $posterOptions['id'], $msgOptions['id']), array('id_topic', 'id_member') ); } } // If there's a custom search index, it needs updating... if (!empty($modSettings['search_custom_index_config'])) { $customIndexSettings = unserialize($modSettings['search_custom_index_config']); $inserts = array(); foreach (text2words($msgOptions['body'], $customIndexSettings['bytes_per_word'], true) as $word) $inserts[] = array($word, $msgOptions['id']); if (!empty($inserts)) $smcFunc['db_insert']('ignore', '{db_prefix}log_search_words', array('id_word' => 'int', 'id_msg' => 'int'), $inserts, array('id_word', 'id_msg') ); } // Increase the post counter for the user that created the post. if (!empty($posterOptions['update_post_count']) && !empty($posterOptions['id']) && $msgOptions['approved']) { // Are you the one that happened to create this post? if ($user_info['id'] == $posterOptions['id']) $user_info['posts']++; updateMemberData($posterOptions['id'], array('posts' => '+')); } // They've posted, so they can make the view count go up one if they really want. (this is to keep views >= replies...) $_SESSION['last_read_topic'] = 0; // Better safe than sorry. if (isset($_SESSION['topicseen_cache'][$topicOptions['board']])) $_SESSION['topicseen_cache'][$topicOptions['board']]--; // Update all the stats so everyone knows about this new topic and message. updateStats('message', true, $msgOptions['id']); // Update the last message on the board assuming it's approved AND the topic is. if ($msgOptions['approved']) updateLastMessages($topicOptions['board'], $new_topic || !empty($topicOptions['is_approved']) ? $msgOptions['id'] : 0); // Alright, done now... we can abort now, I guess... at least this much is done. ignore_user_abort($previous_ignore_user_abort); // Success. return $topicOptions['id']; }
тут в строчке
$ID_MEMBER = smf_authenticateUser("id");
ждет сюрпиз. Если постит не администратор, функция возвращает ошибку.
Для исправления придется лезть в файл forum/SSI.php и выше строчки
loadPermissions();
добавлять
loadBoard();
Гнусный хардкодинг :\
В работе использованы куски кода моста Mambo-SMF, куски кода самого SMF, документация к форуму.