public function save_setting()
 {
     $contributor_appearances = $_REQUEST['podlove_contributor_defaults']['contributor'];
     foreach (DefaultContribution::all() as $contribution) {
         $contribution->delete();
     }
     $position = 0;
     foreach ($contributor_appearances as $contributor_appearance) {
         foreach ($contributor_appearance as $contributor_id => $contributor) {
             $c = new DefaultContribution();
             if (isset($contributor['role']) && ($role = ContributorRole::find_one_by_slug($contributor['role']))) {
                 $c->role_id = $role->id;
             }
             if (isset($contributor['group']) && ($group = ContributorGroup::find_one_by_slug($contributor['group']))) {
                 $c->group_id = $group->id;
             }
             $c->contributor_id = $contributor_id;
             if (isset($contributor['comment'])) {
                 $c->comment = $contributor['comment'];
             }
             $c->position = $position++;
             $c->save();
         }
     }
 }
    public static function adn_contributor_filter()
    {
        if (!is_admin()) {
            return;
        }
        if (!\Podlove\Modules\Base::is_module_settings_page()) {
            return;
        }
        if (!\Podlove\Modules\Base::is_active('app_dot_net')) {
            return;
        }
        $adn = \Podlove\Modules\AppDotNet\App_Dot_Net::instance();
        $roles = \Podlove\Modules\Contributors\Model\ContributorRole::all();
        $groups = \Podlove\Modules\Contributors\Model\ContributorGroup::all();
        $selected_role = $adn->get_module_option('adn_contributor_filter_role');
        $selected_group = $adn->get_module_option('adn_contributor_filter_group');
        if (count($roles) > 0 || count($groups) > 0) {
            $adn->register_option('contributor_filter', 'callback', array('label' => __('Contributor Filter', 'podlove'), 'description' => __('<br />Filter <code title="' . __('The contributors of the episode', 'podlove') . '">{episodeContributors}</code> by Group and/or role', 'podlove'), 'callback' => function () use($selected_role, $selected_group, $roles, $groups) {
                if (count($groups) > 0) {
                    ?>
						<select class="chosen" id="podlove_module_app_dot_net_adn_contributor_filter_group" name="podlove_module_app_dot_net[adn_contributor_filter_group]">
							<option value="">&nbsp;</option>
							<?php 
                    foreach ($groups as $group) {
                        echo "<option value='" . $group->id . "' " . ($selected_group == $group->id ? "selected" : "") . ">" . $group->title . "</option>";
                    }
                    ?>
						</select> Group
					<?php 
                }
                if (count($roles) > 0) {
                    ?>
						<select class="chosen" id="podlove_module_app_dot_net_adn_contributor_filter_role" name="podlove_module_app_dot_net[adn_contributor_filter_role]">
							<option value="">&nbsp;</option>
							<?php 
                    foreach ($roles as $role) {
                        echo "<option value='" . $role->id . "' " . ($selected_role == $role->id ? "selected" : "") . ">" . $role->title . "</option>";
                    }
                    ?>
						</select> Role
					<?php 
                }
            }));
        }
    }
 public function prepare_items()
 {
     // number of items per page
     $per_page = 10;
     // define column headers
     $columns = $this->get_columns();
     $hidden = array();
     $sortable = $this->get_sortable_columns();
     $this->_column_headers = array($columns, $hidden, $sortable);
     // retrieve data
     $data = \Podlove\Modules\Contributors\Model\ContributorRole::all('ORDER BY title ASC');
     // get current page
     $current_page = $this->get_pagenum();
     // get total items
     $total_items = count($data);
     // extrage page for current page only
     $data = array_slice($data, ($current_page - 1) * $per_page, $per_page);
     // add items to table
     $this->items = $data;
     // register pagination options & calculations
     $this->set_pagination_args(array('total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil($total_items / $per_page)));
 }
<?php

namespace Podlove\Modules\Contributors\Model;

use Podlove\Model\Base;
class ContributorRole extends Base
{
    public static function selectOptions()
    {
        $list = array();
        foreach (self::all() as $role) {
            $list[$role->slug] = $role->title;
        }
        return $list;
    }
}
ContributorRole::property('id', 'INT NOT NULL AUTO_INCREMENT PRIMARY KEY');
ContributorRole::property('slug', 'VARCHAR(255)');
ContributorRole::property('title', 'VARCHAR(255)');
    /**
     * Episodes
     *
     * Filter and order episodes with parameters:
     * 
     * - group: Filter by contribution group. Default: ''.
     * - role: Filter by contribution role. Default: ''.
     * - post_status: Publication status of the post. Defaults to 'publish'
     * - order: Designates the ascending or descending order of the 'orderby' parameter. Defaults to 'DESC'.
     *   - 'ASC' - ascending order from lowest to highest values (1, 2, 3; a, b, c).
     *   - 'DESC' - descending order from highest to lowest values (3, 2, 1; c, b, a).
     * - orderby: Sort retrieved episodes by parameter. Defaults to 'publicationDate'.
     *   - 'publicationDate' - Order by publication date.
     *   - 'recordingDate' - Order by recording date.
     *   - 'title' - Order by title.
     *   - 'slug' - Order by episode slug.
     *	 - 'limit' - Limit the number of returned episodes.
     */
    public function episodes($args = [])
    {
        return $this->with_blog_scope(function () use($args) {
            global $wpdb;
            $joins = "";
            if (isset($args['group']) && $args['group']) {
                $joins .= "INNER JOIN " . ContributorGroup::table_name() . " g ON g.id = ec.group_id AND g.slug = '" . esc_sql($args['group']) . "'";
            }
            if (isset($args['role']) && $args['role']) {
                $joins .= "INNER JOIN " . ContributorRole::table_name() . " r ON r.id = ec.role_id AND r.slug = '" . esc_sql($args['role']) . "'";
            }
            $where = "ec.contributor_id = " . (int) $this->id;
            if (isset($args['post_status']) && in_array($args['post_status'], get_post_stati())) {
                $where .= " AND p.post_status = '" . $args['post_status'] . "'";
            } else {
                $where .= " AND p.post_status = 'publish'";
            }
            // order
            $order_map = array('publicationDate' => 'p.post_date', 'recordingDate' => 'e.recordingDate', 'slug' => 'e.slug', 'title' => 'p.post_title');
            if (isset($args['orderby']) && isset($order_map[$args['orderby']])) {
                $orderby = $order_map[$args['orderby']];
            } else {
                $orderby = $order_map['publicationDate'];
            }
            if (isset($args['order'])) {
                $args['order'] = strtoupper($args['order']);
                if (in_array($args['order'], array('ASC', 'DESC'))) {
                    $order = $args['order'];
                } else {
                    $order = 'DESC';
                }
            } else {
                $order = 'DESC';
            }
            if (isset($args['limit'])) {
                $limit = ' LIMIT ' . (int) $args['limit'];
            } else {
                $limit = '';
            }
            $sql = '
				SELECT
					ec.episode_id
				FROM
					' . EpisodeContribution::table_name() . ' ec
					INNER JOIN ' . \Podlove\Model\Episode::table_name() . ' e ON e.id = ec.episode_id
					INNER JOIN ' . $wpdb->posts . ' p ON p.ID = e.post_id
					' . $joins . '
				WHERE ' . $where . '
				GROUP BY ec.episode_id
				ORDER BY ' . $orderby . ' ' . $order . $limit;
            $episode_ids = $wpdb->get_col($sql);
            return array_map(function ($episode_id) {
                return \Podlove\Model\Episode::find_one_by_id($episode_id);
            }, array_unique($episode_ids));
        });
    }
 public function getRole()
 {
     return $this->with_blog_scope(function () {
         return ContributorRole::find_by_id($this->role_id);
     });
 }
    private static function group_or_role_stats_table($context, $numbers)
    {
        ?>
		<table cellspacing="0" cellspadding="0">
			<thead>
				<tr>
					<th><?php 
        echo $context == 'group' ? __('Group', 'podlove') : __('Role', 'podlove');
        ?>
</th>
					<th><?php 
        _e('Female', 'podlove');
        ?>
</th>
					<th><?php 
        _e('Male', 'podlove');
        ?>
</th>
					<th><?php 
        _e('Not Attributed', 'podlove');
        ?>
</th>
				</tr>
			</thead>
			<tbody>
			<?php 
        foreach ($numbers as $group_or_role_id => $group_or_role_numbers) {
            $group_or_role = $context == 'group' ? ContributorGroup::find_one_by_id($group_or_role_id) : ContributorRole::find_one_by_id($group_or_role_id);
            // This return either a group or a role object
            if (!$group_or_role) {
                continue;
            }
            ?>
					<tr>
						<td><?php 
            echo $group_or_role->title;
            ?>
</td>
						<td><?php 
            echo self::get_percentage($group_or_role_numbers['by_gender']['female'], $group_or_role_numbers['total']);
            ?>
%</td>
						<td><?php 
            echo self::get_percentage($group_or_role_numbers['by_gender']['male'], $group_or_role_numbers['total']);
            ?>
%</td>
						<td><?php 
            echo self::get_percentage($group_or_role_numbers['by_gender']['none'], $group_or_role_numbers['total']);
            ?>
%</td>
					</tr>
				<?php 
        }
        ?>
			</tbody>
		</table>
		<?php 
    }
 public function getRole()
 {
     return ContributorRole::find_by_id($this->role_id);
 }
    public function feed_settings($wrapper)
    {
        $contributors_roles = \Podlove\Modules\Contributors\Model\ContributorRole::all();
        $contributors_groups = \Podlove\Modules\Contributors\Model\ContributorGroup::all();
        $option_name = 'podlove_feed_' . $_REQUEST['feed'] . '_contributor_filter';
        $selected_filter = get_option($option_name);
        if (!$selected_filter) {
            $selected_filter = array('group' => NULL, 'role' => NULL);
        }
        $wrapper->subheader(__('Contributors', 'podlove'));
        $wrapper->callback('services_form_table', array('label' => __('Contributor Filter', 'podlove'), 'callback' => function () use($contributors_roles, $contributors_groups, $selected_filter) {
            ?>
					<select name="podlove_feed[contributor_filter][group]" id="">
						<option value=""></option>
						<?php 
            foreach ($contributors_groups as $group) {
                echo "<option value='" . $group->id . "' " . ($group->id == $selected_filter['group'] ? 'selected' : '') . ">" . $group->title . "</option>";
            }
            ?>
					</select>
					<?php 
            echo __('Group', 'podlove');
            ?>

					<select name="podlove_feed[contributor_filter][role]" id="">
						<option value=""></option>
						<?php 
            foreach ($contributors_roles as $role) {
                echo "<option value='" . $role->id . "' " . ($role->id == $selected_filter['role'] ? 'selected' : '') . ">" . $role->title . "</option>";
            }
            ?>
					</select>
					<?php 
            echo __('Role', 'podlove');
            ?>
					<p>
						<span class="description"><?php 
            echo __('Limit contributors to the given group and/or role.', 'podlove');
            ?>
</span>
					</p>
				<?php 
        }));
        return $wrapper;
    }