<?php
namespace SabaiApps\Directories\Component\Voting;

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Exception;
use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\Field;
use SabaiApps\Directories\Component\Display;
use SabaiApps\Directories\Component\Entity;
use SabaiApps\Directories\Component\CSV;
use SabaiApps\Framework\Criteria\IsCriteria;

class VotingComponent extends AbstractComponent implements
	System\IMainRouter,
	System\IAdminRouter,
	Field\ITypes,
	Field\IWidgets,
	Field\IRenderers,
	Field\IFilters,
	Display\IButtons,
	Display\IStatistics,
	ITypes,
	CSV\IExporters,
	CSV\IImporters {

	const VERSION = '2.0.6', PACKAGE = 'directories-pro';

	protected static $_types = array();

	public static function interfaces() {
		return array( 'Dashboard\IPanels', 'Notification\IEvents' );
	}

	public static function description() {
		return 'Adds voting/rating/bookmarking features to content.';
	}

	public function getDefaultConfig() {
		return array(
			'report_enable'           => false,
			'report_require_comment'  => true,
			'report_threshold_num'    => 10,
			'report_threshold_sum'    => 50,
			'report_threshold_avg'    => 5,
			'report_threshold_action' => 'pending', // 'trash', 'draft', 'pending', 'none'
			'report_reset_action'     => true, // Reset reports when action is taken
		);
	}

	public function onCoreComponentsLoaded() {
		$this->_application->setHelper( 'Voting_CanVote', array( __CLASS__, 'canVoteHelper' ) );
	}

	public static function canVoteHelper( Application $application, Entity\Type\IEntity $entity, $type, $isDownVote = false ) {
		$info = $application->Voting_Types_impl( $type )->votingTypeInfo();

		// Permission required?
		if ( empty( $info['require_permission'] ) ) {
			// Allow if not anonymous
			return ! $application->getUser()->isAnonymous();
		}

		// Allow guest?
		if ( empty( $info['allow_anonymous'] ) && $application->getUser()->isAnonymous() ) {
			return false;
		}

		// Has vote permission?
		if ( ! $application->HasPermission( 'voting_' . $type . '_' . $entity->getBundleName() ) ) {
			return false;
		}

		// Additional check for down voting
		if ( $isDownVote ) {
			return empty( $info['require_down_permission'] )
				|| $application->HasPermission( 'voting_' . $type . '_down_' . $entity->getBundleName() );
		}

		// Additional check for voting own item
		if ( $application->Entity_IsAuthor( $entity ) ) {
			return empty( $info['require_own_permission'] )
				|| $application->HasPermission( 'voting_' . $type . '_own_' . $entity->getBundleName() );
		}

		return true;
	}

	public function systemMainRoutes( $lang = null ) {
		$routes = array(
			'/_drts/voting/vote' => array(
				'controller'      => 'VoteEntity',
				'type'            => Application::ROUTE_CALLBACK,
				'callback_path'   => 'vote_entity',
				'access_callback' => true,
			),
		);

		foreach ( $this->_application->Entity_Bundles() as $bundle ) {
			if ( ! $this->_application->Entity_BundleTypeInfo( $bundle->type, 'voting_enable' ) ) {
				continue;
			}

			$routes[ $bundle->getPath( false, $lang ) . '/vote' ] = array(
				'controller'      => 'VoteEntityForm',
				'access_callback' => true,
				'title_callback'  => true,
				'callback_path'   => 'vote_entity_form',
			);
		}

		if ( $this->_application->isComponentLoaded( 'Dashboard' ) ) {
			// For dashboard
			$dashboard_slug = $this->_application->getComponent( 'Dashboard' )->getSlug( 'dashboard', $lang );
			$routes[ '/' . $dashboard_slug . '/:user_name/:panel_name/votes' ] = array(
				'controller'      => 'DashboardVotes',
				'callback_path'   => 'dashboard_votes',
				'access_callback' => true,
			);
		}

		return $routes;
	}

	public function systemOnAccessMainRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'vote_entity':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $vote_type = $context->getRequest()->asStr( 'voting_type' ) )
						|| ( ! $entity_id = $context->getRequest()->asInt( 'entity_id' ) )
						|| ( ! $entity_type = $context->getRequest()->asStr( 'entity_type' ) )
						|| ! $this->_application->Voting_Types_impl( $vote_type )
						|| ( ! $entity = $this->_application->Entity_Entity( $entity_type, $entity_id ) )
					) {
						return false;
					}

					$context->entity      = $entity;
					$context->voting_type = $vote_type;
				}
				return true;

			case 'vote_entity_form':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $vote_type = $context->getRequest()->asStr( 'voting_type' ) )
						|| ( ! $entity_id = $context->getRequest()->asInt( 'entity_id' ) )
						|| ! $this->_application->Voting_Types_impl( $vote_type )
						|| ( ! $entity = $this->_application->Entity_Entity( $context->bundle->entitytype_name, $entity_id ) )
					) {
						return false;
					}

					$context->entity      = $entity;
					$context->voting_type = $vote_type;
				}
				return true;

			case 'dashboard_votes':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( $context->dashboard_panel !== 'voting_votes' ) {
						return false;
					}

					try {
						$this->_application->Voting_Types_impl( $context->dashboard_panel_link );
					} catch ( Exception\IException $e ) {
						$this->_application->logError( $e );
						return false;
					}
				}
				return true;
		}
	}

	public function systemMainRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'vote_entity_form':
				return sprintf(
					$this->_application->Voting_Types_impl( $context->voting_type )->votingTypeInfo( 'label_action' ),
					$context->bundle->getLabel( 'singular' )
				);
		}
	}

	public function systemAdminRoutes() {
		$routes = array(
			'/directories/system/voting_votes' => array(
				'controller'      => 'ViewVotes',
				'access_callback' => true,
				'title_callback'  => true,
				'callback_path'   => 'view_votes',
			),
		);
		foreach ( array_keys( $this->_application->Entity_BundleTypes() ) as $bundle_type ) {
			if ( ! $admin_path = $this->_application->Entity_BundleTypeInfo( $bundle_type, 'admin_path' ) ) {
				continue;
			}

			$routes += array(
				$admin_path . '/votes/clear' => array(
					'controller'      => 'ClearVotes',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => 'clear_votes',
				),
			);
		}

		return $routes;
	}

	public function systemOnAccessAdminRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'view_votes':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $entity_id = $context->getRequest()->asInt( 'entity_id' ) )
						|| ( ! $entity_type = $context->getRequest()->asStr( 'entity_type' ) )
						|| ( ! $entity = $this->_application->Entity_Entity( $entity_type, $entity_id ) )
						|| ( ! $voting_type = $context->getRequest()->asStr( 'voting_type' ) )
						|| ( ! $this->_application->Voting_Types_impl( $voting_type, true ) )
					) {
						$context->setNotFoundError();
						return false;
					}
					$context->entity      = $entity;
					$context->voting_type = $voting_type;
				}
				return true;
			case 'clear_votes':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ( ! $entity_id = $context->getRequest()->asInt( 'entity_id' ) )
						|| ( ! $field_name = $context->getRequest()->asStr( 'field_name' ) )
						|| ( ! $entity = $this->_application->Entity_Entity( $context->bundle->entitytype_name, $entity_id ) )
						|| $context->bundle->name !== $entity->getBundleName()
						|| ( ! $field = $this->_application->Entity_Field( $context->bundle, $field_name ) )
						|| strpos( $field->getFieldName(), 'voting_' ) !== 0
						|| ( ! $type = substr( $field->getFieldName(), strlen( 'voting_' ) ) )
						|| ( ! $type_impl = $this->_application->Voting_Types_impl( $type, true ) )
					) {
						$context->setNotFoundError();
						return false;
					}
					// Require edit entity permission
					if ( ! $this->_application->Entity_IsRoutable( $context->bundle, 'edit', $entity ) ) {
						$context->setError();
						return false;
					}
					$context->entity      = $entity;
					$context->voting_type = $type;
				}
				return true;
		}
	}

	public function systemAdminRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'view_votes':
				return $this->_application->Voting_Types_impl( $context->voting_type )->votingTypeInfo( 'label' )
					. ' - ' . $this->_application->Entity_Title( $context->entity );
			case 'clear_votes':
				return $this->_application->Voting_Types_impl( $context->voting_type )->votingTypeInfo( 'label' )
					. ' - ' . __( 'Clear all', 'directories-pro' );
		}
	}

	public function fieldGetTypeNames() {
		return array( 'voting_vote' );
	}

	public function fieldGetType( $name ) {
		return new FieldType\FieldType( $this->_application, $name );
	}

	public function fieldGetWidgetNames() {
		return array( 'voting_vote' );
	}

	public function fieldGetWidget( $name ) {
		return new FieldWidget\FieldWidget( $this->_application, $name );
	}

	public function fieldGetRendererNames() {
		return array( 'voting_rating' );
	}

	public function fieldGetRenderer( $name ) {
		switch ( $name ) {
			case 'voting_rating':
				return new FieldRenderer\RatingFieldRenderer( $this->_application, $name );
		}
	}

	public function fieldGetFilterNames() {
		return array( 'voting_rating' );
	}

	public function fieldGetFilter( $name ) {
		switch ( $name ) {
			case 'voting_rating':
				return new FieldFilter\RatingFieldFilter( $this->_application, $name );
		}
	}

	public function onEntityPermissionsFilter( &$permissions, $bundle ) {
		foreach ( array_keys( $this->_application->Voting_Types() ) as $type ) {
			if ( ! $type_info = $this->_isVotingPermissionRequired( $bundle, $type ) ) {
				continue;
			}

			$guest_allowed                    = ! empty( $type_info['allow_anonymous'] );
			$permissions[ 'voting_' . $type ] = array(
				'title'         => sprintf( $type_info['permission_label'], $bundle->getLabel() ),
				'guest_allowed' => $guest_allowed,
				'default'       => true,
				'weight'        => 100,
			);
			if ( ! empty( $type_info['own_permission_label'] ) ) {
				$permissions[ 'voting_' . $type . '_own' ] = array(
					'title'         => sprintf( $type_info['own_permission_label'], $bundle->getLabel() ),
					'guest_allowed' => $guest_allowed,
					'weight'        => 101,
				);
			}
			if ( ! empty( $type_info['require_down_permission'] ) ) {
				$permissions[ 'voting_' . $type . '_down' ] = array(
					'title'         => sprintf( $type_info['down_permission_label'], $bundle->getLabel() ),
					'guest_allowed' => $guest_allowed,
					'default'       => true,
					'weight'        => 102,
				);
			}
		}
	}

	protected function _isVotingPermissionRequired( $bundle, $type ) {
		return ( ( $voting = $this->_application->Entity_BundleTypeInfo( $bundle, 'voting_enable' ) )
			&& in_array( $type, $voting )
			&& ( $type_impl = $this->_application->Voting_Types_impl( $type ) )
			&& ( $type_info = $type_impl->votingTypeInfo() )
			&& ! empty( $type_info['require_permission'] )
		) ? $type_info : false;
	}

	public function onEntityCreateBundlesSuccess( $bundles ) {
		$reload = false;
		foreach ( $bundles as $bundle ) {
			if ( $voting = $this->_application->Entity_BundleTypeInfo( $bundle, 'voting_enable' ) ) {
				foreach ( $voting as $type ) {
					if ( ! $type_impl = $this->_application->Voting_Types_impl( $type, true ) ) {
						continue;
					}

					if ( ! $label = $type_impl->votingTypeInfo( 'label_field' ) ) {
						$label = $type_impl->votingTypeInfo( 'label' );
					}
					$this->_application->getComponent( 'Entity' )->createEntityField(
						$bundle,
						'voting_' . $type,
						array(
							'type'          => 'voting_vote',
							'label'         => $label,
							'weight'        => 99,
							'max_num_items' => 1, // Only 1 entry per entity should be created
						),
						true
					);
					$reload = true;
				}
			}
		}
		if ( $reload ) {
			// Reload system routing tables to reflect changes
			$this->_application->getComponent( 'System' )->reloadRoutes( $this );
		}
	}

	public function onEntityUpdateBundlesSuccess( $bundles ) {
		if ( empty( $bundles ) ) {
			return;
		}

		$this->onEntityCreateBundlesSuccess( $bundles );
	}

	public function onEntityDeleteBundlesCommitted( array $bundles, $deleteContent ) {
		if ( empty( $bundles ) || empty( $deleteContent ) ) {
			return;
		}

		$criteria = $this->getModel()->createCriteria( 'Vote' )->bundleName_in( array_keys( $bundles ) );
		$this->getModel()->getGateway( 'Vote' )->deleteByCriteria( $criteria );
	}

	public function displayGetButtonNames( Entity\Model\Bundle $bundle ) {
		$ret = array();
		if ( $voting = $this->_application->Entity_BundleTypeInfo( $bundle, 'voting_enable' ) ) {
			foreach ( $voting as $type ) {
				if ( ! $type_impl = $this->_application->Voting_Types_impl( $type, true ) ) {
					continue;
				}

				if ( $names = $type_impl->votingTypeInfo( 'vote_button' ) ) {
					if ( is_array( $names ) ) {
						foreach ( $names as $name ) {
							$ret[] = $name;
						}
					} else {
						$ret[] = 'voting_' . $type;
					}
				}
				if ( $type_impl->votingTypeInfo( 'vote_form' ) ) {
					$ret[] = 'voting_form_' . $type;
				}
			}
		}
		return $ret;
	}

	public function displayGetButton( $name ) {
		if ( strpos( $name, 'voting_form_' ) === 0 ) {
			return new DisplayButton\VoteFormDisplayButton( $this->_application, $name );
		} elseif ( strpos( $name, 'voting_' ) === 0 ) {
			return new DisplayButton\VoteDisplayButton( $this->_application, $name );
		}
	}

	public function displayGetStatisticNames( Entity\Model\Bundle $bundle ) {
		$ret = array();
		if ( $voting = $this->_application->Entity_BundleTypeInfo( $bundle, 'voting_enable' ) ) {
			foreach ( $voting as $type ) {
				if ( ( ! $type_impl = $this->_application->Voting_Types_impl( $type, true ) )
					|| ( ! $names = $type_impl->votingTypeInfo( 'entity_statistic' ) )
				) {
					continue;
				}

				if ( is_array( $names ) ) {
					foreach ( $names as $name ) {
						$ret[] = $name;
					}
				} else {
					$ret[] = 'voting_' . $type;
				}
			}
		}
		return $ret;
	}

	public function displayGetStatistic( $name ) {
		return new DisplayStatistic\CountDisplayStatistic( $this->_application, $name );
	}

	public function votingGetTypeNames() {
		return array( 'updown', 'bookmark', 'rating', 'report' );
	}

	public function votingGetType( $name ) {
		switch ( $name ) {
			case 'updown':
				return new Type\UpdownType( $this->_application, $name );
			case 'bookmark':
				return new Type\BookmarkType( $this->_application, $name );
			case 'rating':
				return new Type\RatingType( $this->_application, $name );
			case 'report':
				return new Type\ReportType( $this->_application, $name );
		}
	}

	public function dashboardGetPanelNames() {
		return array( 'voting_votes' );
	}

	public function dashboardGetPanel( $name ) {
		return new DashboardPanel\VotesDashboardPanel( $this->_application, $name );
	}

	public function onViewQueryFilter( $query, $bundle, $mode, $name, $settings, $isDefault ) {
		if ( ! empty( $settings['query']['voting_bookmark_only'] ) ) {
			if ( $this->_application->getUser()->isAnonymous() ) {
				if ( ( $cookie = $this->_application->System_Cookie( 'drts-voting-bookmark-' . $bundle->entitytype_name ) )
					&& ( $ids = explode( '/', $cookie ) )
				) {
					$query->fieldIsIn( 'id', $ids );
				} else {
					$query->fieldIs( 'id', -1 );
				}
			} else {
				$user_id = array(
					'tables'      => $tables = array(
						$this->_application->getDB()->getResourcePrefix() . 'voting_vote'  => array(
							'alias' => 'voting_vote',
							'on'    => 'vote_entity_id = %3$s',
						),
					),
					'column'      => 'voting_vote.vote_user_id',
					'column_type' => Application::COLUMN_INTEGER,
				);
				$field_name = array(
					'tables'      => $tables,
					'column'      => 'voting_vote.vote_field_name',
					'column_type' => Application::COLUMN_VARCHAR,
				);
				$query->addCriteria( new IsCriteria( $user_id, $this->_application->getUser()->id ) )
					->addCriteria( new IsCriteria( $field_name, 'voting_bookmark' ) );
			}
		}
	}

	public function onViewFeatureSettingsFormFilter( &$form, $bundle, $settings, $submitValues ) {
		if ( ! $voting = (array) $this->_application->Entity_BundleTypeInfo( $bundle, 'voting_enable' ) ) {
			return;
		}

		if ( in_array( 'bookmark', $voting ) ) {
			$form['query']['voting_bookmark_only'] = array(
				'#type'          => 'checkbox',
				'#title'         => __( 'Show bookmarked items only', 'directories-pro' ),
				'#default_value' => ! empty( $settings['query']['voting_bookmark_only'] ),
				'#horizontal'    => true,
				'#weight'        => 30,
			);
		}
	}

	public function notificationGetEventNames() {
		return array( 'voting_report_threshold_reached' );
	}

	public function notificationGetEvent( $name ) {
		if ( $name === 'voting_report_threshold_reached' ) {
			return new NotificationEvent\ReportThresholdReachedNotificationEvent( $this->_application, $name );
		}
	}

	public function onDirectoryAdminSettingsFormFilter( &$form ) {
		// Moved from Directory to System component
		$this->onSystemAdminSettingsFormFilter( $form );
	}

	public function onSystemAdminSettingsFormFilter( &$form ) {
		$default_severity_levels = Type\ReportType::reasons( $this->_application );
		$current_severity_levels = empty( $this->_config['report_severity_levels'] ) ? array() : $this->_config['report_severity_levels'];
		$severity_levels         = $current_severity_levels + $default_severity_levels;
		krsort( $severity_levels );
		$form['fields'][ $this->_name . '_report' ] = array(
			'#component'              => $this->_name,
			'#tab'                    => 'Security',
			'#title'                  => __( 'Report Moderation Settings', 'directories-pro' ),
			'report_enable'           => array(
				'#type'          => 'checkbox',
				'#title'         => __( 'Enable report moderation', 'directories-pro' ),
				'#default_value' => ! empty( $this->_config['report_enable'] ),
				'#horizontal'    => true,
			),
			'report_severity_levels'  => array(
				'#title'                  => __( 'Severity levels', 'directories-pro' ),
				'#type'                   => 'options',
				'#disable_icon'           => true,
				'#disable_add_csv'        => true,
				'#disable_add'            => true,
				'#disable_sort'           => true,
				'#multiple'               => true,
				'#default_value'          => array(
					'options' => $severity_levels,
					'default' => array_keys( empty( $current_severity_levels ) ? $default_severity_levels : $current_severity_levels ),
				),
				'#options_value_disabled' => array_keys( $default_severity_levels ),
				'#options_placeholder'    => $default_severity_levels,
				'#value_title'            => __( 'Severity score', 'directories-pro' ),
				'#default_options_only'   => true,
				'#horizontal'             => true,
				'#states'                 => $states              = array(
					'visible' => array(
						'input[name="' . $this->_name . '_report[report_enable]"]' => array(
							'type'  => 'checked',
							'value' => true,
						),
					),
				),
				'#required'               => true,
			),
			'report_require_comment'  => array(
				'#type'          => 'checkbox',
				'#title'         => __( 'Require additional details', 'directories-pro' ),
				'#default_value' => ! empty( $this->_config['report_require_comment'] ),
				'#horizontal'    => true,
				'#states'        => $states,
			),
			'report_threshold_num'    => array(
				'#type'          => 'slider',
				'#title'         => __( 'Report count threshold', 'directories-pro' ),
				'#default_value' => $this->_config['report_threshold_num'] ?? 10,
				'#horizontal'    => true,
				'#states'        => $states,
				'#max'           => 50,
			),
			'report_threshold_sum'    => array(
				'#type'          => 'slider',
				'#title'         => __( 'Severity total threshold', 'directories-pro' ),
				'#default_value' => $this->_config['report_threshold_sum'] ?? 50,
				'#horizontal'    => true,
				'#states'        => $states,
				'#max'           => 500,
			),
			'report_threshold_avg'    => array(
				'#type'          => 'slider',
				'#title'         => __( 'Average severity threshold', 'directories-pro' ),
				'#default_value' => $this->_config['report_threshold_avg'] ?? 5,
				'#horizontal'    => true,
				'#states'        => $states,
				'#max'           => 10,
			),
			'report_threshold_action' => array(
				'#type'          => 'select',
				'#title'         => __( 'Action on reaching threshold', 'directories-pro' ),
				'#options'       => array(
					'trash'   => __( 'Move to trash', 'directories-pro' ),
					'draft'   => __( 'Set status to Draft', 'directories-pro' ),
					'pending' => __( 'Set status to Pending Review', 'directories-pro' ),
					'none'    => __( 'None', 'directories-pro' ),
				),
				'#default_value' => $this->_config['report_threshold_action'] ?? 'pending',
				'#horizontal'    => true,
				'#states'        => $states,
			),
			'report_reset_action'     => array(
				'#type'          => 'checkbox',
				'#title'         => __( 'Reset reports on action', 'directories-pro' ),
				'#default_value' => ! empty( $this->_config['report_reset_action'] ),
				'#horizontal'    => true,
				'#states'        => $states,
			),
			'report_threshold_notify' => array(
				'#type'       => 'item',
				'#title'      => __( 'Notify on reaching threshold', 'directories-pro' ),
				'#value'      => __( 'A notification will be sent when a reported item reaches the specified threshold. You can customize the notification under the Notifications tab.', 'directories-pro' ),
				'#horizontal' => true,
				'#states'     => $states,
			),
		);
	}

	public function onVotingEntityVoted( $entity, $fieldName, $results ) {
		if ( $fieldName !== 'voting_report'
			|| empty( $this->_config['report_enable'] )
			|| empty( $results[''] )
			|| ! is_array( $results[''] )
		) {
			return;
		}

		$results = $results[''];
		if ( empty( $results['count'] ) ) {
			return;
		}

		if ( ! empty( $this->_config['report_threshold_num'] )
			&& $results['count'] < $this->_config['report_threshold_num']
		) {
			return;
		}

		if ( ! empty( $this->_config['report_threshold_sum'] )
			&& $results['sum'] < $this->_config['report_threshold_sum']
		) {
			return;
		}

		if ( ! empty( $this->_config['report_threshold_avg'] )
			&& $results['average'] < $this->_config['report_threshold_avg']
		) {
			return;
		}

		$this->_application->Action( 'voting_report_threshold_reached', array( $entity, $results ) );
	}

	public function onVotingReportThresholdReached( $entity, $results ) {
		if ( ! empty( $this->_config['report_threshold_action'] ) ) {
			if ( $this->_application->getPlatform()->getEntityMeta( $entity->getType(), $entity->getId(), 'voting_report_threshold_reached' ) ) {
				return;
			}
			$action = $this->_config['report_threshold_action'];
			switch ( $action ) {
				case 'draft':
				case 'trash':
				case 'pending':
					if ( $entity->isPublished() ) {
						$status = $this->_application->Entity_Status( $entity->getType(), $action );
						$this->_application->Entity_Save(
							$entity,
							array(
								'status' => $status,
							)
						);
						$this->_application->getPlatform()->setEntityMeta(
							$entity->getType(),
							$entity->getId(),
							'voting_report_threshold_reached',
							array(
								'action'  => $action,
								'results' => $results,
								't'       => time(),
							)
						);
					}
					break;
				default:
			}
			if ( ! empty( $this->_config['report_reset_action'] ) ) {
				$criteria = $this->getModel()->createCriteria( 'Vote' )
					->fieldName_is( 'voting_report' )
					->entityId_is( $entity->getId() );
				$this->getModel()->getGateway( 'Vote' )->updateByCriteria( $criteria, array( 'vote_marked' => 1 ) );
				$this->_application->Voting_Votes_recalculate( $entity, 'voting_report' );
			}
		}
	}

	public function onSystemAdminSystemFormFilter( &$form ) {
		if ( $this->_config['report_enable'] ) {
			$form['#tabs']['voting_reports'] = array(
				'#title'  => __( 'Reports', 'directories-pro' ),
				'#weight' => 100,
			);
			$form['voting_reports']          = array(
				'#tab' => 'voting_reports',
			) + $this->_application->Voting_VotesByEntity( 'report' );
		}
	}

	public function csvGetExporterNames() {
		return array( 'voting_vote' );
	}

	public function csvGetExporter( $name ) {
		return new CSVExporter\VotingCSVExporter( $this->_application, $name );
	}

	public function csvGetImporterNames() {
		return array( 'voting_vote' );
	}

	public function csvGetImporter( $name ) {
		return new CSVImporter\VotingCSVImporter( $this->_application, $name );
	}
}
