text:
<?php

namespace Elkarte\Admin;

use Elkarte\Elkarte\Controller\AbstractController;
use Elkarte\Elkarte\Controller\Action;

/**
 * This class is the administration maillist controller.
 *
 *  - handles maillist configuration
 *  - handles the showing, repairing, deleting and bouncing failed emails
 *  - handles the adding / editing / removing of both filters and parsers
 *
 * @package Maillist
 */
class ManageMaillistController extends AbstractController
{
	/**
	 * Basic feature settings form
	 * @var SettingsForm
	 */
	protected $_maillistSettings;

	/**
	 * Basic filter settings form
	 * @var SettingsForm
	 */
	protected $_filtersSettings;

	/**
	 * Parsers settings form
	 * @var SettingsForm
	 */
	protected $_parsersSettings;

	/**
	 * Main dispatcher.
	 *
	 * This function checks permissions and passes control to the sub action.
	 *
	 * @see AbstractController::action_index()
	 * @uses Maillist template
	 */
	public function action_index()
	{
		global $context, $txt;

		// Template & language
		$this->_templates->load('Maillist');
		loadLanguage('Maillist');

		// All the functions available
		$subActions = array(
			'emaillist' => array($this, 'action_unapproved_email', 'permission' => 'approve_emails'),
			'approve' => array($this, 'action_approve_email', 'permission' => 'approve_emails'),
			'delete' => array($this, 'action_delete_email', 'permission' => 'approve_emails'),
			'bounce' => array($this, 'action_bounce_email', 'permission' => 'approve_emails'),
			'emailtemplates' => array($this, 'action_view_bounce_templates', 'permission' => 'approve_emails'),
			'view' => array($this, 'action_view_email', 'permission' => 'approve_emails'),
			'emailsettings' => array($this, 'action_settings', 'permission' => 'admin_forum'),
			'emailfilters' => array($this, 'action_list_filters', 'permission' => 'admin_forum'),
			'editfilter' => array($this, 'action_edit_filters', 'permission' => 'admin_forum'),
			'deletefilter' => array($this, 'action_delete_filters', 'permission' => 'admin_forum'),
			'emailparser' => array($this, 'action_list_parsers', 'permission' => 'admin_forum'),
			'editparser' => array($this, 'action_edit_parsers', 'permission' => 'admin_forum'),
			'deleteparser' => array($this, 'action_delete_parsers', 'permission' => 'admin_forum'),
			'sortparsers' => array($this, 'action_sort_parsers', 'permission' => 'admin_forum'),
			'sortfilters' => array($this, 'action_sort_filters', 'permission' => 'admin_forum'),
		);

		// Action Controller
		$action = new Action('manage_maillist');

		// Help is needed in most places, so load it up front
		require_once(SUBSDIR . '/Maillist.subs.php');

		// Create the title area for the template.
		$context[$context['admin_menu_name']]['tab_data'] = array(
			'title' => $txt['ml_admin_configuration'],
			'help' => 'maillist_help_short',
			'description' => $txt['ml_configuration_desc'],
		);

		// Default to sub action 'emaillist' if none was given, call integrate_sa_manage_maillist
		$subAction = isset($this->http_req->query->sa) && isset($subActions[$this->http_req->query->sa]) && (empty($subActions[$this->http_req->query->sa]['permission']) || allowedTo($subActions[$this->http_req->query->sa]['permission'])) ? $this->http_req->query->sa : 'emaillist';
		$subAction = $action->initialize($subActions, $subAction);

		// Final bits
		$context['page_title'] = $txt['ml_admin_configuration'];
		$context['sub_action'] = $subAction;

		// If you have the permissions, then go Play
		$action->dispatch($subAction);
	}

