Resource icon

vBulletin Big Board Importer [vBulletin 3 + vBulletin 4] [Paid] 1.5.0

No permission to buy ($150.00)
Import errors can happen as a matter of course and can usually be ignored. The data is being directly dumped and imported without being properly fit and cast for the destination fields, so you can get expected errors like "data too long" and "incorrect integer". It still works though.

User fields must be mapped manually. Find this code in the exporter:

Code:
        $start = microtime(true);
        echo "          skipping user fields";
        //Map any custom user feilds you have here. Disabled by default.
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'interests\' AS field_id, field3 AS field_value FROM userfield WHERE field3 != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' > ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'twitter\' AS field_id, field14 AS field_value FROM userfield WHERE field14 != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'last_bump_date\' AS field_id, field17 AS field_value FROM userfield WHERE field17 > 0 ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'icq\' AS field_id, icq AS field_value FROM user WHERE icq != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'aim\' AS field_id, aim AS field_value FROM user WHERE aim != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'yahoo\' AS field_id, yahoo AS field_value FROM user WHERE yahoo != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'skype\' AS field_id, skype AS field_value FROM user WHERE skype != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');
        // exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'msn\' AS field_id, msn AS field_value FROM user WHERE msn != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');

Can you give me a line for a single selection menu? or would it be same for like all Single-Line Text Box above.
 
Yep, they are entities. That is a condition of the source data, not a problem with the importer. It will be necessary to find a way to decode those entities.
 
Yep, they are entities. That is a condition of the source data, not a problem with the importer. It will be necessary to find a way to decode those entities.

Not possible at this moment? How come those with urdu forums coverted fine to xenforo?

I am also getting signature as NULL.

How to fix that?
 
Not possible at this moment?

I'm sure it's possible, but I don't know of an existing conversion tool off hand. Refer to my previous post:

Your Chinese characters are apparently being stored as HTML entities in vB? Check the posts in the vB database to confirm this.

You may be able to modify the $extraOutCommand line:

Code:
	// USE THIS SETTING TO SPECIFY AN EXTRA PIPE FOR ALL OUTPUT RECORDS
	// EXAMPLE IS TO PIPE OUTPUT THROUGH iconv IF CHARACTER ENCODING CHANGES ARE NEEDED
	// '| iconv -f LATIN1 -t UTF8'
	public static $extraOutCommand = '| iconv -f LATIN1 -t UTF8';

But off hand I don't know if iconv has an option to convert from entities.

So basically you need to find some CLI program that you can pipe your output through in order to convert those entities. $extraOutCommand allows you to specify such a pipe.
 
I'm sure it's possible, but I don't know of an existing conversion tool off hand. Refer to my previous post:



So basically you need to find some CLI program that you can pipe your output through in order to convert those entities. $extraOutCommand allows you to specify such a pipe.
I think @digitalpoint would have a solution to this.
 
@Slavik @Jake Bunce

1) Any idea how to import user profile field which has select flags from a drop down menu? Is it same thing as others like for example the interests field? Please check my post above.

2) Any idea why signatures are not being imported/exported right? It says NULL for whichever member not having a signature...
 
Last edited:
Works a peach, thanks. For anyone else reading that wants to do this, I had to tweak it slightly

Code:
exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'xenforofieldname\'     AS field_id, IF(field5 = 1, \'Yes\', \'No\') AS field_value FROM userfield  ORDER BY userid" ' . self::$extraOutCommand . ' >> ' . self::$dataDir . 'xf_user_field_value.txt');

As vBulletin stores an unchecked option as an empty field, I had to remove the WHERE statement, and I also needed to escape the quotes around the Yes/No values.

Hey, how can i have a code for exporting
Country Flag: Single-Selection Menu

Now it is a list where users select their country from drop down menu. How do i export and import?
 
Here is the latest attach function containing two bug fixes, one for vB4 file system attachments, and one for handling empty batches:

Code:
    public function attachments ()
    {
      
        static $types = array(
            1 => 'GIF',
            2 => 'JPEG',
            3 => 'PNG'
        );
      
      
      
        $globalStart = microtime(true);
      
        $start = $this->db->fetchOne('
            SELECT MAX(attachment_id)
            FROM xf_attachment
        ');

        $maxvB = $this->db->fetchOne('SELECT MAX(attachmentid) FROM ' . $this->sourceDb . '.attachment');
      
        $fileName = '' . self::$dataDir . 'attachment_tmp';
      
        $attachmentModel = XenForo_Model::create('XenForo_Model_Attachment');

        $emptyBatch = false;

        while (
      
      
                //    WHERE attachment.attachmentid BETWEEN ' . intval($start) . ' AND ' . intval($start + 999) . ' AND attachment.contenttypeid = 1
      
                $attachments = $this->db->fetchAll('
                    SELECT attachment.attachmentid AS attachment_id, attachment.userid AS user_id, ' . (self::$isVB4 ? 'contentid' : 'postid') . ' AS content_id, attachment.dateline AS attachment_date, ' . (self::$isVB4 ? 'filedata.filedata' : 'attachment.filedata') . ' AS file, ' . (self::$isVB4 ? 'filedata.thumbnail_filesize' : 'attachment.thumbnail_filesize') . ', attachment.filename, ' . (self::$isVB4 ? 'filedata.extension' : 'attachment.extension') . ' AS extension, ' . (self::$isVB4 ? 'filedata.filedataid' : 'attachment.attachmentid') . ' AS filesysid
                    FROM ' . $this->sourceDb . '.attachment
                        ' . (self::$isVB4 ? 'LEFT JOIN ' . $this->sourceDb . '.filedata ON (filedata.filedataid = attachment.filedataid)' : '') . '
                    WHERE attachment.attachmentid BETWEEN ' . intval($start) . ' AND ' . intval($start + 999) . (self::$isVB4 ? ' AND attachment.contenttypeid = 1' : '') . '
                    ' . (self::$isVB4 ? 'GROUP BY attachment.filedataid' : '') . '
                    ORDER BY attachment.attachmentid
                ')

                OR $emptyBatch = (($start + 999) < $maxvB)
        )
        {
            if ($emptyBatch)
            {
                echo "\r\n         EMPTY BATCH (no 'post' attachments in range)\r\n";

                $start = $start + 1000;
                $emptyBatch = false;

                continue;
            }

            echo "\r\n         " . number_format(count($attachments)) . " new attachments\r\n";

            foreach ($attachments as $attachment)
            {
          
                echo $attachment['attachment_id'] . ' ';
              

                if (self::$attachFile AND file_exists(self::$attachFile . implode('/', preg_split('//', $attachment['user_id'],  -1, PREG_SPLIT_NO_EMPTY)) . '/' . strval($attachment['filesysid']) . '.attach'))
                {
                    $attachment['file'] = file_get_contents(self::$attachFile . implode('/', preg_split('//', $attachment['user_id'],  -1, PREG_SPLIT_NO_EMPTY)) . '/' . strval($attachment['filesysid']) . '.attach');
                }
                else
                {
                    $attachment['file'] = $attachment['file'];
                }

                if (!$attachment['file'])
                {
                    echo '!!MISSING DATA!!';
                    continue;
                }


                if ($attachment['thumbnail_filesize'] > 0 AND $attachment['extension'] != 'bmp')
                {
                    file_put_contents($fileName, $attachment['file']);                      
                  
                    $tempThumbFile = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
                  
                    $imageInfo = getimagesize($fileName);
                  
                    if ($imageInfo === false)
                    {
                        echo '!!BAD!!';
                        $attachment['width'] = 0;
                        $attachment['height'] = 0;
                        $attachment['thumbnail_width'] = 0;
                        $attachment['thumbnail_height'] = 0;
                        $attachment['thumbnail_file'] = '';
                        $attachment['file_type'] = '';
                      
                    }
                    else
                    {
                        $attachment['width'] = $imageInfo[0];
                        $attachment['height'] = $imageInfo[1];
                        $attachment['thumbnail_width'] = 0;
                        $attachment['thumbnail_height'] = 0;
                        $attachment['thumbnail_file'] = '';

                        $attachment['file_type'] = strtoupper(image_type_to_extension($imageInfo[2], false));
                  
                  
                        $image = XenForo_Image_Abstract::createFromFile($fileName, $imageInfo[2]);
                  
                        if ($image)
                        {
                            if ($image->thumbnail(XenForo_Application::get('options')->attachmentThumbnailDimensions))
                            {
                                $image->output($imageInfo[2], $tempThumbFile);
                                $attachment['thumbnail_file'] = file_get_contents($tempThumbFile);
                                unlink($tempThumbFile);
                            }
                            else
                            {
                                $attachment['thumbnail_file'] = $attachment['file'];              
                            }
                              
                            $attachment['thumbnail_width'] = $image->getWidth();
                            $attachment['thumbnail_height'] = $image->getHeight();
                          
                            unset($image);
                      
                        }

                    }
                }
                else
                {
                    $attachment['width'] = 0;
                    $attachment['height'] = 0;
                    $attachment['thumbnail_width'] = 0;
                    $attachment['thumbnail_height'] = 0;
                    $attachment['thumbnail_file'] = '';
                    $attachment['file_type'] = '';
                }

                if (self::$extraOutCommand)
                {
                    $attachFileNameTmp = array();
                    exec('echo $\'' . addslashes($attachment['filename']) . '\' ' . self::$extraOutCommand , $attachFileNameTmp);
                    $attachFileName = $attachFileNameTmp[0];
                }
                else
                {
                    $attachFileName = $attachment['filename'];
                }

                $this->db->query('
                    INSERT INTO xf_attachment_data
                        (user_id, upload_date, filename, file_size, file_hash, width, height, thumbnail_width, thumbnail_height, attach_count)
                    VALUES
                        (' . $attachment['user_id'] . ', ' . $attachment['attachment_date'] . ', "' . addslashes($attachFileName) . '", ' . strlen($attachment['file']) . ', "' . addslashes(md5($attachment['file'])) . '", ' . $attachment['width'] . ', ' . $attachment['height'] . ', ' . $attachment['thumbnail_width'] . ', ' . $attachment['thumbnail_height'] . ', 1)
                ');
              
                $attachmentDataId = $this->db->lastInsertId();
              
                $this->db->query('
                    INSERT INTO xf_attachment
                        (attachment_id, data_id, content_type, content_id, attach_date, temp_hash, unassociated, view_count)
                    VALUES
                        (' . $attachment['attachment_id'] . ', ' . $attachmentDataId . ', "post", ' . $attachment['content_id'] . ', ' . $attachment['attachment_date'] . ', "", 0, 0)
                    ON DUPLICATE KEY UPDATE
                        data_id = VALUES(data_id), content_type = VALUES(content_type), content_id = VALUES(content_id), attach_date = VALUES(attach_date), temp_hash = VALUES(temp_hash), unassociated = VALUES(unassociated), view_count = VALUES(view_count)
                ');
          

            // SKIP THIS AND WRITE TO FILE INSTEAD

                $path = $attachmentModel->getAttachmentDataFilePath(array(
                    'data_id' => $attachmentDataId,
                    'file_hash' => md5($attachment['file']),
                ));
                XenForo_Helper_File::createDirectory(dirname($path));
                file_put_contents($path, $attachment['file']);
              
                if ($attachment['thumbnail_width'])
                {
                    $path = $attachmentModel->getAttachmentThumbnailFilePath(array(
                        'data_id' => $attachmentDataId,
                        'file_hash' => md5($attachment['file']),
                    ));

                    XenForo_Helper_File::createDirectory(dirname($path), true);

                    file_put_contents($path, $attachment['thumbnail_file']);
                }
            }
          
    
    }

Is this the updated code or the one in the latest download. Please tell.

Update: it is new.
 
Last edited:
Code:
ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL ve                   rsion

While importing i am getting this...

I am running.

Code:
MySQL Support    enabled
Active Persistent Links    0
Active Links    0
Client API version    5.1.70
MYSQL_MODULE_TYPE    external
MYSQL_SOCKET    /var/lib/mysql/mysql.sock
MYSQL_INCLUDE    -I/usr/include/mysql
MYSQL_LIBS    -L/usr/lib64 -lmysqlclient
 
Code:
ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL ve                   rsion

While importing i am getting this...

I am running.

Code:
MySQL Support    enabled
Active Persistent Links    0
Active Links    0
Client API version    5.1.70
MYSQL_MODULE_TYPE    external
MYSQL_SOCKET    /var/lib/mysql/mysql.sock
MYSQL_INCLUDE    -I/usr/include/mysql
MYSQL_LIBS    -L/usr/lib64 -lmysqlclient

Update to 5.5
 
Update to 5.5

Upgraded to 5.5 and still getting this error.

Code:
Importing...
xf_conversation_masterERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_conversation_messageERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_conversation_recipientERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_conversation_userERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_deletion_logERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_edit_historyERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_forumERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_forum_readERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_ipERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_liked_contentERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_link_forumERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_moderator_logERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_nodeERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_pollERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_poll_responseERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_poll_voteERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_post.ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
.ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
.ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
.ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
.ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.02s)...
xf_profile_postERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_reportERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_report_commentERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_stats_dailyERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_threadERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_thread_readERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.01s)...
xf_thread_user_postERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_thread_watchERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_userERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_authenticateERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_banERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_external_authdefence_xen.xf_user_external_auth  repair  status  OK
ERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.01s)...
xf_user_field_valueERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_followERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_ignoredERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_optionERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_privacyERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_user_profileERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_warningERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...
xf_warning_action_triggerERROR 1148 (42000) at line 1: The used command is not allowed with this MySQL version
(0.00s)...

  Import Total Time: 0.21s)...

Cleaning Up...
  trim thread titles
  building import log

  import log done, total time: 0.02s)...

    updating post like cache
  like cache done, total time: 0.00s)...

    Updating Posts With Edit History...
  edit history done, total time: 0.00s)...

