Asgaros / asgaros-forum

Asgaros Forum WordPress plugin repository.
https://asgaros.com
GNU General Public License v2.0
90 stars 34 forks source link

Save content as Custom Post Types/Taxonomies #85

Closed KTS915 closed 7 years ago

KTS915 commented 8 years ago

This would have many benefits:

  1. It would make it easier to integrate with other plugins.
  2. It would make it possible to administer stuff from the back-end.
  3. It would make it easier to accomplish #53

Thanks!

Asgaros commented 8 years ago

ToDo-List (work in progress)

Posts:

Steps:

Topics:

Steps:

Forums:

Steps:

Asgaros commented 8 years ago

Some of the points which prevents me from doing this now is: What should happen to posts and topics when you delete an account? The default behavior in WordPress is, that you have to delete all the content the user created in the past OR you assign a new author for all his contents.

Both ways are not an optimal solution for a Forum because you may want to keep the content and dont make someone other responsible for it.

KTS915 commented 8 years ago

Thanks for considering this! In my own case, I wouldn't delete user accounts at all, but would simply move them to a sort of "standby" status, which would render them inactive unless and until the user started to reactivate them.

So I think that there are two possibilities here. One is that "delete" could instead mean demotion to a "standby" role for inactive accounts. The other would have delete mean delete, but all affected posts and topics would then be assigned to a specific account designed for the purpose. I have seen such accounts labeled "Anonymous" on other sites. I am not particularly keen on that label (what about "A Contributor"?) but the concept seems sound.

Thanks again!

Asgaros commented 8 years ago

Yes, I would not delete them too for consistency reasons but there is always some point where some user might ask you to delete their account (because of some privacy reasons or whatever). When this is the case, WordPress gives you only those two options for the existing content, which are both absolutely not optimal for a forum-system.

The one and only good solution would be a combination of choice number two (Move posts to some other account) with the creation of some placeholder-account with a name like "Deleted User" so the content can be moved to that account.

The question is: Should the forum-plugin create such a placeholder-account? People may wonder why there is such an account and just delete it. This can lead to some stupid result later when the admin of such a site dont know what to do with the existing content when deleting another user. We cant prevent him from doing it in some wrong way anyway. We also cant be sure that he is doing it in the correct way.

The best way would be some hook in the WordPress core so we can exclude the forum-content at all from that movement/deletion during the process of an account-deletion. But I dont know yet if this is possible.

KTS915 commented 8 years ago

My second suggestion above was to create what you are calling a placeholder account. The hook idea sounds good, but I too don't know if something like that exists. I'll do some searching.

So far as deleting an account is concerned, this is actually impossible already on wordpress.com (i.e. not self-hosted WordPress). To preserve privacy of someone who no longer wishes to be active, wordpress.com says that the user should just delete any personal details (e.g. about his or her website) and change the email address to that of a throwaway account.

KTS915 commented 8 years ago

Thinking about this further, I wonder whether the answer is simply for an admin to create a dummy account and then assign the posts of any deleted user to that account. So no need for your plugin to manage that aspect of things at all!

Asgaros commented 8 years ago

Yes, sure it is simple to create just an account and assign the content to it, as long as you know that its a good idea to do it. The main problem I am afraid of is the following:

The problem is just: Some of that WordPress things are not really obvious for "normal" users. Thats why I want to avoid such situations where suddenly a lot of users complain that I am responsible for "destroying" their discussions. I know that I have to change the way on how my content is stored in the database but it brings that really huge problem with it. In the actual database-structure the deletion of users have no effect at all at the forum-content.

Asgaros commented 8 years ago

It seems for this case there is some still undocumented argument for custom post types in the core:

<?php
/**
* Whether to delete posts of this type when deleting a user.
*
* If true, posts of this type belonging to the user will be moved to trash when then user is deleted.
* If false, posts of this type belonging to the user will *not* be trashed or deleted.
* If not set (the default), posts are trashed if post_type_supports( 'author' ).
* Otherwise posts are not trashed or deleted. Default null.
*
* @since 4.6.0
* @access public
* @var bool $delete_with_user
*/
public $delete_with_user = null;
?>

Lets see. I will test this. Maybe it will solve all problems. :)

Asgaros commented 7 years ago

Note for myself:

Possible import example

<?php

if (!defined('ABSPATH')) exit;