	/**
	 * Main listing of failed emails.
	 *
	 * What it does
	 * - shows the sender, key and subject of the email
	 * - Will show the found key if it was missing or possible sender if it was wrong
	 * - icons to view, bounce, delete or approve a failure
	 * - Accessed by ?action=Admin;area=maillist;sa=emaillist
	 *
	 * @uses showlist sub template
	 */
	public function action_unapproved_email()
	{
		global $context, $scripturl, $modSettings, $txt;

		// Set an id if none was supplied
		$id = $this->http_req->getQuery('e_id', 'intval', 0);
		if (empty($id) || $id <= 0)
			$id = 0;

		createToken('Admin-ml', 'get');

		// Build the list option array to display the email data
		$listOptions = array(
			'id' => 'view_email_errors',
			'title' => $txt['ml_emailerror'],
			'items_per_page' => $modSettings['defaultMaxMessages'],
			'no_items_label' => $txt['ml_emailerror_none'],
			'base_href' => $scripturl . '?action=Admin;area=maillist',
			'default_sort_col' => 'id_email',
			'get_items' => array(
				'function' => array($this, 'list_maillist_unapproved'),
				'params' => array(
					$id,
				),
			),
			'get_count' => array(
				'function' => 'list_maillist_count_unapproved',
			),
			'columns' => array(
				'id_email' => array(
					'header' => array(
						'value' => $txt['id'],
						'class' => 'nowrap',
					),
					'data' => array(
						'db' => 'id_email',
					),
					'sort' => array(
						'default' => 'id_email ',
						'reverse' => 'id_email DESC',
					),
				),
				'error' => array(
					'header' => array(
						'value' => $txt['error'],
					),
					'data' => array(
						'function' => function($rowData) {
							$error = $rowData['error_code'];
							if ($error === 'error_pm_not_found')
								return '' . $rowData['error'] . '';
							else
								return $rowData['error'];
						},
					),
					'sort' => array(
						'default' => 'error ',
						'reverse' => 'error DESC',
					),
				),
				'subject' => array(
					'header' => array(
						'value' => $txt['subject'],
					),
					'data' => array(
						'db' => 'subject',
					),
					'sort' => array(
						'default' => 'subject',
						'reverse' => 'subject DESC',
					),
				),
				'key' => array(
					'header' => array(
						'value' => $txt['key'],
					),
					'data' => array(
						'db' => 'key',
						'class' => 'wordbreak'
					),
					'sort' => array(
						'default' => 'message_key',
						'reverse' => 'message_key DESC',
					),
				),
				'message' => array(
					'header' => array(
						'value' => $txt['message_id'],
					),
					'data' => array(
						'sprintf' => array(
							'format' => '%2$s',
							'params' => array(
								'link' => true,
								'message' => true,
							),
						),
					),
					'sort' => array(
						'default' => 'message_id',
						'reverse' => 'message_id DESC',
					),
				),
				'from' => array(
					'header' => array(
						'value' => $txt['from'],
					),
					'data' => array(
						'db' => 'from',
					),
					'sort' => array(
						'default' => 'email_from',
						'reverse' => 'email_from DESC',
					),
				),
				'type' => array(
					'header' => array(
						'value' => $txt['message_type'],
					),
					'data' => array(
						'function' => function ($rowData) {
							global $txt;

							// Do we have a type?
							if (empty($rowData['type']))
								return $txt['not_applicable'];
							// Personal?
							elseif ($rowData['type'] === 'p')
								return $txt['personal_message'];
							// New Topic?
							elseif ($rowData['type'] === 'x')
								return $txt['new_topic'];
							// Ah a Reply then
							else
								return $txt['topic'] . ' ' . $txt['reply'];
						},
					),
					'sort' => array(
						'default' => 'message_type',
						'reverse' => 'message_type DESC',
					),
				),
				'action' => array(
					'header' => array(
						'value' => $txt['message_action'],
					),
					'data' => array(
						'function' => function($rowData) {
							global $context, $txt, $settings;

							$id = $rowData['id_email'] . ';';
							$commands = array();
							$security = $context['session_var'] . '=' . $context['session_id'] . ';' . $context['Admin-ml_token_var'] . '=' . $context['Admin-ml_token'];

							if ($rowData['error_code'] === 'error_pm_not_found')
								$commands[] = ' ';
							else
								$commands[] = ' ';

							$commands[] = '
';
							$commands[] = ' ';
							$commands[] = '';

							return implode('', $commands);
						},
					),
					'class' => 'listaction',
				),
			),
			'form' => array(
				'href' => $scripturl . '?action=Admin;area=maillist;sa=emaillist',
				'include_sort' => true,
				'include_start' => true,
			),
			'additional_rows' => array(
				array(
					'position' => 'top_of_list',
					'value' => isset($this->http_req->session->email_error) ? '
http_req->session->email_error_type) ? 'successbox' : 'errorbox') . '">' . $this->http_req->session->email_error . '
' : $txt['heading'],
				),
			),
		);

		// Clear any errors
		unset($_SESSION['email_error'], $_SESSION['email_error_type']);

		// Set the context values for the template
		$context['page_title'] = $txt['emailerror_title'];
		$context['sub_template'] = 'show_list';
		$context['default_list'] = 'view_email_errors';

		// Create the list.
		createList($listOptions);
	}

	/**
	 * Show a failed email for review by the moderation team
	 *
	 * - Will not show a PM if it has been identified as such
	 * - Accessed by ?action=Admin;area=maillist;sa=view;item=?
	 *
	 * @uses show_email sub template
	 */
	public function action_view_email()
	{
		global $txt, $context;

		allowedTo('approve_emails');
		$this->session->check('get');
		validateToken('Admin-ml', 'get');

		$id = (int) $this->http_req->query->item;
		if (!empty($id))
		{
			// Load up the email details, no funny biz ;)
			$temp_email = list_maillist_unapproved($id);

			if (!empty($temp_email))
			{
				if ($temp_email[0]['type'] !== 'p' && allowedTo('approve_emails'))
				{
					// The raw email that failed
					$data = $temp_email[0]['body'];

					// Read/parse this message for viewing
					$controller = new EmailpostController();
					$result = $controller->action_pbe_preview($data);
					$text = isset($result['body']) ? $result['body'] : '';
					$email_to = isset($result['to']) ? $result['to'] : '';
				}
				else
				{
					// PM's mean just that ...
					$text = $txt['noaccess'];
					$email_to = $txt['private'];
				}
			}
			else
				$text = $txt['badid'];
		}
		else
			$text = $txt['badid'];

		$parser = $GLOBALS['elk']['bbc'];
		// Prep and show the template with what we found
		$context['body'] = $parser->parseEmail($text);
		$context['to'] = $txt['to'] . ' ' . (isset($email_to) ? $email_to : '');
		$context['notice_subject'] = isset($temp_email[0]['subject']) ? $txt['subject'] . ': ' . $temp_email[0]['subject'] : '';
		$context['notice_from'] = isset($temp_email[0]['from']) ? $txt['from'] . ': ' . $temp_email[0]['from'] : '';
		$context['page_title'] = $txt['show_notice'];
		$context['error_code'] = isset($temp_email[0]['error_code']) && isset($txt[$temp_email[0]['error_code']]) ? $txt[$temp_email[0]['error_code']] : '';
		$context['sub_template'] = 'show_email';
	}

	/**
	 * Deletes an entry from the database
	 *
	 * - Flushes the moderator menu todo numbers so the menu numbers update
	 * - Accessed by ?action=Admin;area=maillist;sa=delete;item=?'
	 * - Redirects to ?action=Admin;area=maillist;sa=emaillist
	 */
	public function action_delete_email()
	{
		allowedTo('approve_emails');
		$this->session->check('get');
		validateToken('Admin-ml', 'get');

		$id = (int) $this->http_req->query->item;

		// Remove this entry
		if (!empty($id))
			maillist_delete_error_entry($id);

		// Flush the cache
		$GLOBALS['elk']['cache']->remove('num_menu_errors');

		// Back to the failed list we go
		redirectexit('action=Admin;area=maillist;sa=emaillist');
	}

	/**
	 * Attempts to approve and post a failed email
	 *
	 * - Reviews the data to see if the email error function fixed typical issues like key and wrong id
	 * - Submits the fixed email to the main function which will post it or fail it again
	 * - If successful will remove the entry from the failed log
	 * - Accessed by ?action=Admin;area=maillist;sa=approve;item=?'
	 * - Redirects to action=Admin;area=maillist;sa=emaillist
	 */
	public function action_approve_email()
	{
		global $txt;

		allowedTo('approve_emails');
		$this->session->check('get');
		validateToken('Admin-ml', 'get');

		// Get the id to approve
		$id = (int) $this->http_req->query->item;

		if (!empty($id) && $id !== -1)
		{
			// Load up the email data
			$temp_email = list_maillist_unapproved($id);
			if (!empty($temp_email))
			{
				// Do we have the needed data to approve this, after all it failed for a reason yes?
				if (!empty($temp_email[0]['key']) && (!in_array($temp_email[0]['error_code'], array('error_no_message', 'error_not_find_board', 'error_topic_gone'))))
				{
					// Set up the details needed to get this posted
					$force = true;
					$key = $temp_email[0]['key'];
					$data = $temp_email[0]['body'];

					// Unknown from email?  Update the message ONLY if we found an appropriate one during the error checking process
					if (in_array($temp_email[0]['error_code'], array('error_not_find_member', 'error_key_sender_match')))
					{
						// did we actually find a potential correct name, if so we post from the valid member
						$check_emails = array_pad(explode('=>', $temp_email[0]['from']), 2, '');

						if (!empty($check_emails[1]))
							$data = preg_replace('~(From: )(.*<)?(' . preg_quote(trim($check_emails[0])) . ')(>)?(\n)~i', '$1$2' . trim($check_emails[1]) . '$4$5', $data);
					}

					// Lets TRY AGAIN to make a post!
					include_once(CONTROLLERDIR . '/Emailpost.controller.php');
					$controller = new EmailpostController();
					$text = $controller->action_pbe_post($data, $force, $key);

					// Assuming all went well, remove this entry and file since we are done.
					if ($text === true)
					{
						maillist_delete_error_entry($id);

						// Flush the menu count cache
						$GLOBALS['elk']['cache']->remove('num_menu_errors');

						$_SESSION['email_error'] = $txt['approved'];
						$_SESSION['email_error_type'] = 1;
					}
					else
						$_SESSION['email_error'] = $txt['error_approved'];
				}
				else
					$_SESSION['email_error'] = $txt['cant_approve'];
			}
			else
				$_SESSION['email_error'] = $txt['badid'];
		}
		else
			$_SESSION['email_error'] = $txt['badid'];

		// back to the list we go
		redirectexit('action=Admin;area=maillist;sa=emaillist');
	}

	/**
	 * Allows the Admin to choose from predefined and custom templates
	 *
	 * - Uses the selected template to send a bounce notification with
	 * details as specified by the template
	 * - Accessed by ?action=Admin;area=maillist;sa=bounce;item=?'
	 * - Redirects to action=Admin;area=maillist;sa=bounced
	 *
	 * @uses bounce_email sub-template
	 */
	public function action_bounce_email()
	{
		global $context, $txt, $modSettings, $scripturl, $mbname;

		if (!isset($this->http_req->query->bounce))
		{
			$this->session->check('get');
			validateToken('Admin-ml', 'get');
		}

		// We should have been sent an email ID
		if (isset($this->http_req->query->item))
		{
			// Needs to be an int!
			$id = (int) $this->http_req->query->item;

			// Load up the email details, no funny biz yall ;)
			$temp_email = list_maillist_unapproved($id);

			if (!empty($temp_email))
			{
				// Set the options
				$this->http_req->post->item = (int) $temp_email[0]['id_email'];
				$fullerrortext = $txt[$temp_email[0]['error_code']];

				// Build the template selection area, first the standard ones
				$bounce = array('bounce', 'inform');
				foreach ($bounce as $k => $type)
				{
					$context['bounce_templates'][$k]['body'] = $txt['ml_' . $type . '_body'];
					$context['bounce_templates'][$k]['subject'] = $txt['ml_' . $type . '_subject'];
					$context['bounce_templates'][$k]['title'] = $txt['ml_' . $type . '_title'];
				}

				// And now any custom ones available for this moderator
				$context['bounce_templates'] += array_merge($context['bounce_templates'], maillist_templates('bnctpl', $txt['ml_bounce_template_subject_default']));

				// Replace all the variables in the templates
				foreach ($context['bounce_templates'] as $k => $name)
				{
					$context['bounce_templates'][$k]['body'] = strtr($name['body'], array(
						'{MEMBER}' => un_htmlspecialchars($temp_email[0]['name']),
						'{SCRIPTURL}' => $scripturl,
						'{FORUMNAME}' => $mbname,
						'{REGARDS}' => replaceBasicActionUrl($txt['regards_team']),
						'{SUBJECT}' => $temp_email[0]['subject'],
						'{ERROR}' => $fullerrortext,
						'{FORUMNAMESHORT}' => (!empty($modSettings['maillist_sitename']) ? $modSettings['maillist_sitename'] : $mbname),
						'{EMAILREGARDS}' => (!empty($modSettings['maillist_sitename_regards']) ? $modSettings['maillist_sitename_regards'] : ''),
					));
				}
			}
			else
				$context['settings_message'] = $txt['badid'];
		}
		else
			$context['settings_message'] = $txt['badid'];

		// Check if they are sending the notice
		if (isset($this->http_req->query->bounce) && isset($temp_email))
		{
			$this->session->check('post');
			validateToken('Admin-ml');

			// They did check the box, how else could they have posted
			if (isset($this->http_req->post->warn_notify))
			{
				// lets make sure we have the items to send it
				$check_emails = explode('=>', $temp_email[0]['from']);
				$to = trim($check_emails[0]);
				$subject = trim($this->http_req->post->warn_sub);
				$body = trim($this->http_req->post->warn_body);

				if (empty($body) || empty($subject))
					$context['settings_message'] = $txt['bad_bounce'];
				else
				{
					// Time for someone to get a we're so sorry message!
					sendmail($to, $subject, $body, null, null, false, 5);
					redirectexit('action=Admin;area=maillist;bounced');
				}
			}
		}

		// Prepare and show the template
		createToken('Admin-ml');
		$context['warning_data'] = array('notify' => '', 'notify_subject' => '', 'notify_body' => '');
		$context['body'] = isset($fullerrortext) ? $GLOBALS['elk']['bbc']->parseEmail($fullerrortext) : '';
		$context['item'] = isset($this->http_req->post->item) ? $this->http_req->post->item : '';
		$context['notice_to'] = $txt['to'] . ' ' . isset($temp_email[0]['from']) ? $temp_email[0]['from'] : '';
		$context['page_title'] = $txt['bounce_title'];
		$context['sub_template'] = 'bounce_email';
	}

	/**
	 * List all the filters in the system
	 *
	 * - Allows to add/edit or delete filters
	 * - Filters are used to alter text in a post, to remove crud that comes with emails
	 * - Filters can be defined as regex, the system will check it for valid syntax
	 * - Accessed by ?action=Admin;area=maillist;sa=emailfilters;
	 */
	public function action_list_filters()
	{
		global $context, $scripturl, $txt, $settings, $modSettings;

		$id = 0;

		// Build the listoption array to display the filters
		$listOptions = array(
			'id' => 'email_filter',
			'title' => $txt['filters'],
			'items_per_page' => $modSettings['defaultMaxMessages'],
			'no_items_label' => $txt['no_filters'],
			'base_href' => $scripturl . '?action=Admin;area=maillist;sa=emailfilters',
			'default_sort_col' => 'name',
			'get_items' => array(
				'function' => array($this, 'load_filter_parser'),
				'params' => array(
					$id,
					'filter'
				),
			),
			'get_count' => array(
				'function' => array($this, 'count_filter_parser'),
				'params' => array(
					$id,
					'filter'
				),
			),
			'columns' => array(
				'name' => array(
					'header' => array(
						'value' => $txt['filter_name'],
						'style' => 'white-space: nowrap;'
					),
					'data' => array(
						'db' => 'filter_name',
					),
					'sort' => array(
						'default' => 'filter_name, id_filter',
						'reverse' => 'filter_name DESC, id_filter DESC',
					),
				),
				'from' => array(
					'header' => array(
						'value' => $txt['filter_from'],
					),
					'data' => array(
						'db' => 'filter_from',
					),
					'sort' => array(
						'default' => 'filter_from, id_filter',
						'reverse' => 'filter_from DESC, id_filter DESC',
					),
				),
				'to' => array(
					'header' => array(
						'value' => $txt['filter_to'],
						'style' => 'width:10em;',
					),
					'data' => array(
						'db' => 'filter_to',
					),
					'sort' => array(
						'default' => 'filter_to, id_filter',
						'reverse' => 'filter_to DESC, id_filter DESC',
					),
				),
				'type' => array(
					'header' => array(
						'value' => $txt['filter_type'],
					),
					'data' => array(
						'db' => 'filter_type',
					),
					'sort' => array(
						'default' => 'filter_type, id_filter',
						'reverse' => 'filter_type DESC, id_filter DESC',
					),
				),
				'action' => array(
					'header' => array(
						'value' => $txt['message_action'],
						'class' => 'centertext',
					),
					'data' => array(
						'sprintf' => array(
							'format' => '

									',
							'params' => array(
								'id_filter' => true,
							),
						),
						'class' => 'centertext',
						'style' => 'white-space:nowrap;',
					),
				),
			),
			'form' => array(
				'href' => $scripturl . '?action=Admin;area=maillist;sa=editfilter;',
				'include_sort' => true,
				'include_start' => true,
				'hidden_fields' => array(
					$context['session_var'] => $context['session_id'],
				),
			),
			'additional_rows' => array(
				array(
					'position' => isset($this->http_req->query->saved) ? 'top_of_list' : 'after_title',
					'value' => isset($this->http_req->query->saved) ? '
' . $txt['saved'] . '
' : $txt['filters_title'],
				),
				array(
					'position' => 'below_table_data',
					'class' => 'submitbutton',
					'value' => '<input type="submit" name="addfilter" value="' . $txt['add_filter'] . '" />
						' . $txt['sort_filter'] . '',
				),
			),
		);

		// Set the context values
		$context['page_title'] = $txt['filters'];
		$context['sub_template'] = 'show_list';
		$context['default_list'] = 'email_filter';

		// Create the list.
		createList($listOptions);
	}

	/**
	 * Show a full list of all the filters in the system for drag/drop sorting
	 */
	public function action_sort_filters()
	{
		global $context, $scripturl, $txt;

		$id = 0;
		$token = createToken('Admin-sort');

		// build the listoption array to display the data
		$listOptions = array(
			'id' => 'sort_email_fp',
			'title' => $txt['sort_filter'],
			'sortable' => true,
			'items_per_page' => 0,
			'no_items_label' => $txt['no_filters'],
			'base_href' => $scripturl . '?action=Admin;area=maillist;sa=sortfilters',
			'get_items' => array(
				'function' => array($this, 'load_filter_parser'),
				'params' => array(
					$id,
					'filter'
				),
			),
			'get_count' => array(
				'function' => array($this, 'count_filter_parser'),
				'params' => array(
					$id,
					'filter'
				),
			),
			'columns' => array(
				'filterorder' => array(
					'header' => array(
						'value' => '',
						'class' => 'hide',
					),
					'data' => array(
						'db' => 'filter_order',
						'class' => 'hide',
					),
				),
				'name' => array(
					'header' => array(
						'value' => $txt['filter_name'],
						'style' => 'white-space: nowrap;width: 10em'
					),
					'data' => array(
						'db' => 'filter_name',
					),
				),
				'from' => array(
					'header' => array(
						'value' => $txt['filter_from'],
					),
					'data' => array(
						'db' => 'filter_from',
					),
				),
				'to' => array(
					'header' => array(
						'value' => $txt['filter_to'],
						'style' => 'width:10em;',
					),
					'data' => array(
						'db' => 'filter_to',
					),
				),
				'type' => array(
					'header' => array(
						'value' => $txt['filter_type'],
					),
					'data' => array(
						'db' => 'filter_type',
					),
				),
			),
			'form' => array(
				'href' => $scripturl . '?action=Admin;area=maillist;sa=sortfilters',
				'hidden_fields' => array(
					$context['session_var'] => $context['session_id'],
				),
			),
			'additional_rows' => array(
				array(
					'position' => 'after_title',
					'value' => $txt['filter_sort_description'],
				),
			),
			'javascript' => '
				$().elkSortable({
					sa: "parserorder",
					placeholder: "ui-state-highlight",
					containment: "#sort_email_fp",
					error: "' . $txt['admin_order_error'] . '",
					title: "' . $txt['admin_order_title'] . '",
					href: "?action=Admin;area=maillist;sa=sortfilters",
					token: {token_var: "' . $token['Admin-sort_token_var'] . '", token_id: "' . $token['Admin-sort_token'] . '"}
				});
			',
		);

		// Set the context values
		$context['page_title'] = $txt['filters'];
		$context['sub_template'] = 'show_list';
		$context['default_list'] = 'sort_email_fp';
		$context[$context['admin_menu_name']]['current_subsection'] = 'emailfilters';

		// Create the list.
		createList($listOptions);
	}

	/**
	 * Returns the number of filters or parsers in the system
	 *
	 * - Callback for createList()
	 *
	 * @param int $id 0 for all of a certain style
	 * @param string $style one of filter or parser
	 */
	public function count_filter_parser($id, $style)
	{
		return list_count_filter_parser($id, $style);
	}

	/**
	 * Returns the details for the filters or parsers in the system
	 *
	 * - Callback for createList()
	 *
	 * @param int $start The item to start with (for pagination purposes)
	 * @param int $items_per_page The number of items to show per page
	 * @param string $sort A string indicating how to sort the results
	 * @param int $id
	 * @param string $style
	 */
	public function load_filter_parser($start, $items_per_page, $sort, $id, $style)
	{
		return list_get_filter_parser($start, $items_per_page, $sort, $id, $style);
	}

	/**
	 * Edit or Add a filter
	 *
	 * - If regex will check for proper syntax before saving to the database
	 */
	public function action_edit_filters()
	{
		global $context, $scripturl, $txt, $modSettings;

		// Editing an existing filter?
		if (isset($this->http_req->query->f_id))
		{
			// Needs to be an int!
			$id = (int) $this->http_req->query->f_id;
			if (empty($id) || $id <= 0)
				$GLOBALS['elk']['errors']->fatal_lang_error('error_no_id_filter');

			// Load it up and set it as the current values
			$row = maillist_load_filter_parser($id, 'filter');
			$modSettings['id_filter'] = $row['id_filter'];
			$modSettings['filter_type'] = $row['filter_type'];
			$modSettings['filter_to'] = $row['filter_to'];
			$modSettings['filter_from'] = $row['filter_from'];
			$modSettings['filter_name'] = $row['filter_name'];

			// Some items for the form
			$context['page_title'] = $txt['edit_filter'];
			$context['editing'] = true;
			$context['settings_message'] = array();
		}
		else
		{
			// Setup place holders for adding a new one instead
			$modSettings['filter_type'] = '';
			$modSettings['filter_to'] = '';
			$modSettings['filter_from'] = '';
			$modSettings['filter_name'] = '';

			$context['page_title'] = $txt['add_filter'];
			$context['editing'] = false;
			$context['settings_message'] = array();
		}

		// Initialize the filer settings form
		$this->_initFiltersSettingsForm();

		// Initialize it with our settings
		$config_vars = $this->_filtersSettings->settings();

		// Saving the new or edited entry?
		if (isset($this->http_req->query->save))
		{
			$this->session->check();

			$GLOBALS['elk']['hooks']->hook('save_filter_settings');

			// Editing an entry?
			$editid = (isset($this->http_req->query->edit)) ? (int) $this->http_req->query->edit : -1;
			$editname = (isset($this->http_req->query->edit)) ? 'id_filter' : '';

			// If its regex we do a quick check to see if its valid or not
			if ($this->http_req->post->filter_type === 'regex')
			{
				$valid = (@preg_replace($this->http_req->post->filter_from, $this->http_req->post->filter_to, 'ElkArte') === null) ? false : true;
				if (!$valid)
				{
					// Seems to be bad ... reload the form, set the message
					$context['error_type'] = 'notice';
					$context['settings_message'][] = $txt['regex_invalid'];
					$modSettings['filter_type'] = $this->http_req->post->filter_type;
					$modSettings['filter_to'] = $this->http_req->post->filter_to;
					$modSettings['filter_from'] = $this->http_req->post->filter_from;
					$modSettings['filter_name'] = $this->http_req->post->filter_name;
				}
			}

			if (empty($this->http_req->post->filter_type) || empty($this->http_req->post->filter_from))
			{
				$context['error_type'] = 'notice';
				$context['settings_message'][] = $txt['filter_invalid'];
			}

			// if we are good to save, so save it ;)
			if (empty($context['settings_message']))
			{
				// And ... its a filter
				$config_vars[] = array('text', 'filter_style');
				$this->http_req->post->filter_style = 'filter';

				Email_Settings::saveTableSettings($config_vars, 'postby_emails_filters', $this->http_req->post, array(), $editid, $editname);
				writeLog();
				redirectexit('action=Admin;area=maillist;sa=emailfilters;saved');
			}
		}

		// Prepare some final context for the template
		$title = !empty($this->http_req->query->saved) ? 'saved_filter' : ($context['editing'] == true ? 'edit_filter' : 'add_filter');
		$context['post_url'] = $scripturl . '?action=Admin;area=maillist;sa=editfilter' . ($context['editing'] ? ';edit=' . $modSettings['id_filter'] : ';new') . ';save';
		$context['settings_title'] = $txt[$title];
		$context['breadcrumbs'][] = array(
			'url' => $scripturl . '?action=Admin;area=maillist;sa=editfilter',
			'name' => ($context['editing']) ? $txt['edit_filter'] : $txt['add_filter'],
		);
		$context[$context['admin_menu_name']]['tab_data'] = array(
			'title' => $txt[$title],
			'description' => $txt['filters_title'],
		);
		$context[$context['admin_menu_name']]['current_subsection'] = 'emailfilters';

		// Load and show
		Email_Settings::prepare_db($config_vars);
		$this->_templates->load('Admin');
		loadCSSFile('Admin.css');
		$context['sub_template'] = 'show_settings';
	}

	/**
	 * Initialize Mailist settings form.
	 */
	protected function _initFiltersSettingsForm()
	{
		global $txt;

		// Instantiate the extended parser form
		$this->_filtersSettings = new Email_Settings();

		// Set up the config_vars for the form
		$config_vars = array(
			array('text', 'filter_name', 25, 'subtext' => $txt['filter_name_desc']),
			array('select', 'filter_type',
				array(
					'standard' => $txt['option_standard'],
					'regex' => $txt['option_regex'],
				),
			),
			array('large_text', 'filter_from', 4, 'subtext' => $txt['filter_from_desc']),
			array('text', 'filter_to', 25, 'subtext' => $txt['filter_to_desc']),
		);

		$GLOBALS['elk']['hooks']->hook('modify_maillist_filter_settings', array(&$config_vars));

		return $this->_filtersSettings->settings($config_vars);
	}

	/**
	 * Deletes a filter from the system / database
	 */
	public function action_delete_filters()
	{
		// Removing the filter?
		if (isset($this->http_req->query->f_id))
		{
			$this->session->check('get');
			$id = (int) $this->http_req->query->f_id;

			maillist_delete_filter_parser($id);
			redirectexit('action=Admin;area=maillist;sa=emailfilters;deleted');
		}
	}

	/**
	 * Show a list of all the parsers in the system
	 *
	 * - Allows to add/edit or delete parsers
	 * - Parsers are used to split a message at a line of text
	 * - Parsers can only be defined as regex, the system will check it for valid syntax
	 * - Accessed by ?action=Admin;area=maillist;sa=emailparser;
	 */
	public function action_list_parsers()
	{
		global $context, $scripturl, $txt, $settings, $modSettings;

		$id = 0;

		// Build the listoption array to display the data
		$listOptions = array(
			'id' => 'email_parser',
			'title' => $txt['parsers'],
			'items_per_page' => $modSettings['defaultMaxMessages'],
			'no_items_label' => $txt['no_parsers'],
			'base_href' => $scripturl . '?action=Admin;area=maillist;sa=emailparser',
			'get_items' => array(
				'function' => array($this, 'load_filter_parser'),
				'params' => array(
					$id,
					'parser'
				),
			),
			'get_count' => array(
				'function' => array($this, 'count_filter_parser'),
				'params' => array(
					$id,
					'parser'
				),
			),
			'columns' => array(
				'name' => array(
					'header' => array(
						'value' => $txt['parser_name'],
						'style' => 'white-space: nowrap;'
					),
					'data' => array(
						'db' => 'filter_name',
					),
					'sort' => array(
						'default' => 'filter_name',
						'reverse' => 'filter_name DESC',
					),
				),
				'from' => array(
					'header' => array(
						'value' => $txt['parser_from'],
					),
					'data' => array(
						'db' => 'filter_from',
					),
					'sort' => array(
						'default' => 'filter_from',
						'reverse' => 'filter_from DESC',
					),
				),
				'type' => array(
					'header' => array(
						'value' => $txt['parser_type'],
					),
					'data' => array(
						'db' => 'filter_type',
					),
					'sort' => array(
						'default' => 'filter_type',
						'reverse' => 'filter_type DESC',
					),
				),
				'action' => array(
					'header' => array(
						'value' => $txt['message_action'],
						'class' => 'centertext',
					),
					'data' => array(
						'sprintf' => array(
							'format' => '

									',
							'params' => array(
								'id_filter' => true,
							),
						),
						'class' => 'centertext',
						'style' => 'white-space:nowrap;',
					),
				),
			),
			'form' => array(
				'href' => $scripturl . '?action=Admin;area=maillist;sa=editparser',
				'include_sort' => true,
				'include_start' => true,
				'hidden_fields' => array(
					$context['session_var'] => $context['session_id'],
				),
			),
			'additional_rows' => array(
				array(
					'position' => isset($this->http_req->query->saved) ? 'top_of_list' : 'after_title',
					'value' => isset($this->http_req->query->saved) ? '
' . $txt['saved'] . '
' : $txt['parsers_title'],
				),
				array(
					'position' => 'below_table_data',
					'class' => 'submitbutton',
					'value' => '
						<input type="submit" name="addparser" value="' . $txt['add_parser'] . '" class="right_submit" />
						' . $txt['sort_parser'] . '',
					),
			),
		);

		// Set the context values
		$context['page_title'] = $txt['parsers'];
		$context['sub_template'] = 'show_list';
		$context['default_list'] = 'email_parser';

		// Create the list.
		createList($listOptions);
	}

	/**
	 * Show a full list of all the parsers in the system for drag/drop sorting
	 */
	public function action_sort_parsers()
	{
		global $context, $scripturl, $txt;

		$id = 0;
		$token = createToken('Admin-sort');

		// Build the listoption array to display the data
		$listOptions = array(
			'id' => 'sort_email_fp',
			'title' => $txt['sort_parser'],
			'sortable' => true,
			'items_per_page' => 0,
			'no_items_label' => $txt['no_parsers'],
			'base_href' => $scripturl . '?action=Admin;area=maillist;sa=sortparsers',
			'default_sort_col' => 'filterorder',
			'get_items' => array(
				'function' => array($this, 'load_filter_parser'),
				'params' => array(
					$id,
					'parser'
				),
			),
			'get_count' => array(
				'function' => array($this, 'count_filter_parser'),
				'params' => array(
					$id,
					'parser'
				),
			),
			'columns' => array(
				'filterorder' => array(
					'header' => array(
						'value' => '',
						'class' => 'hide',
					),
					'data' => array(
						'db' => 'filter_order',
						'class' => 'hide',
					),
				),
				'name' => array(
					'header' => array(
						'value' => $txt['parser_name'],
						'style' => 'white-space: nowrap;width: 10em'
					),
					'data' => array(
						'db' => 'filter_name',
					),
				),
				'from' => array(
					'header' => array(
						'value' => $txt['parser_from'],
					),
					'data' => array(
						'db' => 'filter_from',
					),
				),
				'type' => array(
					'header' => array(
						'value' => $txt['parser_type'],
					),
					'data' => array(
						'db' => 'filter_type',
					),
				),
			),
			'form' => array(
				'href' => $scripturl . '?action=Admin;area=maillist;sa=sortparsers',
				'hidden_fields' => array(
					$context['session_var'] => $context['session_id'],
				),
			),
			'additional_rows' => array(
				array(
					'position' => 'after_title',
					'value' => $txt['parser_sort_description'],
				),
			),
			'javascript' => '
				$().elkSortable({
					sa: "parserorder",
					placeholder: "ui-state-highlight",
					containment: "#sort_email_fp",
					error: "' . $txt['admin_order_error'] . '",
					title: "' . $txt['admin_order_title'] . '",
					href: "?action=Admin;;area=maillist;sa=sortparsers",
					token: {token_var: "' . $token['Admin-sort_token_var'] . '", token_id: "' . $token['Admin-sort_token'] . '"}
				});
			',
		);

		// Set the context values
		$context['page_title'] = $txt['parsers'];
		$context['sub_template'] = 'show_list';
		$context['default_list'] = 'sort_email_fp';
		$context[$context['admin_menu_name']]['current_subsection'] = 'emailparser';

		// Create the list.
		createList($listOptions);
	}

	/**
	 * Adds or Edits an existing parser
	 *
	 * - All parsers are assumed regex
	 */
	public function action_edit_parsers()
	{
		global $context, $scripturl, $txt, $modSettings;

		// Editing an existing filter?
		if (isset($this->http_req->query->f_id))
		{
			// Needs to be an int!
			$id = (int) $this->http_req->query->f_id;
			if (empty($id) || $id < 0)
				$GLOBALS['elk']['errors']->fatal_lang_error('error_no_id_filter');

			// Load this filter so we can edit it
			$row = maillist_load_filter_parser($id, 'parser');

			$modSettings['id_filter'] = $row['id_filter'];
			$modSettings['filter_type'] = $row['filter_type'];
			$modSettings['filter_from'] = $row['filter_from'];
			$modSettings['filter_name'] = $row['filter_name'];

			$context['page_title'] = $txt['edit_parser'];
			$context['editing'] = true;
		}
		else
		{
			// Setup place holders for adding a new one instead
			$modSettings['filter_type'] = '';
			$modSettings['filter_name'] = '';
			$modSettings['filter_from'] = '';

			// To the template we go
			$context['page_title'] = $txt['add_parser'];
			$context['editing'] = false;
		}

		// Initialize the mailparser settings form
		$this->_initParsersSettingsForm();

		// Initialize it with our settings
		$config_vars = $this->_parsersSettings->settings();

		// Check if they are saving the changes
		if (isset($this->http_req->query->save))
		{
			$this->session->check();

			$GLOBALS['elk']['hooks']->hook('save_parser_settings');

			// Editing a parser?
			$editid = isset($this->http_req->query->edit) ? (int) $this->http_req->query->edit : -1;
			$editname = isset($this->http_req->query->edit) ? 'id_filter' : '';

			// Test the regex
			if ($this->http_req->post->filter_type === 'regex' && !empty($this->http_req->post->filter_from))
			{
				$valid = (preg_replace($this->http_req->post->filter_from, '', 'ElkArte') === null) ? false : true;
				if (!$valid)
				{
					// Regex did not compute .. Danger, Will Robinson
					$context['settings_message'] = $txt['regex_invalid'];
					$context['error_type'] = 'notice';

					$modSettings['filter_type'] = $this->http_req->post->filter_type;
					$modSettings['filter_from'] = $this->http_req->post->filter_from;
					$modSettings['filter_name'] = $this->http_req->post->filter_name;
				}
			}

			if (empty($this->http_req->post->filter_type) || empty($this->http_req->post->filter_from))
			{
				$context['error_type'] = 'notice';
				$context['settings_message'][] = $txt['filter_invalid'];
			}

			// All clear to save?
			if (empty($context['settings_message']))
			{
				// Shhh ... its really a parser
				$config_vars[] = array('text', 'filter_style');
				$this->http_req->post->filter_style = 'parser';

				// Save, log, show
				Email_Settings::saveTableSettings($config_vars, 'postby_emails_filters', $this->http_req->post, array(), $editid, $editname);
				writeLog();
				redirectexit('action=Admin;area=maillist;sa=emailparser;saved');
			}
		}

		// Prepare the context for viewing
		$title = ((isset($this->http_req->query->saved) && $this->http_req->query->saved == '1') ? 'saved_parser' : ($context['editing'] == true ? 'edit_parser' : 'add_parser'));
		$context['settings_title'] = $txt[$title];
		$context['post_url'] = $scripturl . '?action=Admin;area=maillist;sa=editparser' . ($context['editing'] ? ';edit=' . $modSettings['id_filter'] : ';new') . ';save';
		$context['breadcrumbs'][] = array(
			'url' => $scripturl . '?action=Admin;area=maillist;sa=editparser',
			'name' => ($context['editing']) ? $txt['edit_parser'] : $txt['add_parser'],
		);
		$context[$context['admin_menu_name']]['tab_data'] = array(
			'title' => $txt[$title],
			'description' => $txt['parsers_title'],
		);
		$context[$context['admin_menu_name']]['current_subsection'] = 'emailparser';

		// prep it, load it, show it
		Email_Settings::prepare_db($config_vars);
		$this->_templates->load('Admin');
		loadCSSFile('Admin.css');
		$context['sub_template'] = 'show_settings';
	}

	/**
	 * Initialize Mailist settings form.
	 */
	protected function _initParsersSettingsForm()
	{
		global $txt;

		// Instantiate the extended parser form
		$this->_parsersSettings = new Email_Settings();

		// Define the menu array
		$config_vars = array(
			array('text', 'filter_name', 25, 'subtext' => $txt['parser_name_desc']),
			array('select', 'filter_type', 'subtext' => $txt['parser_type_desc'],
				array(
					'regex' => $txt['option_regex'],
					'standard' => $txt['option_standard'],
				),
			),
			array('large_text', 'filter_from', 4, 'subtext' => $txt['parser_from_desc']),
		);

		$GLOBALS['elk']['hooks']->hook('modify_maillist_parser_settings', array(&$config_vars));

		return $this->_parsersSettings->settings($config_vars);
	}

	/**
	 * Removes a parser from the system and database
	 */
	public function action_delete_parsers()
	{
		// Removing the filter?
		if (isset($this->http_req->query->f_id))
		{
			$this->session->check('get');
			$id = (int) $this->http_req->query->f_id;

			maillist_delete_filter_parser($id);
			redirectexit('action=Admin;area=maillist;sa=emailparser;deleted');
		}
	}

	/**
	 * All the post by email settings, used to control how the feature works
	 *
	 * @uses Admin language
	 */
	public function action_settings()
	{
		global $scripturl, $context, $txt, $modSettings;

		// Be nice, show them we did something
		if (isset($this->http_req->query->saved))
			$context['settings_message'] = $txt['saved'];

		// Templates and language
		loadLanguage('Admin');
		$this->_templates->load('Admin');
		loadCSSFile('Admin.css');

		// Load any existing email => board values used for new topic creation
		$context['maillist_from_to_board'] = array();
		$data = (!empty($modSettings['maillist_receiving_address'])) ? unserialize($modSettings['maillist_receiving_address']) : array();
		foreach ($data as $key => $addr)
		{
			$context['maillist_from_to_board'][$key] = array(
				'id' => $key,
				'emailfrom' => $addr[0],
				'boardto' => $addr[1],
			);
		}

		// Initialize the maillist settings form
		$this->_initMaillistSettingsForm();

		// Retrieve the config settings
		$config_vars = $this->_maillistSettings->settings();

		// Saving settings?
		if (isset($this->http_req->query->save))
		{
			$this->session->check();

			$GLOBALS['elk']['hooks']->hook('save_maillist_settings');

			$email_error = false;
			$board_error = false;
			$maillist_receiving_address = array();

			// Basic checking of the email addresses
			if (!DataValidator::is_valid($this->http_req->post, array('maillist_sitename_address' => 'valid_email'), array('maillist_sitename_address' => 'trim')))
				$email_error = $this->http_req->post->maillist_sitename_address;
			if (!DataValidator::is_valid($this->http_req->post, array('maillist_sitename_help' => 'valid_email'), array('maillist_sitename_help' => 'trim')))
				$email_error = $this->http_req->post->maillist_sitename_help;
			if (!DataValidator::is_valid($this->http_req->post, array('maillist_mail_from' => 'valid_email'), array('maillist_mail_from' => 'trim')))
				$email_error = $this->http_req->post->maillist_mail_from;

			// Inbound email set up then we need to check for both valid email and valid board
			if (!$email_error && !empty($this->http_req->post->emailfrom))
			{
				// Get the board ids for a quick check
				$boards = maillist_board_list();

				// Check the receiving emails and the board id as well
				$boardtocheck = !empty($this->http_req->post->boardto) ? $this->http_req->post->boardto : array();
				$addresstocheck = !empty($this->http_req->post->emailfrom) ? $this->http_req->post->emailfrom : array();

				foreach ($addresstocheck as $key => $checkme)
				{
					// Valid email syntax
					if (!DataValidator::is_valid($addresstocheck, array($key => 'valid_email'), array($key => 'trim')))
					{
						$email_error = $checkme;
						$context['error_type'] = 'notice';
						continue;
					}

					// Valid board id?
					if (!isset($boardtocheck[$key]) || !isset($boards[$key]))
					{
						$board_error = $checkme;
						$context['error_type'] = 'notice';
						continue;
					}

					// Decipher as [0] emailaddress and [1] board id
					$maillist_receiving_address[] = array($checkme, $boardtocheck[$key]);
				}
			}

			// Enable or disable the fake cron
			enable_maillist_imap_cron(!empty($this->http_req->post->maillist_imap_cron));

			// Check and set any errors or give the go ahead to save
			if ($email_error)
				$context['settings_message'] = sprintf($txt['email_not_valid'], $email_error);
			elseif ($board_error)
				$context['settings_message'] = sprintf($txt['board_not_valid'], $board_error);
			else
			{
				// Clear the moderation count cache
				$GLOBALS['elk']['cache']->remove('num_menu_errors');

				// Should be off if mail posting is on, we ignore it anyway but this at least updates the ACP
				if (!empty($this->http_req->post->maillist_enabled))
					updateSettings(array('disallow_sendBody' => ''));

				updateSettings(array('maillist_receiving_address' => serialize($maillist_receiving_address)));
				SettingsForm::save_db($config_vars, $this->http_req->post);
				writeLog();
				redirectexit('action=Admin;area=maillist;sa=emailsettings;saved');
			}
		}

		// Javascript vars for the "add more" buttons in the receive_email callback
		$board_list = maillist_board_list();
		$script = '';
		$i = 0;

		// Create the board selection list
		foreach ($board_list as $board_id => $board_name)
			$script .= $i++ . ': {id:' . $board_id . ', name:' . JavaScriptEscape($board_name) . '},';

		theme()->addInlineJavascript('
		var sEmailParent = \'add_more_email_placeholder\',
			oEmailOptionsdt = {size: \'50\', name: \'emailfrom[]\', class: \'input_text\'},
			oEmailOptionsdd = {size: \'1\', type: \'select\', name: \'boardto[]\', class: \'input_select\'},
			oEmailSelectData = {' . $script . '};

			document.getElementById(\'add_more_board_div\').style.display = \'block\';', true
		);

		$context['boards'] = $board_list;
		$context['settings_title'] = $txt['ml_emailsettings'];
		$context['page_title'] = $txt['ml_emailsettings'];
		$context['post_url'] = $scripturl . '?action=Admin;area=maillist;sa=emailsettings;save';
		$context['sub_template'] = 'show_settings';
		SettingsForm::prepare_db($config_vars);
	}

	/**
	 * Initialize Maillist settings form.
	 */
	protected function _initMaillistSettingsForm()
	{
		// Instantiate the form
		$this->_maillistSettings = new SettingsForm();

		// Initialize it with our settings
		$config_vars = $this->_settings();

		return $this->_maillistSettings->settings($config_vars);
	}

	/**
	 * Load up the config var array for settings display etc.
	 */
	protected function _settings()
	{
		global $txt;

		// Define the menu
		$config_vars = array(
				array('desc', 'maillist_help'),
				array('check', 'maillist_enabled'),
				array('check', 'pbe_post_enabled'),
				array('check', 'pbe_pm_enabled'),
				array('check', 'pbe_no_mod_notices', 'subtext' => $txt['pbe_no_mod_notices_desc'], 'postinput' => $txt['recommended']),
				array('check', 'pbe_bounce_detect', 'subtext' => $txt['pbe_bounce_detect_desc'], 'postinput' => $txt['experimental']),
				array('check', 'pbe_bounce_record', 'subtext' => $txt['pbe_bounce_record_desc'], 'postinput' => $txt['experimental']),
			array('title', 'maillist_outbound'),
				array('desc', 'maillist_outbound_desc'),
				array('check', 'maillist_group_mode'),
				array('check', 'maillist_digest_enabled'),
				array('text', 'maillist_sitename', 40, 'subtext' => $txt['maillist_sitename_desc'], 'postinput' => $txt['maillist_sitename_post']),
				array('text', 'maillist_sitename_address', 40, 'subtext' => $txt['maillist_sitename_address_desc'], 'postinput' => $txt['maillist_sitename_address_post']),
				array('text', 'maillist_mail_from', 40, 'subtext' => $txt['maillist_mail_from_desc'], 'postinput' => $txt['maillist_mail_from_post']),
				array('text', 'maillist_sitename_help', 40, 'subtext' => $txt['maillist_sitename_help_desc'], 'postinput' => $txt['maillist_sitename_help_post']),
				array('text', 'maillist_sitename_regards', 40, 'subtext' => $txt['maillist_sitename_regards_desc']),
			array('title', 'maillist_inbound'),
				array('desc', 'maillist_inbound_desc'),
				array('check', 'maillist_newtopic_change'),
				array('check', 'maillist_newtopic_needsapproval', 'subtext' => $txt['maillist_newtopic_needsapproval_desc'], 'postinput' => $txt['recommended']),
				array('callback', 'maillist_receive_email_list'),
			array('title', 'misc'),
				array('check', 'maillist_allow_attachments'),
				array('int', 'maillist_key_active', 2, 'subtext' => $txt['maillist_key_active_desc']),
			'',
				array('text', 'maillist_leftover_remove', 40, 'subtext' => $txt['maillist_leftover_remove_desc']),
				array('text', 'maillist_sig_keys', 40, 'subtext' => $txt['maillist_sig_keys_desc']),
				array('int', 'maillist_short_line', 2, 'subtext' => $txt['maillist_short_line_desc']),
		);

		// Imap?
		if (!function_exists('imap_open'))
			$config_vars = array_merge($config_vars,
				array(
					array('title', 'maillist_imap_missing'),
				)
			);
		else
			$config_vars = array_merge($config_vars,
				array(
					array('title', 'maillist_imap'),
					array('title', 'maillist_imap_reason'),
						array('text', 'maillist_imap_host', 45, 'subtext' => $txt['maillist_imap_host_desc'], 'disabled' => !function_exists('imap_open')),
						array('text', 'maillist_imap_mailbox', 20, 'postinput' => $txt['maillist_imap_mailbox_desc'], 'disabled' => !function_exists('imap_open')),
						array('text', 'maillist_imap_uid', 20, 'postinput' => $txt['maillist_imap_uid_desc'], 'disabled' => !function_exists('imap_open')),
						array('password', 'maillist_imap_pass', 20, 'postinput' => $txt['maillist_imap_pass_desc'], 'disabled' => !function_exists('imap_open')),
						array('select', 'maillist_imap_connection',
							array(
								'imap' => $txt['maillist_imap_unsecure'],
								'pop3' => $txt['maillist_pop3_unsecure'],
								'imaptls' => $txt['maillist_imap_tls'],
								'imapssl' => $txt['maillist_imap_ssl'],
								'pop3tls' => $txt['maillist_pop3_tls'],
								'pop3ssl' => $txt['maillist_pop3_ssl']
							), 'postinput' => $txt['maillist_imap_connection_desc'], 'disabled' => !function_exists('imap_open'),
						),
						array('check', 'maillist_imap_delete', 20, 'subtext' => $txt['maillist_imap_delete_desc'], 'disabled' => !function_exists('imap_open')),
						array('check', 'maillist_imap_cron', 20, 'subtext' => $txt['maillist_imap_cron_desc'], 'disabled' => !function_exists('imap_open')),
				)
			);

		$GLOBALS['elk']['hooks']->hook('modify_maillist_settings', array(&$config_vars));

		return $config_vars;
	}

	/**
	 * Return the form settings for use in Admin search
	 */
	public function settings_search()
	{
		return $this->_settings();
	}

	/**
	 * View all the custom email bounce templates.
	 *
	 * - Shows all the bounce templates in the system available to this user
	 * - Provides for actions to add or delete them
	 * - Accessed by ?action=Admin;area=maillist;sa=emailtemplates;
	 */
	public function action_view_bounce_templates()
	{
		global $modSettings, $context, $txt, $scripturl;

		// We'll need this, because bounce templates are stored with warning templates.
		require_once(ROOTDIR . '/Messages/Moderation.subs.php');

		// Submitting a new one or editing an existing one then pass this request off
		if (isset($this->http_req->post->add) || isset($this->http_req->post->save) || isset($this->http_req->query->tid))
			return $this->action_modify_bounce_templates();
		// Deleting and existing one
		elseif (isset($this->http_req->post->delete) && !empty($this->http_req->post->deltpl))
		{
			$this->session->check('post');
			validateToken('mod-mlt');
			removeWarningTemplate($this->http_req->post->deltpl, 'bnctpl');
		}

		// This is all the information required for showing the email templates.
		$listOptions = array(
			'id' => 'bounce_template_list',
			'title' => $txt['ml_bounce_templates_title'],
			'items_per_page' => $modSettings['defaultMaxMessages'],
			'no_items_label' => $txt['ml_bounce_templates_none'],
			'base_href' => $scripturl . '?action=Admin;area=maillist;sa=emailtemplates;' . $context['session_var'] . '=' . $context['session_id'],
			'default_sort_col' => 'title',
			'get_items' => array(
				'function' => array($this, 'list_getBounceTemplates'),
			),
			'get_count' => array(
				'function' => array($this, 'list_getBounceTemplateCount'),
				'params' => array('bnctpl'),
			),
			'columns' => array(
				'title' => array(
					'header' => array(
						'value' => $txt['ml_bounce_templates_name'],
					),
					'data' => array(
						'sprintf' => array(
							'format' => '%2$s',
							'params' => array(
								'id_comment' => false,
								'title' => false,
								'body' => false,
							),
						),
					),
					'sort' => array(
						'default' => 'template_title',
						'reverse' => 'template_title DESC',
					),
				),
				'creator' => array(
					'header' => array(
						'value' => $txt['ml_bounce_templates_creator'],
					),
					'data' => array(
						'db' => 'creator',
					),
					'sort' => array(
						'default' => 'creator_name',
						'reverse' => 'creator_name DESC',
					),
				),
				'time' => array(
					'header' => array(
						'value' => $txt['ml_bounce_templates_time'],
					),
					'data' => array(
						'db' => 'time',
					),
					'sort' => array(
						'default' => 'lc.log_time DESC',
						'reverse' => 'lc.log_time',
					),
				),
				'delete' => array(
					'header' => array(
						'value' => '<input type="checkbox" class="input_check" onclick="invertAll(this, this.form);" />',
						'style' => 'width: 4%;',
						'class' => 'centertext',
					),
					'data' => array(
						'function' => function ($rowData) {
							return '<input type="checkbox" name="deltpl[]" value="' . $rowData['id_comment'] . '" class="input_check" />';
						},
						'class' => 'centertext',
					),
				),
			),
			'form' => array(
				'href' => $scripturl . '?action=Admin;area=maillist;sa=emailtemplates',
				'token' => 'mod-mlt',
			),
			'additional_rows' => array(
				array(
					'class' => 'submitbutton',
					'position' => 'below_table_data',
					'value' => '
					<input type="submit" name="delete" value="' . $txt['ml_bounce_template_delete'] . '" onclick="return confirm(\'' . $txt['ml_bounce_template_delete_confirm'] . '\');" class="right_submit" />
					<input type="submit" name="add" value="' . $txt['ml_bounce_template_add'] . '" class="right_submit" />',
				),
			),
		);

		// Create the template list.
		$context['page_title'] = $txt['ml_bounce_templates_title'];
		createToken('mod-mlt');

		createList($listOptions);

		// Show the list
		$context['sub_template'] = 'show_list';
		$context['default_list'] = 'bounce_template_list';
	}

	/**
	 * Edit a 'it bounced' template.
	 *
	 * @uses bounce_template sub template
	 */
	public function action_modify_bounce_templates()
	{
		global $context, $txt, $user_info;

		require_once(ROOTDIR . '/Messages/Moderation.subs.php');

		$context['id_template'] = isset($this->http_req->query->tid) ? (int) $this->http_req->query->tid : 0;
		$context['is_edit'] = (bool) $context['id_template'];

		// Standard template things, you know the drill
		$context['page_title'] = $context['is_edit'] ? $txt['ml_bounce_template_modify'] : $txt['ml_bounce_template_add'];
		$context['sub_template'] = 'bounce_template';
		$context[$context['admin_menu_name']]['current_subsection'] = 'templates';

		// Defaults to show
		$context['template_data'] = array(
			'title' => '',
			'body' => $txt['ml_bounce_template_body_default'],
			'subject' => $txt['ml_bounce_template_subject_default'],
			'personal' => false,
			'can_edit_personal' => true,
		);

		// If it's an edit load it.
		if ($context['is_edit'])
			modLoadTemplate($context['id_template'], 'bnctpl');

		// Wait, we are saving?
		if (isset($this->http_req->post->save))
		{
			$this->session->check('post');
			validateToken('mod-mlt');

			// To check the BBC is good...

			// Bit of cleaning!
			$template_body = trim($this->http_req->post->template_body);
			$template_title = trim($this->http_req->post->template_title);

			// Need something in both boxes.
			if (!empty($template_body) && !empty($template_title))
			{
				// Safety first.
				$template_title = $GLOBALS['elk']['text']->htmlspecialchars($template_title);

				// Clean up BBC.
				preparsecode($template_body);

				// But put line breaks back!
				$template_body = strtr($template_body, array('
' => "\n"));

				// Is this personal?
				$recipient_id = !empty($this->http_req->post->make_personal) ? $user_info['id'] : 0;

				// Updating or adding ?
				if ($context['is_edit'])
				{
					// Simple update...
					modAddUpdateTemplate($recipient_id, $template_title, $template_body, $context['id_template'], true, 'bnctpl');

					// If it wasn't visible and now is they've effectively added it.
					if ($context['template_data']['personal'] && !$recipient_id)
						logAction('add_bounce_template', array('template' => $template_title));
					// Conversely if they made it personal it's a delete.
					elseif (!$context['template_data']['personal'] && $recipient_id)
						logAction('delete_bounce_template', array('template' => $template_title));
					// Otherwise just an edit.
					else
						logAction('modify_bounce_template', array('template' => $template_title));
				}
				else
				{
					modAddUpdateTemplate($recipient_id, $template_title, $template_body, $context['id_template'], false, 'bnctpl');
					logAction('add_bounce_template', array('template' => $template_title));
				}

				// Get out of town...
				redirectexit('action=Admin;area=maillist;sa=emailtemplates');
			}
			else
			{
				$context['warning_errors'] = array();
				$context['template_data']['title'] = !empty($template_title) ? $template_title : '';
				$context['template_data']['body'] = !empty($template_body) ? $template_body : $txt['ml_bounce_template_body_default'];
				$context['template_data']['personal'] = !empty($this->http_req->post->make_personal);

				if (empty($template_title))
					$context['warning_errors'][] = $txt['ml_bounce_template_error_no_title'];

				if (empty($template_body))
					$context['warning_errors'][] = $txt['ml_bounce_template_error_no_body'];
			}
		}

		createToken('mod-mlt');
	}

	/**
	 * Get all the bounce templates from the system
	 *
	 * - Callback for createList()
	 *
	 * @param int $start The item to start with (for pagination purposes)
	 * @param int $items_per_page  The number of items to show per page
	 * @param string $sort A string indicating how to sort the results
	 */
	public function list_getBounceTemplates($start, $items_per_page, $sort)
	{
		return warningTemplates($start, $items_per_page, $sort, 'bnctpl');
	}

	/**
	 * Get the number of bounce templates in the system
	 *
	 * - Callback for createList() to warningTemplateCount
	 */
	public function list_getBounceTemplateCount()
	{
		return warningTemplateCount('bnctpl');
	}

	/**
	 * Get the number of unapproved emails
	 *
	 * - Callback for createList() to list_maillist_unapproved
	 *
	 * @param int $start The item to start with (for pagination purposes)
	 * @param int $items_per_page The number of items to show per page
	 * @param string $sort A string indicating how to sort the results
	 * @param int $id = 0
	 */
	public function list_maillist_unapproved($start, $items_per_page, $sort = '', $id = 0)
	{
		return list_maillist_unapproved($id, $start, $items_per_page, $sort);
	}
}


text:
A tú servicio

            A menos de 70 km de Bogotá, usted encontrará esta hermosa finca, la casa principal construida en el 2014. Cuenta con tres cuartos, dos baños, cocina y sala comedor espaciosos y modernos, diseñados con mucho estilo por una familia Colombo-Sueca. Piscina con jacuzzi, sillas soldadoras y parasol, zona de juego, dos asadores, parqueadero para 5 carros, árboles frutales, plata eléctrica y los asesoraos necesarios para disfrutar sus vacaciones.  Mayordomo cuida y vive en la finca, en casa separada.
Vista inigualable, en las mañanas disfrutará de los nevados del Ruiz y del Tolima y en la tarde de los mejores atardeceres que pueda imaginar.

redpajama_set_name: RedPajamaGithub


text:
package apple.accelerate.enums;

import org.moe.natj.general.ann.Generated;

@Generated
public final class CBLAS_TRANSPOSE {
    @Generated public static final int CblasNoTrans = 0x0000006F;
    @Generated public static final int CblasTrans = 0x00000070;
    @Generated public static final int CblasConjTrans = 0x00000071;
    @Generated public static final int AtlasConj = 0x00000072;

    @Generated
    private CBLAS_TRANSPOSE() {
    }
}


text:
#ifndef WINPR_ENVIRONMENT_H
#define WINPR_ENVIRONMENT_H

#include <winpr/winpr.h>
#include <winpr/wtypes.h>

#ifndef _WIN32

#ifdef __cplusplus
extern "C"
{
#endif

	WINPR_API DWORD GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer);
	WINPR_API DWORD GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer);

	WINPR_API BOOL SetCurrentDirectoryA(LPCSTR lpPathName);
	WINPR_API BOOL SetCurrentDirectoryW(LPCWSTR lpPathName);

	WINPR_API DWORD SearchPathA(LPCSTR lpPath, LPCSTR lpFileName, LPCSTR lpExtension,
	                            DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart);
	WINPR_API DWORD SearchPathW(LPCWSTR lpPath, LPCWSTR lpFileName, LPCWSTR lpExtension,
	                            DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR* lpFilePart);

	WINPR_API LPSTR GetCommandLineA(VOID);
	WINPR_API LPWSTR GetCommandLineW(VOID);

	WINPR_API BOOL NeedCurrentDirectoryForExePathA(LPCSTR ExeName);
	WINPR_API BOOL NeedCurrentDirectoryForExePathW(LPCWSTR ExeName);

#ifdef __cplusplus
}
#endif

#ifdef UNICODE
#define GetCurrentDirectory GetCurrentDirectoryW
#define SetCurrentDirectory SetCurrentDirectoryW
#define SearchPath SearchPathW
#define GetCommandLine GetCommandLineW
#define NeedCurrentDirectoryForExePath NeedCurrentDirectoryForExePathW
#else
#define GetCurrentDirectory GetCurrentDirectoryA
#define SetCurrentDirectory SetCurrentDirectoryA
#define SearchPath SearchPathA
#define GetCommandLine GetCommandLineA
#define NeedCurrentDirectoryForExePath NeedCurrentDirectoryForExePathA
#endif

#endif

#if !defined(_WIN32) || defined(_UWP)

#ifdef __cplusplus
extern "C"
{
#endif

	WINPR_API DWORD GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize);
	WINPR_API DWORD GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize);

	WINPR_API BOOL SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue);
	WINPR_API BOOL SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue);

	/**
	 * A brief history of the GetEnvironmentStrings functions:
	 * http://blogs.msdn.com/b/oldnewthing/archive/2013/01/17/10385718.aspx
	 */

	WINPR_API LPCH GetEnvironmentStrings(VOID);
	WINPR_API LPWCH GetEnvironmentStringsW(VOID);

	WINPR_API BOOL SetEnvironmentStringsA(LPCH NewEnvironment);
	WINPR_API BOOL SetEnvironmentStringsW(LPWCH NewEnvironment);

	WINPR_API DWORD ExpandEnvironmentStringsA(LPCSTR lpSrc, LPSTR lpDst, DWORD nSize);
	WINPR_API DWORD ExpandEnvironmentStringsW(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize);

	WINPR_API BOOL FreeEnvironmentStringsA(LPCH lpszEnvironmentBlock);
	WINPR_API BOOL FreeEnvironmentStringsW(LPWCH lpszEnvironmentBlock);

