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, документация к форуму.