class AsgarosForumDatabase {
    const DATABASE_VERSION = 5;

    private static $instance = null;
    private static $table_forums;
    private static $table_threads;

    // AsgarosForumDatabase instance creator
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self;
        } else {
            return self::$instance;
        }
    }

    // AsgarosForumDatabase constructor
    private function __construct() {
        $this->setTables();
        register_activation_hook(__FILE__, array($this, 'activatePlugin'));
        add_action('wpmu_new_blog', array($this, 'buildSubsite'));
        add_filter('wpmu_drop_tables', array($this, 'deleteSubsite'));
        add_action('plugins_loaded', array($this, 'buildDatabase'));
    }

    private function setTables() {
        global $wpdb;

        self::$table_forums     = $wpdb->prefix.'forum_forums';
        self::$table_threads    = $wpdb->prefix.'forum_threads';
    }

    public static function getTable($name) {
        if ($name === 'forums') {
            return self::$table_forums;
        } else if ($name === 'threads') {
            return self::$table_threads;
        }
    }

    public static function activatePlugin($networkwide) {
        global $wpdb;

        if (function_exists('is_multisite') && is_multisite()) {
            // Check if it is a network activation. If so, run the database-creation for each id.
            if ($networkwide) {
                $old_blog =  $wpdb->blogid;

                // Get all blog ids
                $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");

                foreach ($blogids as $blog_id) {
                    switch_to_blog($blog_id);
                    $this->setTables();
                    $this->buildDatabase();
                }

                switch_to_blog($old_blog);
                $this->setTables();
            }
        } else {
            $this->buildDatabase();
        }
    }

    // Create tables for a new subsite in a multisite installation.
    public static function buildSubsite($blog_id, $user_id, $domain, $path, $site_id, $meta) {
        if (!function_exists('is_plugin_active_for_network')) {
            require_once(ABSPATH.'/wp-admin/includes/plugin.php');
        }

        if (is_plugin_active_for_network('asgaros-forum/asgaros-forum.php')) {
            switch_to_blog($blog_id);
            $this->setTables();
            $this->buildDatabase();
            restore_current_blog();
            $this->setTables();
        }
    }

    // Delete tables during a subsite uninstall.
    public function deleteSubsite($tables) {
        global $wpdb;
        $tables[] = $wpdb->prefix.'forum_forums';
        $tables[] = $wpdb->prefix.'forum_threads';
        return $tables;
    }

    public static function buildDatabase() {
        global $wpdb;
        $database_version_installed = get_option('asgarosforum_db_version');

        if ($database_version_installed != self::DATABASE_VERSION) {
            $charset_collate = $wpdb->get_charset_collate();

            $sql1 = "
            CREATE TABLE ".self::$table_forums." (
            id int(11) NOT NULL auto_increment,
            name varchar(255) NOT NULL default '',
            parent_id int(11) NOT NULL default '0',
            parent_forum int(11) NOT NULL default '0',
            description varchar(255) NOT NULL default '',
            sort int(11) NOT NULL default '0',
            closed int(11) NOT NULL default '0',
            PRIMARY KEY  (id)
            ) $charset_collate;";

            $sql2 = "
            CREATE TABLE ".self::$table_threads." (
            id int(11) NOT NULL auto_increment,
            parent_id int(11) NOT NULL default '0',
            views int(11) NOT NULL default '0',
            name varchar(255) NOT NULL default '',
            status varchar(20) NOT NULL default 'normal_open',
            PRIMARY KEY  (id)
            ) $charset_collate;";

            require_once(ABSPATH.'wp-admin/includes/upgrade.php');

            dbDelta($sql1);
            dbDelta($sql2);

            /*
            list($usec, $sec) = explode(" ", microtime());
            $time_start = ((float)$usec + (float)$sec);

            $createDummyData = false;

            if ($createDummyData) {
                $i = 0;
                while ($i < 1000) {
                    $i++;

                    $wpdb->query('INSERT INTO '.self::$table_threads.' (name, parent_id) VALUES("Test thread '.$i.'", 1);');
                    $newtopic = $wpdb->insert_id;

                    $j = 0;
                    while ($j < 10) {
                        $j++;
                        $wpdb->query('INSERT INTO '.$wpdb->prefix.'forum_posts (text, parent_id, date, author_id) VALUES("Test post '.$j.'", '.$newtopic.', "'.current_time('Y-m-d H:i:s').'", '.get_current_user_id().');');
                    }
                }
            }
            */

            if ($database_version_installed < 5) {
                // Get IDs of all posts which can be imported.
                $post_ids = $wpdb->get_col('SELECT id FROM '.$wpdb->prefix.'forum_posts;');

                // Check if some of them got already imported and get their IDs.
                $already_imported_args = array(
                    'post_type'     => 'af_post',
                    'numberposts'   => -1,
                    'meta_query'    => array(
                        array(
                            'key'       => 'original_id',
                            'value'     => $post_ids,
                            'compare'   => 'IN'
                        ),
                    )
                );
                $already_imported = get_posts($already_imported_args);
                $already_imported_ids = array();
                foreach ($already_imported as $already_imported_element) {
                    $already_imported_ids[] = $already_imported_element->original_id;
                }
                $already_imported_ids = implode(',', $already_imported_ids);

                // Delete data of already imported posts.
                $wpdb->query('DELETE FROM '.$wpdb->prefix.'forum_posts WHERE id IN ('.$already_imported_ids.');');

                // Now get all posts for the import process.
                $posts = $wpdb->get_results('SELECT p.id, p.text, p.parent_id, p.date, p.date_edit, p.author_id, p.uploads, t.parent_id AS parent_forum FROM '.$wpdb->prefix.'forum_posts AS p, '.$wpdb->prefix.'forum_threads AS t WHERE p.parent_id = t.id ORDER BY p.id ASC;');

                // Set insert_post_data filter because of some WordPress core bug (see function for description).
                add_filter('wp_insert_post_data', array($this, 'filter_insert_post_data'), 10, 2);

                wp_defer_term_counting(true);
                wp_defer_comment_counting(true);

                // Now start the import process for each post.
                foreach ($posts as $post) {
                    $post_args = array(
                        'post_author'   => $post->author_id,
                        'post_date'     => $post->date,
                        'post_content'  => $post->text,
                        'post_status'   => 'publish',
                        'post_type'     => 'af_post',
                        'post_modified' => $post->date_edit,
                        'meta_input'    => array(
                            'parent_af_thread'  => $post->parent_id,
                            'parent_af_forum'   => $post->parent_forum,
                            'uploads'           => maybe_unserialize($post->uploads),
                            'original_id'       => $post->id
                        )
                    );

                    // TODO: slug/link scheint so, als wenn posttype bei import noch nicht bekannt ist.
                    $result = wp_insert_post($post_args);
                }

                // Remove insert_post_data filter.
                remove_filter('wp_insert_post_data', array($this, 'filter_insert_post_data'));

                wp_defer_term_counting(false);
                wp_defer_comment_counting(false);

                // Delete old posts table.
                $wpdb->query('DROP TABLE IF EXISTS '.$wpdb->prefix.'forum_posts;');

                // Delete all meta-data used for checks.
                delete_metadata('post', null, 'original_id', '', true);

                // Set version to 5 to skip this part when the script gets not fully executed.
                update_option('asgarosforum_db_version', 5);

                /*
                print_r('<pre>');
                print_r(count($posts));
                print_r('</pre>');
                */
            }

            /*
            list($usec, $sec) = explode(" ", microtime());
            $time_end = ((float)$usec + (float)$sec);
            $time = $time_end - $time_start;
            echo "In $time Sekunden nichts getan\n";
            die();
            */

            update_option('asgarosforum_db_version', self::DATABASE_VERSION);
        }
    }

    public static function filter_insert_post_data($data, $postarr) {
        // Make sure that the correct edited-date is set because of bug in WordPress core.
        // Reference: https://core.trac.wordpress.org/ticket/36595
        if ($postarr['post_modified'] !== '0000-00-00 00:00:00') {
            $data['post_modified'] = $postarr['post_modified'];
            $data['post_modified_gmt'] = get_gmt_from_date($postarr['post_modified']);
        }

        return $data;
    }
}

?>
Asgaros commented 7 years ago

I will close this issue for now because at the moment the WordPress API has too many drawbacks to accomplish this in a nice way. One example:

A term with the name provided already exists with this parent

Some users can find it necessary to have terms (forums/categories) with the same name in the same level when they split their accessibility by groups but dont want to give them different names.