#ifdef __cplusplus
}
#endif

#ifdef UNICODE
#define GetEnvironmentVariable GetEnvironmentVariableW
#define SetEnvironmentVariable SetEnvironmentVariableW
#define GetEnvironmentStrings GetEnvironmentStringsW
#define SetEnvironmentStrings SetEnvironmentStringsW
#define ExpandEnvironmentStrings ExpandEnvironmentStringsW
#define FreeEnvironmentStrings FreeEnvironmentStringsW
#else
#define GetEnvironmentVariable GetEnvironmentVariableA
#define SetEnvironmentVariable SetEnvironmentVariableA
#define GetEnvironmentStringsA GetEnvironmentStrings
#define SetEnvironmentStrings SetEnvironmentStringsA
#define ExpandEnvironmentStrings ExpandEnvironmentStringsA
#define FreeEnvironmentStrings FreeEnvironmentStringsA
#endif

#endif

#ifdef __cplusplus
extern "C"
{
#endif

	WINPR_API LPCH MergeEnvironmentStrings(PCSTR original, PCSTR merge);

	WINPR_API DWORD GetEnvironmentVariableEBA(LPCSTR envBlock, LPCSTR lpName, LPSTR lpBuffer,
	                                          DWORD nSize);
	WINPR_API BOOL SetEnvironmentVariableEBA(LPSTR* envBlock, LPCSTR lpName, LPCSTR lpValue);

	WINPR_API char** EnvironmentBlockToEnvpA(LPCH lpszEnvironmentBlock);

#ifdef __cplusplus
}
#endif