!! beware of auto-edit history pruning and auto-ip pruning in XenForo @ admin.php?options/list/logging !!

  Cleanup done.
 
Is there are a import log for the vbulletin 4 redirect script?

define('IMPORT_LOG_TABLE', 'import_log_x');

I think it is Kiers script that needs it.

So far i have placed this in the .htaccess and uploaded Kiers file in the forums directory (where xenforo also resides). The pagination URLS and post URLS are not working.
Code:
RewriteEngine on
RewriteRule [^/]+/([\d]+)-.+-([\d]+).html showthread.php?t=$1&page=$2 [NC,L]
RewriteRule [^/]+/([\d]+)-.+.html showthread.php?t=$1 [NC,L]
 
Last edited:
Users who don't have Occupation, Location or Signature in the vbulletin installation, after the import is showing NULL.

Is there a query that i can fire which would replace NULL with ("") in vbulletin so they come up empty after the import? Many thanks. @digitalpoint
 
If you go into something like phpmyadmin where you can change the table definition, the easiest thing to do is probably change that column to not allow NULL values (it's an option in the table schema for each column). Then all the existing NULL values will just be empty instead (which is what you want).
 
If you go into something like phpmyadmin where you can change the table definition, the easiest thing to do is probably change that column to not allow NULL values (it's an option in the table schema for each column). Then all the existing NULL values will just be empty instead (which is what you want).

In PHPmyAdmin, Structure, i changed Default is set to NULL, i tried to change it to <As defined: ""> but it gave me an error. Setting it None, doesn't seem to do anything either. Am i doing something wrong? Or is there any other way?
 
I had to bump my PHP memory_limit up to 2GB before I could get the poll export to complete. :eek: It finally worked, though. Do I just have unusually large polls or something?

Regarding the user profile fields - are the line numbers in menu still accurate? I don't really understand what's going on between 380-396; it looks like 487-506 is the relevant section:

PHP:
//Map any custom user feilds you have here. Disabled by default.
        exec('mysql -h' . $this->slaveDbHost . ' ' . $this->sourceDb . ' ' . $this->sourceDBuser . ' ' . $this->sourceDBpassword . ' -N -q -e "SET NAMES binary;SELECT SQL_NO_CACHE userid AS user_id, \'interests\' AS field_id, field3 AS field_value FROM userfield WHERE field3 != \'\' ORDER BY userid" ' . self::$extraOutCommand . ' > ' . self::$dataDir . 'xf_user_field_value.txt');

Assuming that is correct, what exactly do we need to modify here, just the fieldid for each custom field we want to import? What about lines 497-506? Should they be uncommented as well?

EDIT: Also, exporting attachments, just got this error:
Code:
413 21417 21418 21421 21422 21423 21424 21425 21426 21430
An exception occurred: Invalid image type given. Expects IMAGETYPE_XXX constant. in XenForo/Image/Gd.php on line 92
#0 [internal function]: XenForo_Image_Gd::createFromFileDirect('/home/site/...', 6)
#1 XenForo/Image/Abstract.php(181): call_user_func(Array, '/home/site/...', 6)
#2 /home/site/public/xf2/Export.php(1300): XenForo_Image_Abstract::createFromFile('/home/site/...', 6)
#3 /home/site/public/xf2/Export.php(1842): DigitalPointExporter->attachments()
#4 {main}

EDIT 2: I managed to finish running a test import. The newly imported XenForo appears to operate correctly, until I sign into an account. I'm presented with the standard no-permissions XF error on every page: "You do not have permission to view this page or perform this action."

I set up the usergroup mapping in Export.php, and looking at the database, they appear to have been imported with the correct IDs.

I can sign into the Admin CP, but when I do, I'm presented with this blank page. Any ideas?
 

Attachments

  • Screen Shot 2013-10-19 at 1.47.27 AM.webp
    Screen Shot 2013-10-19 at 1.47.27 AM.webp
    16.9 KB · Views: 24
Last edited:
Back
Top Bottom