MyBB Community Forums

Full Version: Leaving a database record's "snapshot" at a post. [SOLVED]
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Well, here i am (again) to ask another question. The last time I was looking for a way to create some sort of "character sheet" for my users to fill and I did it! Now, even though I can save it on my database, lock the view for those users who want to create another character and show the sheet at a post using the next tag: [hoja_de_personaje] which is replaced by a hook so it displays basic dat such a firstname, lastname, gender and age using a template... *takes a breath* I'm facing a new problem:

It works, it shows the template, replaces the variables and works like i wanted but... Can i leave some sort of snapshot instead of the updated data?

This is my template:

    $character_preview = '
<div>
    <h4>{$firstname} {$lastname}</h4>
    <b>Edad:</b> {$age}
    <b>Sexo:</b> {$gender}
</div>
    ';

That's what i show if an user writes "[hoja_de_personaje]" on its post. Now, this shows the exact same data that is on my database, which is cool, but I don't want to do so. I want to show how it was a the time he/she posted it. So, if the thread/post is 10 days old, I want that 10 days old data, not the updated one.

Why do I want this? Well, easy: I want to avoid cheating. If a user goes and trains his character, it will have better stats (yes, this character sheet will have fields like "power", "health", "mana", etc) so he/she can say: "Hey, my character sheet says that i have it so i can use it" but he/she did not have those new stats at that time.

It may sound silly but this is quite important for me. So, is there a way achieve this?

I think that MyBB should have some sort of thread or post table where it stores that information and that I can (somehow) modify that table to add this field. That's a first thought, i don't know if that's the better way. Other idea that came to my mind was to create a new table a keep record of this data in there, but... again, I want to know if there is a better way than those two.

Once again, sorry for my bad english, I am not a native speaker and... thanks in advance!

---

Edit: Almost forgot it. I like how this is going and I hope that you can solve my problem but I have another question: Is there a wat to add an option to the text editor so the users can click it and it would add the "[hoja_de_personaje]" inside their post? I know, that's like using a nuke to kill a single bug, but it would help me on future tasks if I came with more complex tags.

---

Edit 2: Hi!, well, this was quite difficult but I did it and it works. Either way, I would like to know if any of you think if this is a good method or if I should modify it.

- Table to upload my "character sheets previews":
$db->write_query("CREATE TABLE `".TABLE_PREFIX."rps_character_post_previews` (
            `aid` bigint(30) UNSIGNED NOT NULL AUTO_INCREMENT,
            `pid` int(10) UNSIGNED NOT NULL,
            `preview` text NOT NULL,
            PRIMARY KEY (`aid`)
        ) ENGINE=MyISAM{$db->build_create_table_collation()};"
    );

- Hook:
$plugins->add_hook('datahandler_post_insert_post_end', 'character_post_preview_save');

- Function to save the character preview: (saves the template with the data)
function character_post_preview_save(&$datahandler)
{
    global $mybb, $db, $templates, $myVar;

    $post = get_post($datahandler->pid);

    if(!$post)
	{
		error($lang->error_invalidpost);
    }

    if(strpos($post['message'], '[hoja_de_personaje]') !== false)
    {
        $character = get_character($post['uid']);

        if(!is_null($character))
        {
            $firstname = $character['firstname'];
            $lastname = $character['lastname'];
            $age = $character['age'];
            $gender = $character['gender'];
            eval('$myVar = "' . $templates->get('character_preview_template') . '";');

            $db->insert_query('rps_character_post_previews', [
                'pid'           => $post['pid'],
                'preview'       => $myVar,
            ]);
        }
    }
}

- Function to show the data:
function character_post_preview_show(&$post)
{
    $preview = get_character_preview($post['pid']);

    if(is_null($preview))
    {
        $post['message'] = str_replace('[hoja_de_personaje]', '<b>Error:</b> La hoja de personaje no pudo ser localizada.', $post['message']);
    }

    $post = str_replace('[hoja_de_personaje]', $preview, $post);
}