#endif /* WINPR_ENVIRONMENT_H */


text:
'use strict';

var simpleView = require('../util/controller_util');
var searchqueryView = require('../../../apps/searchquery/searchquery_view');

function searchqueryController() {
  simpleView(searchqueryView);
}

module.exports = searchqueryController;


text:
<?php namespace System\Console;

use Db;
use App;
use Str;
use Url;
use PDO;
use File;
use Config;
use Artisan;
use Cms\Classes\Theme;
use Cms\Classes\ThemeManager;
use Backend\Database\Seeds\SeedSetupAdmin;
use System\Classes\UpdateManager;
use October\Rain\Config\ConfigWriter;
use Illuminate\Console\Command;
use Illuminate\Encryption\Encrypter;
use Symfony\Component\Console\Input\InputOption;
use Exception;

/**
 * Console command to install October.
 *
 * This sets up October for the first time. It will prompt the user for several
 * configuration items, including application URL and database config, and then
 * perform a database migration.
 *
 * @package october\system
 * @author Alexey Bobkov, Samuel Georges
 */
class OctoberInstall extends Command
{
    use \Illuminate\Console\ConfirmableTrait;

    /**
     * The console command name.
     */
    protected $name = 'october:install';

    /**
     * The console command description.
     */
    protected $description = 'Set up October for the first time.';

