Related
I need to update a sublist ('items') field ('receivinglocation') on inbound shipment entry.
When i create an inbound shipment from user interface, i need to update all item line locations from a body field ('customlocation').
I've tried to do that throught a Clientscript:
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/currentRecord'],
/**
* #param{currentRecord} currentRecord
*/
function(currentRecord) {
/**
* Validation function to be executed when record is saved.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #returns {boolean} Return true if record is valid
*
* #since 2015.2
*/
function saveRecord(scriptContext) {
try {
var lines = scriptContext.currentRecord.getLineCount({sublistId: 'items'});
var mainLocationValue = scriptContext.currentRecord.getValue({
fieldId: 'customlocation'
});
if (mainLocationValue != '' && mainLocationValue != null) {
for (var i=0; i<lines; i++){
scriptContext.currentRecord.selectLine({sublistId: 'items',line: i});
scriptContext.currentRecord.setCurrentSublistValue({sublistId: 'items',fieldId: 'receivinglocation',value: mainLocationValue,ignoreFieldChange: true});
scriptContext.currentRecord.commitLine({sublistId: 'items'});
}
}
}
catch (e) {
log.debug({
title: 'Error Details',
details: e
})
}
}
return {
saveRecord: saveRecord
};
});
But the error is always the same from the validation form:
"FAILED_FORM_VALIDATION" - "Form validation failed. You cannot submit this record"
Not sure if this is where your error is coming from, but you do have to return true from the saveRecord() function to allow NetSuite to save the record. You can see a note to this effect directly in the JSDoc section of the script you posted:
#returns {boolean} Return true if record is valid
I have a Client Script that is currently working that sets the Service Item of a time tracking sublist line from the 'item' field on a Case Record. This happens on lineInit and works when I add a new line.
The issue I have is I would like it to work on the first line. When the record loads there is a line already populated with some data, but does not have the Service Item filled in.
If anyone could point me in the right direction, I would appreciate it. could this work on pageInit, or fieldChanged - like have the service item update after I update another line field?
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define([],
function() {
/**
* Function to be executed when field is slaved.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
*
* #since 2015.2
*/
function lineInit(scriptContext) {
if (scriptContext.sublistId !== 'timeitem'){
console.log('test3');
return;
}
/* {N/currentRecord.CurrentRecord} */
var rec = scriptContext.currentRecord;
/* {string} The Service Item in the Body, if any */
var bodyItem = rec.getValue({fieldId: 'item'});
/* {string} The class that has been set at the line level, if any */
var lineServiceItem = rec.getCurrentSublistValue({
sublistId: 'timeitem',
fieldId: 'item'
});
/* IF there IS a value at the body level, and there is NOT a value at the line */
if (bodyItem && !lineServiceItem) {
console.log('test4');
/* Set the line value to the body value */
rec.setCurrentSublistValue({
sublistId: 'timeitem',
fieldId: 'item',
value: bodyItem
});
}
}
return {
lineInit: lineInit
};
});
The first line (or current line in edit mode) will have to be populated during pageInit.
function setLineItem(context, isPageInit) {
if (isPageInit || (context.sublistId === 'timeitem')) {
// set value
}
}
function pageInit(context) {
setLineItem(context, true);
}
function lineInit(context) {
setLineItem(context);
}
I would like to have joomla automatically re-index all the articles that are in a certain category (and subcategory) without indexing the rest. I am not looking for a cron job. I want joomla to do this when I hit the indexing button.
In other words, all the articles that are not in that particular category or its child subcategories should not be indexed and thus not show up in the search results.
EDIT (more info): This is for a specific site where I know the category id(s). I should probably also add that it is a bilingual site, the search module is appearing and indexing twice on the same page for each language. I.e. it indexes and appears on the English 'blog' page and the German 'blog' page.
Is this possible or do I need to manually delete the indexed files I don't want to show up?
EDIT: The discover tool is not working, here is the xml file to try and find out why.
<?xml version="1.0" encoding="utf-8"?>
<extension version="3.1" type="plugin" group="finder" method="upgrade">
<name>plg_finder_content</name>
<author>Joomla! Project</author>
<creationDate>August 2011</creationDate>
<copyright>(C) 2005 - 2014 Open Source Matters. All rights reserved.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin#joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.0.0</version>
<description>PLG_FINDER_CONTENT_XML_DESCRIPTION</description>
<scriptfile>script.php</scriptfile>
<files>
<file plugin="content">article_selectedcategories.php</file>
<filename>index.html</filename>
</files>
<languages>
<language tag="en-GB">language/en-GB/en-GB.plg_finder_content.ini</language>
<language tag="en-GB">language/en-GB/en-GB.plg_finder_content.sys.ini</language>
</languages>
</extension>
EDIT The code from the php to find out why it indexes everything and not just category 31.
<?php
/**
* #package Joomla.Plugin
* #subpackage Finder.Content
*
* #copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
* #license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_BASE') or die;
require_once JPATH_ADMINISTRATOR . '/components/com_finder/helpers/indexer/adapter.php';
/**
* Smart Search adapter for com_content.
*
* #package Joomla.Plugin
* #subpackage Finder.Content
* #since 2.5
*/
class PlgFinderContent extends FinderIndexerAdapter
{
/**
* The plugin identifier.
*
* #var string
* #since 2.5
*/
protected $context = 'Content';
/**
* The extension name.
*
* #var string
* #since 2.5
*/
protected $extension = 'com_content';
/**
* The sublayout to use when rendering the results.
*
* #var string
* #since 2.5
*/
protected $layout = 'article';
/**
* The type of content that the adapter indexes.
*
* #var string
* #since 2.5
*/
protected $type_title = 'Article';
/**
* The table name.
*
* #var string
* #since 2.5
*/
protected $table = '#__content';
/**
* Load the language file on instantiation.
*
* #var boolean
* #since 3.1
*/
protected $autoloadLanguage = true;
/**
* Method to update the item link information when the item category is
* changed. This is fired when the item category is published or unpublished
* from the list view.
*
* #param string $extension The extension whose category has been updated.
* #param array $pks A list of primary key ids of the content that has changed state.
* #param integer $value The value of the state that the content has been changed to.
*
* #return void
*
* #since 2.5
*/
public function onFinderCategoryChangeState($extension, $pks, $value)
{
// Make sure we're handling com_content categories.
if ($extension == 'com_content')
{
$this->categoryStateChange($pks, $value);
}
}
/**
* Method to remove the link information for items that have been deleted.
*
* #param string $context The context of the action being performed.
* #param JTable $table A JTable object containing the record to be deleted
*
* #return boolean True on success.
*
* #since 2.5
* #throws Exception on database error.
*/
public function onFinderAfterDelete($context, $table)
{
if ($context == 'com_content.article')
{
$id = $table->id;
}
elseif ($context == 'com_finder.index')
{
$id = $table->link_id;
}
else
{
return true;
}
// Remove item from the index.
return $this->remove($id);
}
/**
* Smart Search after save content method.
* Reindexes the link information for an article that has been saved.
* It also makes adjustments if the access level of an item or the
* category to which it belongs has changed.
*
* #param string $context The context of the content passed to the plugin.
* #param JTable $row A JTable object.
* #param boolean $isNew True if the content has just been created.
*
* #return boolean True on success.
*
* #since 2.5
* #throws Exception on database error.
*/
public function onFinderAfterSave($context, $row, $isNew)
{
// We only want to handle articles here.
if ($context == 'com_content.article' || $context == 'com_content.form')
{
// Check if the access levels are different.
if (!$isNew && $this->old_access != $row->access)
{
// Process the change.
$this->itemAccessChange($row);
}
// Reindex the item.
$this->reindex($row->id);
}
// Check for access changes in the category.
if ($context == 'com_categories.category')
{
// Check if the access levels are different.
if (!$isNew && $this->old_cataccess != $row->access)
{
$this->categoryAccessChange($row);
}
}
return true;
}
/**
* Smart Search before content save method.
* This event is fired before the data is actually saved.
*
* #param string $context The context of the content passed to the plugin.
* #param JTable $row A JTable object.
* #param boolean $isNew If the content is just about to be created.
*
* #return boolean True on success.
*
* #since 2.5
* #throws Exception on database error.
*/
public function onFinderBeforeSave($context, $row, $isNew)
{
// We only want to handle articles here.
if ($context == 'com_content.article' || $context == 'com_content.form')
{
// Query the database for the old access level if the item isn't new.
if (!$isNew)
{
$this->checkItemAccess($row);
}
}
// Check for access levels from the category.
if ($context == 'com_categories.category')
{
// Query the database for the old access level if the item isn't new.
if (!$isNew)
{
$this->checkCategoryAccess($row);
}
}
return true;
}
/**
* Method to update the link information for items that have been changed
* from outside the edit screen. This is fired when the item is published,
* unpublished, archived, or unarchived from the list view.
*
* #param string $context The context for the content passed to the plugin.
* #param array $pks An array of primary key ids of the content that has changed state.
* #param integer $value The value of the state that the content has been changed to.
*
* #return void
*
* #since 2.5
*/
public function onFinderChangeState($context, $pks, $value)
{
// We only want to handle articles here.
if ($context == 'com_content.article' || $context == 'com_content.form')
{
$this->itemStateChange($pks, $value);
}
// Handle when the plugin is disabled.
if ($context == 'com_plugins.plugin' && $value === 0)
{
$this->pluginDisable($pks);
}
}
/**
* Method to index an item. The item must be a FinderIndexerResult object.
*
* #param FinderIndexerResult $item The item to index as an FinderIndexerResult object.
* #param string $format The item format. Not used.
*
* #return void
*
* #since 2.5
* #throws Exception on database error.
*/
protected function index(FinderIndexerResult $item, $format = 'html')
{
$item->setLanguage();
// Check if the extension is enabled.
if (JComponentHelper::isEnabled($this->extension) == false)
{
return;
}
// Initialise the item parameters.
$registry = new JRegistry;
$registry->loadString($item->params);
$item->params = JComponentHelper::getParams('com_content', true);
$item->params->merge($registry);
$registry = new JRegistry;
$registry->loadString($item->metadata);
$item->metadata = $registry;
// Trigger the onContentPrepare event.
$item->summary = FinderIndexerHelper::prepareContent($item->summary, $item->params);
$item->body = FinderIndexerHelper::prepareContent($item->body, $item->params);
// Build the necessary route and path information.
$item->url = $this->getURL($item->id, $this->extension, $this->layout);
$item->route = ContentHelperRoute::getArticleRoute($item->slug, $item->catslug, $item->language);
$item->path = FinderIndexerHelper::getContentPath($item->route);
// Get the menu title if it exists.
$title = $this->getItemMenuTitle($item->url);
// Adjust the title if necessary.
if (!empty($title) && $this->params->get('use_menu_title', true))
{
$item->title = $title;
}
// Add the meta-author.
$item->metaauthor = $item->metadata->get('author');
// Add the meta-data processing instructions.
$item->addInstruction(FinderIndexer::META_CONTEXT, 'metakey');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'metadesc');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'metaauthor');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'author');
$item->addInstruction(FinderIndexer::META_CONTEXT, 'created_by_alias');
// Translate the state. Articles should only be published if the category is published.
$item->state = $this->translateState($item->state, $item->cat_state);
// Add the type taxonomy data.
$item->addTaxonomy('Type', 'Article');
// Add the author taxonomy data.
if (!empty($item->author) || !empty($item->created_by_alias))
{
$item->addTaxonomy('Author', !empty($item->created_by_alias) ? $item->created_by_alias : $item->author);
}
// Add the category taxonomy data.
$item->addTaxonomy('Category', $item->category, $item->cat_state, $item->cat_access);
// Add the language taxonomy data.
$item->addTaxonomy('Language', $item->language);
// Get content extras.
FinderIndexerHelper::getContentExtras($item);
// Index the item.
$this->indexer->index($item);
}
/**
* Method to setup the indexer to be run.
*
* #return boolean True on success.
*
* #since 2.5
*/
protected function setup()
{
// Load dependent classes.
include_once JPATH_SITE . '/components/com_content/helpers/route.php';
return true;
}
/**
* Method to get the SQL query used to retrieve the list of content items.
*
* #param mixed $query A JDatabaseQuery object or null.
*
* #return JDatabaseQuery A database object.
*
* #since 2.5
*/
protected function getListQuery($query = null)
{
$db = JFactory::getDbo();
// Check if we can use the supplied SQL query.
$query = $query instanceof JDatabaseQuery ? $query : $db->getQuery(true)
->select('a.id, a.title, a.alias, a.introtext AS summary, a.fulltext AS body')
->select('a.state, a.catid, a.created AS start_date, a.created_by')
->select('a.created_by_alias, a.modified, a.modified_by, a.attribs AS params')
->select('a.metakey, a.metadesc, a.metadata, a.language, a.access, a.version, a.ordering')
->select('a.publish_up AS publish_start_date, a.publish_down AS publish_end_date')
->select('c.title AS category, c.published AS cat_state, c.access AS cat_access');
$query->where('a.catid = 31');
// Handle the alias CASE WHEN portion of the query
$case_when_item_alias = ' CASE WHEN ';
$case_when_item_alias .= $query->charLength('a.alias', '!=', '0');
$case_when_item_alias .= ' THEN ';
$a_id = $query->castAsChar('a.id');
$case_when_item_alias .= $query->concatenate(array($a_id, 'a.alias'), ':');
$case_when_item_alias .= ' ELSE ';
$case_when_item_alias .= $a_id.' END as slug';
$query->select($case_when_item_alias);
$case_when_category_alias = ' CASE WHEN ';
$case_when_category_alias .= $query->charLength('c.alias', '!=', '0');
$case_when_category_alias .= ' THEN ';
$c_id = $query->castAsChar('c.id');
$case_when_category_alias .= $query->concatenate(array($c_id, 'c.alias'), ':');
$case_when_category_alias .= ' ELSE ';
$case_when_category_alias .= $c_id.' END as catslug';
$query->select($case_when_category_alias)
->select('u.name AS author')
->from('#__content AS a')
->join('LEFT', '#__categories AS c ON c.id = a.catid')
->join('LEFT', '#__users AS u ON u.id = a.created_by');
return $query;
}
}
I added the whole thing, just search for "where" to find the part that I changed. Everything else I did not touch.
Oh I think you are looking at the xml not the php.
// Check if we can use the supplied SQL query.
$query = $query instanceof JDatabaseQuery ? $query : $db->getQuery(true)
->select('a.id, a.title, a.alias, a.introtext AS summary, a.fulltext AS body')
->select('a.state, a.catid, a.created AS start_date, a.created_by')
->select('a.created_by_alias, a.modified, a.modified_by, a.attribs AS params')
->select('a.metakey, a.metadesc, a.metadata, a.language, a.access, a.version, a.ordering')
->select('a.publish_up AS publish_start_date, a.publish_down AS publish_end_date')
->select('c.title AS category, c.published AS cat_state, c.access AS cat_access');
If you want to exclude category 7 add
$query->where('a.catid <> 7');
after the last select.
Update ... or if you only want catgory 7
$query->where('a.catid = 7');
or if you only want category 7 and its children you would use IN ( comma separated list ).
Update
so this is what i'm getting for the generated query
SELECT a.id, a.title, a.alias, a.introtext AS summary, a.fulltext AS body,a.state, a.catid, a.created AS start_date, a.created_by,a.created_by_alias, a.modified, a.modified_by, a.attribs AS params,a.metakey, a.metadesc, a.metadata, a.language, a.access, a.version, a.ordering,a.publish_up AS publish_start_date, a.publish_down AS publish_end_date,c.title AS category, c.published AS cat_state, c.access AS cat_access, CASE WHEN CHAR_LENGTH(a.alias) != 0 THEN CONCAT_WS(':', a.id, a.alias) ELSE a.id END as slug, CASE WHEN CHAR_LENGTH(c.alias) != 0 THEN CONCAT_WS(':', c.id, c.alias) ELSE c.id END as catslug,u.name AS author
FROM a1pbr_content AS a
LEFT JOIN a1pbr_categories AS c ON c.id = a.catid
LEFT JOIN a1pbr_users AS u ON u.id = a.created_by
WHERE a.catid = 31
And that does give me just articles with catid of 31.
WHen you are indexing are you using the index button or doing it on save?
I am just playing around with the microFramework Slim + Twig template Engine. But actually passing an array in render method.
SomeOne help me to solve the error.
running in my local environment using XAMPP
Below is my code in index.php
<?php
/* Require and initialize Slim and Twig */
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
require 'Views/Twig/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$app = new \Slim\Slim(array(
'view' => new \Slim\Extras\Views\Twig(),
'templates.path' => './Templates'
));
/* Application routes */
$app->get('/', function () use($app) {
$pageTitle = 'hello world';
$body = 'sup world';
$app->render('index.php',array('title' => $pageTitle, 'body' => $body));
});
/* Run the application */
$app->run();
and below is the error am getting
Slim Application Error
The application could not run because of the following error:
Details
Type: ErrorException
Code: 4096
Message: Argument 1 passed to Twig_Template::render() must be an array, object given, called in C:\xampp\htdocs\app\Slim\Extras\Views\Twig.php on line 99 and defined
File: C:\xampp\htdocs\app\Views\Twig\lib\Twig\Template.php
Line: 244
Trace
Your resolution is an overhead. Better way is to pass $this->all(), which will return an array of set's key-value data, as an argument for $template->render() method.
public function render($template)
{
$env = $this->getEnvironment();
$template = $env->loadTemplate($template);
return $template->render($this->all());
}
Before April $this->data was an array but now it is a Set so that's why your problem occurred.
Unfortunately i have to answer my question.
I have just did a little hack in the Twig.php(in slim extras) and introduced a new function called ObjectToArray and now it works
function objectToArray( $object ) {
$array=array();
foreach($object as $member=>$data)
{
$array[$member]=$data;
}
return $array;
}
so my custom Twig.php files looks something like this
<?php
/**
* Slim - a micro PHP 5 framework
*
* #author Josh Lockhart
* #link http://www.slimframework.com
* #copyright 2011 Josh Lockhart
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Extras\Views;
/**
* TwigView
*
* The TwigView is a custom View class that renders templates using the Twig
* template language (http://www.twig-project.org/).
*
* Two fields that you, the developer, will need to change are:
* - twigDirectory
* - twigOptions
*/
class Twig extends \Slim\View
{
/**
* #var string The path to the Twig code directory WITHOUT the trailing slash
*/
public static $twigDirectory = null;
/**
* #var array Paths to directories to attempt to load Twig template from
*/
public static $twigTemplateDirs = array();
/**
* #var array The options for the Twig environment, see
* http://www.twig-project.org/book/03-Twig-for-Developers
*/
public static $twigOptions = array();
/**
* #var TwigExtension The Twig extensions you want to load
*/
public static $twigExtensions = array();
/**
* #var TwigEnvironment The Twig environment for rendering templates.
*/
private $twigEnvironment = null;
/**
* Get a list of template directories
*
* Returns an array of templates defined by self::$twigTemplateDirs, falls
* back to Slim\View's built-in getTemplatesDirectory method.
*
* #return array
**/
private function getTemplateDirs()
{
if (empty(self::$twigTemplateDirs)) {
return array($this->getTemplatesDirectory());
}
return self::$twigTemplateDirs;
}
/**
* Render Twig Template
*
* This method will output the rendered template content
*
* #param string $template The path to the Twig template, relative to the Twig templates directory.
* #return void
*/
public function render($template)
{
$env = $this->getEnvironment();
$template = $env->loadTemplate($template);
return $template->render($this->objectToArray($this->data));
}
/**
* Creates new TwigEnvironment if it doesn't already exist, and returns it.
*
* #return Twig_Environment
*/
public function getEnvironment()
{
if (!$this->twigEnvironment) {
// Check for Composer Package Autoloader class loading
if (!class_exists('\Twig_Autoloader')) {
require_once self::$twigDirectory . '/Autoloader.php';
}
\Twig_Autoloader::register();
$loader = new \Twig_Loader_Filesystem($this->getTemplateDirs());
$this->twigEnvironment = new \Twig_Environment(
$loader,
self::$twigOptions
);
// Check for Composer Package Autoloader class loading
if (!class_exists('\Twig_Extensions_Autoloader')) {
$extension_autoloader = dirname(__FILE__) . '/Extension/TwigAutoloader.php';
if (file_exists($extension_autoloader)) require_once $extension_autoloader;
}
if (class_exists('\Twig_Extensions_Autoloader')) {
\Twig_Extensions_Autoloader::register();
foreach (self::$twigExtensions as $ext) {
$extension = is_object($ext) ? $ext : new $ext;
$this->twigEnvironment->addExtension($extension);
}
}
}
return $this->twigEnvironment;
}
/**
* Converts PHP objects into Array.
*
* #return array
*/
private function objectToArray( $object ) {
$array=array();
foreach($object as $member=>$data)
{
$array[$member]=$data;
}
return $array;
}
}
I think it should work without the above modification.
If you do have find any better solution , let me know
This will solve the problem!
function objectToArray($object){
if (!is_object($object) && !is_array($object)) {
return $object;
}
if (is_object($object)) {
$object = get_object_vars($object);
}
return array_map('objectToArray', $object);
}
I have a form build from the kohana framework which should send an email.
when I press the "send" button, everything seems to work fine...no error messages...but no email turns up!
If I can´t see anything in firebug..where can I look?
the application logs state
"error: Missing i18n entry contact.captcha.valid for language en_US"
but I don´t know how to get to the bottom of the problem..any help welcome..
yours,
Rob
Ill try and find out which version im using.....the application is the latest version of Ushahidi (2.2.1) www.ushahidi.com
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha library.
*
* $Id: Captcha.php 3917 2009-01-21 03:06:22Z zombor $
*
* #package Captcha
* #author Kohana Team
* #copyright (c) 2007-2008 Kohana Team
* #license http://kohanaphp.com/license.html
*/
class Captcha_Core {
// Captcha singleton
protected static $instance;
// Style-dependent Captcha driver
protected $driver;
// Config values
public static $config = array
(
'style' => 'basic',
'width' => 150,
'height' => 50,
'complexity' => 4,
'background' => '',
'fontpath' => '',
'fonts' => array(),
'promote' => FALSE,
);
/**
* Singleton instance of Captcha.
*
* #return object
*/
public static function instance()
{
// Create the instance if it does not exist
empty(self::$instance) and new Captcha;
return self::$instance;
}
/**
* Constructs and returns a new Captcha object.
*
* #param string config group name
* #return object
*/
public static function factory($group = NULL)
{
return new Captcha($group);
}
/**
* Constructs a new Captcha object.
*
* #throws Kohana_Exception
* #param string config group name
* #return void
*/
public function __construct($group = NULL)
{
// Create a singleton instance once
empty(self::$instance) and self::$instance = $this;
// No config group name given
if ( ! is_string($group))
{
$group = 'default';
}
// Load and validate config group
if ( ! is_array($config = Kohana::config('captcha.'.$group)))
throw new Kohana_Exception('captcha.undefined_group', $group);
// All captcha config groups inherit default config group
if ($group !== 'default')
{
// Load and validate default config group
if ( ! is_array($default = Kohana::config('captcha.default')))
throw new Kohana_Exception('captcha.undefined_group', 'default');
// Merge config group with default config group
$config += $default;
}
// Assign config values to the object
foreach ($config as $key => $value)
{
if (array_key_exists($key, self::$config))
{
self::$config[$key] = $value;
}
}
// Store the config group name as well, so the drivers can access it
self::$config['group'] = $group;
// If using a background image, check if it exists
if ( ! empty($config['background']))
{
self::$config['background'] = str_replace('\\', '/', realpath($config['background']));
if ( ! is_file(self::$config['background']))
throw new Kohana_Exception('captcha.file_not_found', self::$config['background']);
}
// If using any fonts, check if they exist
if ( ! empty($config['fonts']))
{
self::$config['fontpath'] = str_replace('\\', '/', realpath($config['fontpath'])).'/';
foreach ($config['fonts'] as $font)
{
if ( ! is_file(self::$config['fontpath'].$font))
throw new Kohana_Exception('captcha.file_not_found', self::$config['fontpath'].$font);
}
}
// Set driver name
$driver = 'Captcha_'.ucfirst($config['style']).'_Driver';
// Load the driver
if ( ! Kohana::auto_load($driver))
throw new Kohana_Exception('core.driver_not_found', $config['style'], get_class($this));
// Initialize the driver
$this->driver = new $driver;
// Validate the driver
if ( ! ($this->driver instanceof Captcha_Driver))
throw new Kohana_Exception('core.driver_implements', $config['style'], get_class($this), 'Captcha_Driver');
Kohana::log('debug', 'Captcha Library initialized');
}
/**
* Validates a Captcha response and updates response counter.
*
* #param string captcha response
* #return boolean
*/
public static function valid($response)
{
// Maximum one count per page load
static $counted;
// User has been promoted, always TRUE and don't count anymore
if (self::instance()->promoted())
return TRUE;
// Challenge result
$result = (bool) self::instance()->driver->valid($response);
// Increment response counter
if ($counted !== TRUE)
{
$counted = TRUE;
// Valid response
if ($result === TRUE)
{
self::instance()->valid_count(Session::instance()->get('captcha_valid_count') + 1);
}
// Invalid response
else
{
self::instance()->invalid_count(Session::instance()->get('captcha_invalid_count') + 1);
}
}
return $result;
}
/**
* Gets or sets the number of valid Captcha responses for this session.
*
* #param integer new counter value
* #param boolean trigger invalid counter (for internal use only)
* #return integer counter value
*/
public function valid_count($new_count = NULL, $invalid = FALSE)
{
// Pick the right session to use
$session = ($invalid === TRUE) ? 'captcha_invalid_count' : 'captcha_valid_count';
// Update counter
if ($new_count !== NULL)
{
$new_count = (int) $new_count;
// Reset counter = delete session
if ($new_count < 1)
{
Session::instance()->delete($session);
}
// Set counter to new value
else
{
Session::instance()->set($session, (int) $new_count);
}
// Return new count
return (int) $new_count;
}
// Return current count
return (int) Session::instance()->get($session);
}
/**
* Gets or sets the number of invalid Captcha responses for this session.
*
* #param integer new counter value
* #return integer counter value
*/
public function invalid_count($new_count = NULL)
{
return $this->valid_count($new_count, TRUE);
}
/**
* Resets the Captcha response counters and removes the count sessions.
*
* #return void
*/
public function reset_count()
{
$this->valid_count(0);
$this->valid_count(0, TRUE);
}
/**
* Checks whether user has been promoted after having given enough valid responses.
*
* #param integer valid response count threshold
* #return boolean
*/
public function promoted($threshold = NULL)
{
// Promotion has been disabled
if (self::$config['promote'] === FALSE)
return FALSE;
// Use the config threshold
if ($threshold === NULL)
{
$threshold = self::$config['promote'];
}
// Compare the valid response count to the threshold
return ($this->valid_count() >= $threshold);
}
/**
* Returns or outputs the Captcha challenge.
*
* #param boolean TRUE to output html, e.g. <img src="#" />
* #return mixed html string or void
*/
public function render($html = TRUE)
{
return $this->driver->render($html);
}
/**
* Magically outputs the Captcha challenge.
*
* #return mixed
*/
public function __toString()
{
return $this->render();
}
} // End Captcha Class
From the little details provided I think your site uses the I18n library from Kohana for internationalization of the site. The other option is your site use Kohana messages for showing form errors.
The Validation class uses the __() function internally when generation validation errors. I think you don't have a message for key valid specified in APPPATH/messages/contact/captcha.php.
You should try to investigate more on how the form is processed and whether some validation errors are being generated. There might be an error in the captcha validation and despite it seem there is no error, there might be and just the error message could not be shown.
class Contact_Controller extends Main_Controller
{
function __construct()
{
parent::__construct();
}
public function index()
{
$this->template->header->this_page = 'contact';
$this->template->content = new View('contact');
$this->template->header->page_title .= Kohana::lang('ui_main.contact').Kohana::config('settings.title_delimiter');
// Setup and initialize form field names
$form = array (
'contact_name' => '',
'contact_email' => '',
'contact_phone' => '',
'contact_subject' => '',
'contact_message' => '',
'captcha' => ''
);
// Copy the form as errors, so the errors will be stored with keys
// corresponding to the form field names
$captcha = Captcha::factory();
$errors = $form;
$form_error = FALSE;
$form_sent = FALSE;
// Check, has the form been submitted, if so, setup validation
if ($_POST)
{
// Instantiate Validation, use $post, so we don't overwrite $_POST fields with our own things
$post = Validation::factory($_POST);
// Add some filters
$post->pre_filter('trim', TRUE);
// Add some rules, the input field, followed by a list of checks, carried out in order
$post->add_rules('contact_name', 'required', 'length[3,100]');
$post->add_rules('contact_email', 'required','email', 'length[4,100]');
$post->add_rules('contact_subject', 'required', 'length[3,100]');
$post->add_rules('contact_message', 'required');
$post->add_rules('captcha', 'required', 'Captcha::valid');
// Test to see if things passed the rule checks
if ($post->validate())
{
// Yes! everything is valid - Send email
$site_email = Kohana::config('settings.site_email');
$message = Kohana::lang('ui_admin.sender').": " . $post->contact_name . "\n";
$message .= Kohana::lang('ui_admin.email').": " . $post->contact_email . "\n";
$message .= Kohana::lang('ui_admin.phone').": " . $post->contact_phone . "\n\n";
$message .= Kohana::lang('ui_admin.message').": \n" . $post->contact_message . "\n\n\n";
$message .= "~~~~~~~~~~~~~~~~~~~~~~\n";
$message .= Kohana::lang('ui_admin.sent_from_website'). url::base();
// Send Admin Message
email::send( $site_email, $post->contact_email, $post->contact_subject, $message, FALSE );
$form_sent = TRUE;
}
// No! We have validation errors, we need to show the form again, with the errors
else
{
// repopulate the form fields
$form = arr::overwrite($form, $post->as_array());
// populate the error fields, if any
$errors = arr::overwrite($errors, $post->errors('contact'));
$form_error = TRUE;
}
}
$this->template->content->form = $form;
$this->template->content->errors = $errors;
$this->template->content->form_error = $form_error;
$this->template->content->form_sent = $form_sent;
$this->template->content->captcha = $captcha;
// Rebuild Header Block
$this->template->header->header_block = $this->themes->header_block();
$this->template->footer->footer_block = $this->themes->footer_block();
}
}
My problem has been solved!
My Email server was not set up properly. I resolved the postfix issue and now the comments form is working as it should,
thanks to all who answered,
Rob