- Function used to get the preview in the "character_post_preview_show" function:
function get_character_preview($pid)
{
    global $db;

    $query = $db->simple_select('rps_character_post_previews', '*', "pid='{$pid}'");

    if(!$db->num_rows($query))
    {
        return NULL;
    }

    $data = $db->fetch_array($query);

    return $data['preview'];
}

And that's it. I think that it is a lot of work to achieve but I want but this is the only way I could find.
(2019-01-04, 02:50 AM)enrolmudas Wrote: [ -> ]Edit: Almost forgot it. I like how this is going and I hope that you can solve my problem but I have another question: Is there a wat to add an option to the text editor so the users can click it and it would add the "[hoja_de_personaje]" inside their post? I know, that's like using a nuke to kill a single bug, but it would help me on future tasks if I came with more complex tags.

See https://www.sceditor.com/documentation/custom-commands/ - these could be added to jscripts/bbcodes_sceditor.js or custom .js files.

Quote:Edit 2: Hi!, well, this was quite difficult but I did it and it works. Either way, I would like to know if any of you think if this is a good method or if I should modify it.
Currently the HTML is rendered and inserted into the database - it might be easier (now or in the future, e.g. when the layout needs changes) to simply save a copy of the raw data in multiple table columns (or a single column, using serialize() if the field structure changes often) and then render it with the template on output (in character_post_preview_show()). That's also where the data should be escaped (MyBB uses htmlspecialchars_uni()) to make sure that HTML code that may be inserted by users doesn't work there (it may break the layout and introduce security problems).
Additionally, all columns values should be escaped with $db->escape_string() before being inserted (in character_post_preview_save()).
So, beside the DB considerations about saving this preview, everything looks fine? Cool, Those are great news. Thanks for your help!

Edit: This is my new code:

    function character_preview_save(&$datahandler)
    {
        global $mybb, $db, $templates, $lang;

        $post = get_post($datahandler->pid);

        if(!$post)
        {
            error('No se encontró el tema/post.');
        }

        if(strpos($post['message'], '[personaje]') !== false)
        {
            $character = rpsystem_get_character($post['uid']);

            if(is_null($character))
            {
                error('No puedes usar el bbcode [personaje] sin haber creado un personaje.');
            }
            else
            {
                $preview_data = array(
                    'firstname'     => $character['firstname'],
                    'lastname'      => $character['lastname'],
                    'age'           => $character['age'],
                    'gender'        => $character['gender'],
                    'template'      => 'character_preview'
                );

                $db->insert_query('rpsystem_character_previews', [
                    'pid'           => $post['pid'],
                    'preview'       => $db->escape_string(serialize($preview_data)),
                ]);
            }
        }
    }

    function character_preview_show(&$post)
    {
        global $myVar, $templates;

        $preview = rpsystem_get_character_preview($post['pid']);

        if(is_null($preview))
        {
            $post['message'] = str_replace('[hoja_de_personaje]', '<b>Error:</b> La hoja de personaje no pudo ser localizada.', $post['message']);
        }
        else
        {
            $data = unserialize($preview);

            $firstname = $data['firstname'];
            $lastname = $data['lastname'];
            $age = $data['age'];
            $gender = $data['gender'];
            eval('$myVar = "' . $templates->get($data['template']) . '";');

            $post = str_replace('[hoja_de_personaje]',  $myVar, $post);
        }
    }

This is what you were talking about?
Yes, only need to filter the values on output like so:
$firstname = htmlspecialchars_uni($data['firstname']);
before the eval() bit; this should make values like <b>hello</b> display exactly as typed, instead of making these HTML tags actually work.
(2019-01-05, 12:15 AM)Devilshakerz Wrote: [ -> ]Yes, only need to filter the values on output like so:
$firstname = htmlspecialchars_uni($data['firstname']);
before the eval() bit; this should make values like <b>hello</b> display exactly as typed, instead of making these HTML tags actually work.

Roger that! Thanks  Heart