    /**
     * @var October\Rain\Config\ConfigWriter
     */
    protected $configWriter;

    /**
     * Create a new command instance.
     */
    public function __construct()
    {
        parent::__construct();

        $this->configWriter = new ConfigWriter;
    }

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $this->displayIntro();

        if (
            App::hasDatabase() &&
            !$this->confirm('Application appears to be installed already. Continue anyway?', false)
        ) {
            return;
        }

        $this->setupDatabaseConfig();
        $this->setupAdminUser();
        $this->setupCommonValues();

        if ($this->confirm('Configure advanced options?', false)) {
            $this->setupEncryptionKey();
            $this->setupAdvancedValues();
        }
        else {
            $this->setupEncryptionKey(true);
        }

        $this->setupMigrateDatabase();
        $this->displayOutro();
    }

    /**
     * Get the console command arguments.
     */
    protected function getArguments()
    {
        return [];
    }

    /**
     * Get the console command options.
     */
    protected function getOptions()
    {
        return [
            ['force', null, InputOption::VALUE_NONE, 'Force the operation to run.'],
        ];
    }

    //
    // Misc
    //

    protected function setupCommonValues()
    {
        $url = $this->ask('Application URL', Config::get('app.url'));
        $this->writeToConfig('app', ['url' => $url]);
    }

    protected function setupAdvancedValues()
    {
        $backendUri = $this->ask('Backend URL', Config::get('cms.backendUri'));
        $this->writeToConfig('cms', ['backendUri' => $backendUri]);

        $defaultMask = $this->ask('File Permission Mask', Config::get('cms.defaultMask.file') ?: '777');
        $this->writeToConfig('cms', ['defaultMask.file' => $defaultMask]);

        $defaultMask = $this->ask('Folder Permission Mask', Config::get('cms.defaultMask.folder') ?: '777');
        $this->writeToConfig('cms', ['defaultMask.folder' => $defaultMask]);

        $debug = (bool) $this->confirm('Enable Debug Mode?', true);
        $this->writeToConfig('app', ['debug' => $debug]);
    }

    //
    // Encryption key
    //

    protected function setupEncryptionKey($force = false)
    {
        $validKey = false;
        $cipher = Config::get('app.cipher');
        $keyLength = $this->getKeyLength($cipher);
        $randomKey = $this->getRandomKey($cipher);

        if ($force) {
            $key = $randomKey;
        }
        else {
            $this->line(sprintf('Enter a new value of %s characters, or press ENTER to use the generated key', $keyLength));

            while (!$validKey) {
                $key = $this->ask('Application key', $randomKey);
                $validKey = Encrypter::supported($key, $cipher);
                if (!$validKey) {
                    $this->error(sprintf('[ERROR] Invalid key length for "%s" cipher. Supplied key must be %s characters in length.', $cipher, $keyLength));
                }
            }
        }

        $this->writeToConfig('app', ['key' => $key]);

        $this->info(sprintf('Application key [%s] set successfully.', $key));
    }

    /**
     * Generate a random key for the application.
     *
     * @param  string  $cipher
     * @return string
     */
    protected function getRandomKey($cipher)
    {
        return Str::random($this->getKeyLength($cipher));
    }

    /**
     * Returns the supported length of a key for a cipher.
     *
     * @param  string  $cipher
     * @return int
     */
    protected function getKeyLength($cipher)
    {
        return $cipher === 'AES-128-CBC' ? 16 : 32;
    }

    //
    // Database config
    //

    protected function setupDatabaseConfig()
    {
        $type = $this->choice('Database type', ['MySQL', 'Postgres', 'SQLite', 'SQL Server']);

        $typeMap = [
            'SQLite' => 'sqlite',
            'MySQL' => 'mysql',
            'Postgres' => 'pgsql',
            'SQL Server' => 'sqlsrv',
        ];

        $driver = array_get($typeMap, $type, 'sqlite');

        $method = 'setupDatabase'.Str::studly($driver);

        $newConfig = $this->$method();

        $this->writeToConfig('database', ['default' => $driver]);

        foreach ($newConfig as $config => $value) {
            $this->writeToConfig('database', ['connections.'.$driver.'.'.$config => $value]);
        }
    }

    protected function setupDatabaseMysql()
    {
        $result = [];
        $result['host'] = $this->ask('MySQL Host', Config::get('database.connections.mysql.host'));
        $result['port'] = $this->output->ask('MySQL Port', Config::get('database.connections.mysql.port') ?: false) ?: '';
        $result['database'] = $this->ask('Database Name', Config::get('database.connections.mysql.database'));
        $result['username'] = $this->ask('MySQL Login', Config::get('database.connections.mysql.username'));
        $result['password'] = $this->ask('MySQL Password', Config::get('database.connections.mysql.password') ?: false) ?: '';
        return $result;
    }

    protected function setupDatabasePgsql()
    {
        $result = [];
        $result['host'] = $this->ask('Postgres Host', Config::get('database.connections.pgsql.host'));
        $result['port'] = $this->ask('Postgres Port', Config::get('database.connections.pgsql.port') ?: false) ?: '';
        $result['database'] = $this->ask('Database Name', Config::get('database.connections.pgsql.database'));
        $result['username'] = $this->ask('Postgres Login', Config::get('database.connections.pgsql.username'));
        $result['password'] = $this->ask('Postgres Password', Config::get('database.connections.pgsql.password') ?: false) ?: '';
        return $result;
    }

    protected function setupDatabaseSqlite()
    {
        $filename = $this->ask('Database path', Config::get('database.connections.sqlite.database'));

        try {
            if (!file_exists($filename)) {
                $directory = dirname($filename);
                if (!is_dir($directory)) {
                    mkdir($directory, 0777, true);
                }

                new PDO('sqlite:'.$filename);
            }
        }
        catch (Exception $ex) {
            $this->error($ex->getMessage());
            $this->setupDatabaseSqlite();
        }

        return ['database' => $filename];
    }

    protected function setupDatabaseSqlsrv()
    {
        $result = [];
        $result['host'] = $this->ask('SQL Host', Config::get('database.connections.sqlsrv.host'));
        $result['port'] = $this->ask('SQL Port', Config::get('database.connections.sqlsrv.port') ?: false) ?: '';
        $result['database'] = $this->ask('Database Name', Config::get('database.connections.sqlsrv.database'));
        $result['username'] = $this->ask('SQL Login', Config::get('database.connections.sqlsrv.username'));
        $result['password'] = $this->ask('SQL Password', Config::get('database.connections.sqlsrv.password') ?: false) ?: '';
        return $result;
    }

    //
    // Migration
    //

    protected function setupAdminUser()
    {
        $this->line('Enter a new value, or press ENTER for the default');

        SeedSetupAdmin::$firstName = $this->ask('First Name', SeedSetupAdmin::$firstName);
        SeedSetupAdmin::$lastName = $this->ask('Last Name', SeedSetupAdmin::$lastName);
        SeedSetupAdmin::$email = $this->ask('Email Address', SeedSetupAdmin::$email);
        SeedSetupAdmin::$login = $this->ask('Admin Login', SeedSetupAdmin::$login);
        SeedSetupAdmin::$password = $this->ask('Admin Password', SeedSetupAdmin::$password);

        if (!$this->confirm('Is the information correct?', true)) {
            $this->setupAdminUser();
        }
    }

    protected function setupMigrateDatabase()
    {
        $this->line('Migrating application and plugins...');

        try {
            Db::purge();

            UpdateManager::instance()
                ->setNotesOutput($this->output)
                ->update()
            ;
        }
        catch (Exception $ex) {
            $this->error($ex->getMessage());
            $this->setupDatabaseConfig();
            $this->setupMigrateDatabase();
        }
    }

    //
    // Helpers
    //

    protected function displayIntro()
    {
        $message = [
            ".====================================================================.",
            "                                                                      ",
            " .d8888b.   .o8888b.   db  .d8888b.  d8888b. d88888b d8888b.  .d888b. ",
            ".8P    Y8. d8P    Y8   88 .8P    Y8. 88  `8D 88'     88  `8D .8P , Y8.",
            "88      88 8P      oooo88 88      88 88oooY' 88oooo  88oobY' 88  |  88",
            "88      88 8b      ~~~~88 88      88 88~~~b. 88~~~~  88`8b   88  |/ 88",
            "`8b    d8' Y8b    d8   88 `8b    d8' 88   8D 88.     88 `88. `8b | d8'",
            " `Y8888P'   `Y8888P'   YP  `Y8888P'  Y8888P' Y88888P 88   YD  `Y888P' ",
            "                                                                      ",
            "`=========================== INSTALLATION ==========================='",
            "",
        ];

        $this->line($message);
    }

    protected function displayOutro()
    {
        $message = [
            ".=========================================.",
            "                ,@@@@@@@,                  ",
            "        ,,,.   ,@@@@@@/@@,  .oo8888o.      ",
            "     ,&%%&%&&%,@@@@@/@@@@@@,8888\88/8o     ",
            "    ,%&\%&&%&&%,@@@\@@@/@@@88\88888/88'    ",
            "    %&&%&%&/%&&%@@\@@/ /@@@88888\88888'    ",
            "    %&&%/ %&%%&&@@\ V /@@' `88\8 `/88'     ",
            "    `&%\ ` /%&'    |.|        \ '|8'       ",
            "        |o|        | |         | |         ",
            "        |.|        | |         | |         ",
            "`========= INSTALLATION COMPLETE ========='",
            "",
        ];

        $this->line($message);
    }

    protected function writeToConfig($file, $values)
    {
        $configFile = $this->getConfigFile($file);

        foreach ($values as $key => $value) {
            Config::set($file.'.'.$key, $value);
        }

        $this->configWriter->toFile($configFile, $values);
    }

    /**
     * Get a config file and contents.
     *
     * @return array
     */
    protected function getConfigFile($name = 'app')
    {
        $env = $this->option('env') ? $this->option('env').'/' : '';

        $name .= '.php';

        $contents = File::get($path = $this->laravel['path.config']."/{$env}{$name}");

        return $path;
    }
}


text:
title: 'Yarn Package Manager: Será que é melhor do que NPM?'
author: André Lucas
date: 2016-12-23 17:52:34 -03:00
url: "/yarn-package-manager-sera-que-e-melhor-do-que-npm/"
featured_image: uploads/2016/12/Screenshot_1-2
featured_image_extension: jpg
mvp_post_template:
- def
mvp_featured_image:
- show
post_views_count:
- 376
categories:
- Desenvolvimento Web
tags:
- desenvolvimento
- git
- node.js
- npm
- programação
- software
- yarn

---
Se você programa em NODE.js, provavelmente tem uma relação bem próxima com o gerenciador de pacotes (Package Manager) [NPM](https://www.npmjs.com/). Desde o início da criação do NODE o NPM teve um papel central e em alguns casos determinante no sucesso da tecnologia. A facilidade de gerenciar bibliotecas e pedaços de códigos com o NPM fez a comunidade de NODE ser ativa da mesma maneira que as gemas (Gems) no Ruby on Rails.

Contudo, recentemente um concorrente andou ganhando destaque no universo da plataforma em JavaScript: o [Yarn](https://yarnpkg.com/).

Aproveite também para conferir as melhores bibliotecas JavaScript para 2018.

## Histórico

Em um passado recente para se desenvolver um software ou um site você tinha que achar o pacote ou biblioteca online, fazer o download da versão em .zip, extrair, adicionar ao seu projeto e torcer para estar tudo ok. Poucas pessoas eram ativas no desenvolvimento e melhora desses packages e o controle de versão era uma complicação imensa.

Para solucionar essa dificuldade, gestores de pacotes (que agora vou me referir apenas como package managers) como o Bower começaram a surgir. Como a maior parte dos packages são open source (código aberto) e sua disponibilidade era em repositórios de controle de versão, como o [Github](https://github.com/), essas ferramentas ajudaram a organizar, baixar, atualizar e aplicar códigos para sua reutilização em diversos projetos.

O \[Se você programa em NODE.js, provavelmente tem uma relação bem próxima com o gerenciador de pacotes (Package Manager) [NPM](https://www.npmjs.com/). Desde o início da criação do NODE o NPM teve um papel central e em alguns casos determinante no sucesso da tecnologia. A facilidade de gerenciar bibliotecas e pedaços de códigos com o NPM fez a comunidade de NODE ser ativa da mesma maneira que as gemas (Gems) no Ruby on Rails.

Contudo, recentemente um concorrente andou ganhando destaque no universo da plataforma em JavaScript: o [Yarn](https://yarnpkg.com/).

Aproveite também para conferir as melhores bibliotecas JavaScript para 2018.

## Histórico

Em um passado recente para se desenvolver um software ou um site você tinha que achar o pacote ou biblioteca online, fazer o download da versão em .zip, extrair, adicionar ao seu projeto e torcer para estar tudo ok. Poucas pessoas eram ativas no desenvolvimento e melhora desses packages e o controle de versão era uma complicação imensa.

Para solucionar essa dificuldade, gestores de pacotes (que agora vou me referir apenas como package managers) como o Bower começaram a surgir. Como a maior parte dos packages são open source (código aberto) e sua disponibilidade era em repositórios de controle de versão, como o [Github](https://github.com/), essas ferramentas ajudaram a organizar, baixar, atualizar e aplicar códigos para sua reutilização em diversos projetos.

O\][4](https://bower.io/) tinha seus problemas, então uma solução melhorada surgiu com o nome de NPM. Só que a partir do ponto que Node escolheu o NPM como seu package manager padrão ele cresceu imensamente e hoje é um dos maiores gerenciadores disponíveis.

## Problemas com NPM

O NPM é muito bom, mas também não é perfeito. Com o avanço das tecnologias e com a transformação do uso dessas tecnologias, veja alguns dos problemas que hoje o gerenciador enfrenta:

* **Dependências aninhadas** (Nested dependencies): A versão 2 do NPM aninha as suas dependências, o que leva a duplicações. Isto é, um mesmo pacote pode ser baixado várias vezes em um mesmo projeto. Ao mesmo tempo, se você é um usuário de Windows isso também levará a um problema de caminho grande. Deletar as pastas das dependências acaba se tornando um problema por esse motivo para esses usuários, mas na versão 3 do NPM isso foi resolvido.
* **Instalação em fila**: Quando NPM busca as dependências de seus repositórios ele instala cada dependência uma depois da outra, o que gasta um tempo enorme para instalar um projeto e começar a trabalhar nele.
* **Registro único**: Se um pacote não está no [NpmJS](https://www.npmjs.com/), você não vai conseguir baixá-lo.
* **Não há instalação offline**

## Introduzindo Yarn

O [Yarn](https://yarnpkg.com/) nasceu de uma iniciativa de gigantes da tecnologia, como Facebook e Google, para a criação de um package manager melhor, mais rápido e mais seguro.

Essa ferramenta possibilita utilizar de pacotes e bibliotecas de múltiplos registros, como NpmJS e Bower e roda as instalações em paralelo. Se você já tiver algum pacote instalado, o Yarn cria uma cópia em cache, facilitando a instalação offline de pacotes. Além disso, já desde o início ele oferece uma estrutura plana de dependências, contrastando com a aninhada do NPM.

Se um request de um pacote qualquer falhar, não vai causar o erro na instalação imediatamente. Ele irá tentar novamente após falhas. Ou seja, considerando tudo que vimos o Yarn é uma melhor alternativa para o NPM por proporcionar uma dinâmica de trabalho melhor.

## Instalando o Yarn

Há duas formas hoje para instalar o Yarn no seu computador. O primeiro método é utilizar o próprio NPM – é tipo instalar o Chrome baixando ele no Internet Explorer).

npm install -g yarn

Esse comando instala o Yarn globalmente e faz o comando yarn ficar disponível no terminal ou prompt de comando.

O segundo método é ir ao [site oficial](https://yarnpkg.com/) e baixar o instalador para o seu sistema operacional.

É importante mencionar que ambos jeitos necessitam que o Node.js esteja instalado previamente.

## Como o Yarn funciona

O Yarn não substitui o NPM, mas sim o melhora. ele utiliza o mesmo arquivo **package.json** e salva as dependências na pasta **node_modules/**. Para testar, vamos ver um exemplo de um arquivo package.json de um projeto.

"dependencies": {
"agenda": "^0.8.1",
"agendash": "^0.3.1",
"bcrypt-nodejs": "_",
"body-parser": "^1.15.0",
"busboy": "^0.2.12",
"cloudinary": "^1.3.1",
"connect-flash": "_",
"connect-mongo": "^1.1.0",
"cookie-parser": "^1.4.1",
"cookie-session": "^1.2.0",
"errorhandler": "^1.4.3",
"express": "^4.13.4",
"express-session": "^1.13.0",
"ip": "^1.1.2",
"jade": "^1.11.0",
"method-override": "^2.3.5",
"mkdirp": "^0.5.1",
"mongodb": "^2.1.8",
"mongoose": "^4.4.7",
"morgan": "^1.7.0",
"multer": "^0.1.8",
"node-moip": "0.0.2-2",
"node-transloadit": "_",
"nodemailer": "^2.3.0",
"nodemon": "^1.9.1",
"passport": "^0.3.2",
"passport-facebook": "^2.1.0",
"passport-google-oauth": "^1.0.0",
"passport-local": "_",
"passport-twitter": "^1.0.4",
"qs": "^2.4.2",
"request": "^2.73.0",
"response-time": "^2.3.1",
"sendgrid": "^2.0.0",
"stylus": "^0.54.2"
}

Em seguida, podemos criar uma pasta chamada yarn e copiar o arquivo package.json para lá. Para instalar as dependências vá pelo terminal ou prompt de comando até a raiz desse diretório e rode o comando `yarn` ou `yarn install`.

Se você ainda não tiver um arquivo package.json, basta rodar o comando `yarn init.` Isso irá gerar uma série de perguntas para já criar um arquivo baseado dos seus objetivos.

## Adicionar, atualizar e remover dependências

Para adicionar uma dependência, o comando é:

yarn add \[package-name\]

Se você quiser uma versão ou tag específica, pode fazer da seguinte forma:

yarn add \[package\]@\[versão-ou-tag\]

Para dependências de desenvolvimento (dev dependencies), opcionais ou peer, pode usar os comandos `--dev` `--peer` `--optional`.

yarn add gulp --dev

Isso irá salvar o gulp no bloco de `devDependencies`. Para atualizar ou remover um pacote, precisamos apenas trocar  o comando `add` para ou `upgrade` ou `remove` seguido do nome do pacote.

# atualizar o gulp de 3.9.1 para a versão 4
yarn upgrade gulp@4.0

# remover o gulp

yarn remove gulp

## O arquivo yarn.lock

Após cada instalação, atualização ou remoção, o Yarn atualiza um arquivo chamado **yarn.lock** que irá manter um controle sobre cada pacote instalado na pasta **node_modules**.

Uma dica bacana é que você deverá adicionar esse arquivo no seu controle de versão se quiser uma consistência nos pacotes.

## Removendo o lixo

A maior parte dos packages managers vêm com muito lixo e coisas inúteis. Utilizando uma [imagem de guy fierri](https://medium.com/friendship-dot-js/i-peeked-into-my-node-modules-directory-and-you-wont-believe-what-happened-next-b89f63d21558#.trgwdqrkz) podemos ver como o Yarn ajuda nessa questão ao rodarmos o comando `yarn clean`. Ele irá remover todo o lixo que conseguir.

Após o comando um arquivo chamado **.yarnclean** será criado e irá ajudar futuras instalações a não adicionarem lixo para o projeto. Esse é um arquivo que é bom manter no seu controle de versão.

## Atualizando o Yarn

Sempre novas versões são lançadas dos packages managers para os melhorar. No caso do Yarn, a forma que temos para atualizar o seu próprio sistema é rodando o comando:

yarn self-update

Se você quiser instalar uma versão específica do gerenciador, então é só utilizar o seguinte:

yarn self-update 0.15.1

[Para mais comandos, por favor consulte o site oficial.](https://yarnpkg.com/en/docs/cli/)

## O futuro do Yarn {#future-of-yarn}

Como o Yarn está sendo apoiado por empresas grandes e de peso, é muito provável que iremos ver novas funcionalidades que ajudam e melhoram o desenvolvimento de software. Não acho impossível em breve termos esse package manager como padrão em novas tecnologias ou mesmo sendo o substituto de antigos gestores de pacotes em tecnologias que usamos hoje. Quem sabe até do Node.js.

De qualquer maneira o projeto é muito bacana e vale a pena ser utilizado para simplificar a vida dos desenvolvedores.

Algo bobo, mas interessante: Usuários do Windows não possuem acesso aos emojis quando instalam as dependências:

Fonte do artigo de referência: [Scotch.io](https://scotch.io/tutorials/yarn-package-manager-an-improvement-over-npm#introducing-yarn)


text:
Lightweight replacement to `express`.

```coffee
slush = require "slush"

app = slush()

app.ready ->
  console.log "Listening at http://localhost:" + app.port
```

### Options

- **sock: String?** Listen on a socket path
- **port: Number?** If undefined, default to `process.env.PORT`, or `443` (if HTTPS), or `8000` (if HTTP)
- **secure: Boolean?** If true, an HTTPS server is created with `ssl.key` and `ssl.crt` from the project root
- **maxHeaders: Number?** Limit the number of headers (defaults to 50)
- **timeout: Number?** The timeout (in ms) before a request sends a 408 response
- **onError: Function?** Send a custom response when your server throws an unexpected error
- **onUnhandled: Function?** Send a custom response when a request goes unhandled

### Request handling

The `slush` server uses a pipeline of request handlers (called "pipes").

```coffee
app.pipe (req, res) ->
  # Handle the request in here.
  # Calls to `pipe` can be chained.
```

The return value of each pipe is used to determine the server's next action.

Return `undefined` or `null` to skip the rest of the current pipe.

Return a `Number` literal to set the status code for an empty response.

Return an `Error` instance for invalid requests.

Return a `Promise` instance for asynchronous requests.
The resolved value is treated the same as synchronous return values.

The server will visit each pipe in the pipeline until `res.send` is called.
If that never happens, the server responds with "404 Not Found".

If an `Error` instance is thrown while inside a pipe, the server responds with "500 Internal Error".

### Express middleware

Support for `express` middleware is included.

```coffee
app.use middleware
```

### The `drain` method

Use the `drain()` method for running code after the response is sent.

```coffee
app.drain (req, res) ->
  # The response has been sent.
```

### Server events

- `request: (req, res)` A request was received
- `response: (req, res)` A response has finished
- `requestError: (error)` The request handler caught an error (not emitted when `options.onError` is defined)
- `error: ()` A server error occurred [(see here)](https://nodejs.org/api/net.html#net_event_error)
- `close: ()` The server has closed, all connections have ended [(see here)](https://nodejs.org/api/net.html#net_event_close)


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://github.com/googleapis/google-api-java-client-services/
 * Modify at your own risk.
 */

package com.google.api.services.dfareporting.model;

/**
 * Audience Segment Group.
 *
 *
 This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the Campaign Manager 360 API. For a detailed explanation
 * see:
 * https://developers.google.com/api-client-library/java/google-http-java-client/json
 *

 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class AudienceSegmentGroup extends com.google.api.client.json.GenericJson {

  /**
   * Audience segments assigned to this group. The number of segments must be between 2 and 100.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.util.List<AudienceSegment> audienceSegments;

  static {
    // hack to force ProGuard to consider AudienceSegment used, since otherwise it would be stripped out
    // see https://github.com/google/google-api-java-client/issues/543
    com.google.api.client.util.Data.nullOf(AudienceSegment.class);
  }

  /**
   * ID of this audience segment group. This is a read-only, auto-generated field.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key @com.google.api.client.json.JsonString
  private java.lang.Long id;

  /**
   * Name of this audience segment group. This is a required field and must be less than 65
   * characters long.
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.String name;

  /**
   * Audience segments assigned to this group. The number of segments must be between 2 and 100.
   * @return value or {@code null} for none
   */
  public java.util.List<AudienceSegment> getAudienceSegments() {
    return audienceSegments;
  }

  /**
   * Audience segments assigned to this group. The number of segments must be between 2 and 100.
   * @param audienceSegments audienceSegments or {@code null} for none
   */
  public AudienceSegmentGroup setAudienceSegments(java.util.List<AudienceSegment> audienceSegments) {
    this.audienceSegments = audienceSegments;
    return this;
  }

  /**
   * ID of this audience segment group. This is a read-only, auto-generated field.
   * @return value or {@code null} for none
   */
  public java.lang.Long getId() {
    return id;
  }

  /**
   * ID of this audience segment group. This is a read-only, auto-generated field.
   * @param id id or {@code null} for none
   */
  public AudienceSegmentGroup setId(java.lang.Long id) {
    this.id = id;
    return this;
  }

  /**
   * Name of this audience segment group. This is a required field and must be less than 65
   * characters long.
   * @return value or {@code null} for none
   */
  public java.lang.String getName() {
    return name;
  }

  /**
   * Name of this audience segment group. This is a required field and must be less than 65
   * characters long.
   * @param name name or {@code null} for none
   */
  public AudienceSegmentGroup setName(java.lang.String name) {
    this.name = name;
    return this;
  }

  @Override
  public AudienceSegmentGroup set(String fieldName, Object value) {
    return (AudienceSegmentGroup) super.set(fieldName, value);
  }

  @Override
  public AudienceSegmentGroup clone() {
    return (AudienceSegmentGroup) super.clone();
  }

}


text:
def convert_dates
  @items.each do |item|
    unless item[:published_at].nil?
      item[:published_at] = DateTime.parse(item[:published_at].to_s)
    end

    unless item[:modified_at].nil?
      item[:modified_at] = DateTime.parse(item[:modified_at].to_s)
    end
  end
end

def set_modified_at
  @items.each do |item|
    if !item[:published_at].nil? and item[:modified_at].nil?
      item[:modified_at] = item[:published_at]
    end
  end

  unless @items['/index.*'].nil?
    @items['/index.*'][:modified_at] = latest_publication[:published_at]
  end

  unless @items['/publications.*'].nil?
    @items['/publications.*'][:modified_at] = latest_publication[:published_at]
  end
end

def hide_items
  @items.each do |item|
    if ['css', 'js'].include?(item[:extension])
      item[:is_hidden] = true
    end
  end
end


text: Machine readable UML XMI unzipped in its directory structure,


text:
require 'test_helper'

# Test Models:
#
#   Answer       - :scope => :question_id
#   Comment      - :scope => :question_id (with an AR default scope)
#   Invoice      - :scope => :account_id, :start_at => 1000
#   Product      - :scope => :account_id, :start_at => lambda { |r| r.computed_start_value }
#   Order        - :scope => :non_existent_column
#   User         - :scope => :account_id, :column => :custom_sequential_id
#   Address      - :scope => :account_id ('sequential_id' does not exist)
#   Email        - :scope => [:emailable_id, :emailable_type]
#   Subscription - no options
#   Rating       - :scope => :comment_id, skip: { |r| r.score == 0 }
#   Monster      - no options
#   Zombie       - STI, inherits from Monster
#   Werewolf     - STI, inherits from Monster

class ActsAsSequencedTest < ActiveSupport::TestCase
  test "default start_at" do
    question = Question.create
    answer = question.answers.create
    assert_equal 1, answer.sequential_id
  end

  test "custom start_at" do
    account = Account.create
    invoice = account.invoices.create
    assert_equal 1000, invoice.sequential_id

    another_invoice = account.invoices.create
    assert_equal 1001, another_invoice.sequential_id
  end

  test "lambda start_at" do
    account = Account.create
    product = Product.create(:account_id => account.id)
    assert_equal 3, product.sequential_id

    another_product = Product.create(:account_id => account.id)
    assert_equal 4, another_product.sequential_id
  end

  test "custom start_at with populated table" do
    account = Account.create
    account.invoices.create(:sequential_id => 1)
    invoice = account.invoices.create
    assert_equal 1000, invoice.sequential_id
  end

  test "sequential id increment" do
    question = Question.create
    question.answers.create(:sequential_id => 10)
    another_answer = question.answers.create
    assert_equal 11, another_answer.sequential_id
  end

  test "default scope" do
    Subscription.create(:sequential_id => 1)
    subscription = Subscription.create
    assert_equal 2, subscription.sequential_id
  end

  test "undefined scope method" do
    account = Account.create
    order = account.orders.build
    assert_raises(NoMethodError) { order.save }
  end

  test "custom sequential id column" do
    account = Account.create
    user = account.users.create
    assert_equal 1, user.custom_sequential_id
  end

  test "sequential id remains on save" do
    question = Question.create
    answer = question.answers.create
    assert_equal 1, answer.sequential_id

    answer.reload
    answer.body = "Updated body"
    answer.save
    assert_equal 1, answer.sequential_id
  end

  test "undefined sequential id column" do
    account = Account.create
    address = account.addresses.build
    assert_raises(NoMethodError) { address.save }
  end

  test "manually setting sequential id" do
    question = Question.create
    answer = question.answers.build(:sequential_id => 10)
    another_answer = question.answers.build(:sequential_id => 10)
    answer.save
    another_answer.save

    assert_equal 10, answer.sequential_id
    assert_equal 10, another_answer.sequential_id
  end

  test "model with a default scope for sorting" do
    question = Question.create
    (1..3).each { |id| question.comments.create(:sequential_id => id) }
    comment = question.comments.create
    assert_equal 4, comment.sequential_id
  end

  test "multi-column scopes" do
    Email.create(:emailable_id => 1, :emailable_type => "User", :sequential_id => 2)
    Email.create(:emailable_id => 1, :emailable_type => "Question", :sequential_id => 3)
    email = Email.create(:emailable_id => 1, :emailable_type => "User")
    assert_equal 3, email.sequential_id
  end

  test "skip option" do
    rating = Rating.create(:comment_id => 1, :score => 1)
    assert_equal 1, rating.sequential_id

    rating = Rating.create(:comment_id => 1, :score => 0)
    assert_equal nil, rating.sequential_id
  end

  test "STI" do
    zombie = Zombie.create
    werewolf = Werewolf.create
    assert_equal 1, zombie.sequential_id
    assert_equal 2, werewolf.sequential_id
  end
end


text:
package monocle.function

import monocle.Lens
import scala.annotation.implicitNotFound

@implicitNotFound("Could not find an instance of Field4[${S},${A}], please check Monocle instance location policy to " +
  "find out which import is necessary")
trait Field4[S, A] extends Serializable {

  /** Creates a Lens from S to it is fourth element */
  def fourth: Lens[S, A]

}

object Field4 extends Field4Functions

trait Field4Functions {

  def fourth[S, A](implicit ev: Field4[S, A]): Lens[S, A] = ev.fourth

}