I am using "wForm" extension for forms. I want to add "captcha" in my form.
I have tried "myCaptcha" component for "cForm".
http://www.yiiframework.com/wiki/733/how-to-show-a-captcha-in-cform/
but i am getting the following error
"WForm" and its behaviors do not have a method or closure named "MyCaptcha".
how can i use the "cform captcha in wForm"?
Download the extension cCaptcha extension
1) Unzip CaptchaExtended.zip files into ../protected/extensions/captchaExtended/.
2) Register class paths to CaptchaExtendedAction and CaptchaExtendedValidator, e.g. in components/controller.php:
public function init(){
// register class paths for extension captcha extended
Yii::$classMap = array_merge( Yii::$classMap, array(
'CaptchaExtendedAction' => Yii::getPathOfAlias('ext.captchaExtended').DIRECTORY_SEPARATOR.'CaptchaExtendedAction.php',
'CaptchaExtendedValidator' => Yii::getPathOfAlias('ext.captchaExtended').DIRECTORY_SEPARATOR.'CaptchaExtendedValidator.php'
));
}
3) Define action in controller, e.g. SiteController:
public function actions(){
return array(
'captcha'=>array(
'class'=>'CaptchaExtendedAction',
// if needed, modify settings
'mode'=>CaptchaExtendedAction::MODE_MATH,
),
);
}
4) Define client validation in model::rules():
public function rules(){
return array(
array('verifyCode', 'CaptchaExtendedValidator', 'allowEmpty'=>!CCaptcha::checkRequirements()),
);
}
5) add the following in your view file (in your form)
$this->widget('CCaptcha'); //for captch image
echo CHtml::activeTextField($model,'verifyCode'); //text field to enter captcha text
Related
I want to create a CakePHP Widget in order to create a custom form control. The end goal is to make it a plugin, but for now I am trying to determine the general structure of a Widget. I have created a file in src/View/Widget/DateTimeWidget.php containing
<?php
namespace App\View\Widget;
use Cake\View\Form\ContextInterface;
use Cake\View\Widget\WidgetInterface;
class DateTimeWidget implements WidgetInterface
{
protected $_templates;
public function __construct($templates)
{
$this->_templates = $templates;
}
public function render(array $data, ContextInterface $context)
{
$data += [
'name' => '',
];
return $this->_templates->format('DateTime', [
'name' => $data['name'],
'attrs' => $this->_templates->formatAttributes($data, ['name'])
]);
}
public function secureFields(array $data)
{
return [$data['name']];
}
}
?>
I load the Widget in a View with the code
$this->Form->addWidget(
'datetime',
['DateTime']
);
and then create a form control with it using
echo $this->Form->control('end_time', ['type' => 'datetime']);
However, I get the error Cannot find template named 'DateTime'.
I have created the basic template code
<?php
$this->Form->setTemplates([
'DateTime' => '<p {{attrs}}>Test template</p>'
]);
But I have no idea where in the folder structure to put it? In most plugins I have looked at it is in a helper file, but I wonder if this is the default way to do it? What are my options? And how do i tell CakePHP to load it? What is the preferred way of doing this?
Thank you!
If you want your widget to come with default string templates, then you could for example define them in the widget itself, by adding it to the string template instance that is being passed to the widget's constructor. You'd do it in the widget's render() method though, it wouldn't work properly in the constructor, as widget instances are being reused, ie they are only being constructed once, for example:
public function render(array $data, ContextInterface $context)
{
if (!array_key_exists('customDateTime', $this->_templates->getConfig())) {
$this->_templates->add([
'customDateTime' => '<p {{attrs}}>Test template</p>',
// ...
]);
}
// ...
}
Another option is to put the string templates in a config file:
// in path_to_your_plugin/config/form_helper_templates.php
<?php
return [
'customDateTime' => '<p {{attrs}}>Test template</p>',
// ...
];
and ask the users to load the form helper string templates in their view templates when they want to use your widgets:
$this->Form->templater()->load('YourPluginName.form_helper_templates');
Both options will integrate properly with the form helper, so that users can still override the templates by setting custom templates either via FormHelper::setTemplates(), StringTemplate::load()/add(), or the templates option for FormHelper::control().
I think you should use Cells for it.
Take a look at: https://book.cakephp.org/3/en/views/cells.html
is there any posibillity to access a controller function from a php file that is based in resource/public/php/file.php
What I want is this php file is special file I use it for this:
<img src="file.php"></img>
I will disable readable paths. So this php file does some encryption and need a connection to a normal controller function.
thanks
is there any posibillity to access a controller function from a php file that is based in resource/public/php/file.php
Yes, it is possible but therefor you need to bootstrap the TYPO3 core as well. Or if it is a static and public method than you can call it directly.
But this seems not the right way to do it in your case.
Assuming you're working on some kind of captcha thing you should consider your own page type for rendering the dynamic images. Here's a working example:
TypoScript Setup
In TypoScript we're registering our own page typ and pointing it out to our extension, controller and action:
DynamicCaptchaImage = PAGE
DynamicCaptchaImage {
typeNum = 1234
10 = USER_INT
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
pluginName = Pi1
extensionName = MyExtName
vendorName = MyCompanyName
controller = MyExtbaseController
action = renderCaptchaImage
# view =< plugin.tx_myextname.view // you provide the view yourself
# persistence =< plugin.tx_myextname.persistence // in case you need a repository you should uncomment it
settings =< plugin.tx_myextname.settings
}
config {
disableAllHeaderCode = 1
additionalHeaders = Content-Type: image/png
xhtml_cleaning = 0
admPanel = 0
debug = 0
}
}
See also: Registering a custom typeNum-based Controller access
Controller
Here's an example how your controller and action should look like:
<?php
namespace MyCompanyName\MyExtName\Controller;
/**
* MyExtbaseController
*/
class MyExtbaseController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* Render Captcha Image Action
*
* #return void
*/
public function renderCaptchaImageAction() {
// Send some headers
header('Content-Type: image/png');
// < do your magic stuff here >
// Breaks the script because we've sent already some headers and want
// to prevent that TYPO3 is adding another stuff (eg. for debugging purposes)
// that can break the image from loading.
// return FALSE; does not stop doing that!
exit;
}
}
See also: Extbase wiki
Accessing the controller
Now we've configured the custom page type we're allowed to access the controller by calling the page type given in the TypoScript setup.
Eg. http://www.example.com?type=1234 points out to the renderCaptchaImageAction() in the MyExtbaseController.
Fluid
In Fluid you can link to the page type you've configured by:
<img src="{f:link.page(pageType: 1234)}" />
See also: Fluid wiki
Realurl
If you're using the extension realurl you can change ?type=1234 to captcha.png by:
// [...]
'fileName' => array(
'index' => array(
'captcha.png' => array(
'keyValues' => array(
'type' => 1234,
),
),
),
),
// [...]
See also: Realurl wiki
We have built out our own version of the tinyMCE editor in SilverStripe. The only issue is that you need to hit refresh for our custom configuration to be loaded. Once it has been refreshed once, it sticks for the rest of the session.
Our set up is as follows:
BolierplateWYSIWYG.php
class BolierplateWYSIWYG extends Extension {
protected function defaults() {
$defaultEditorConfig = HtmlEditorConfig::get('cms');
$defaultEditorConfig->setOptions(
array(
'theme' => 'advanced',
'priority' => 1,
// More config options
)
);
return HtmlEditorConfig::get('cms');
}
public function getConfig() {
return $this->defaults();
}
}
Then, inside of Page.php we have the following:
... page functions ...
public function getCMSFields() {
$fields = parent::getCMSFields();
// Update WYSIWYG
$digital360Wysiwyg = new Digital360WYSIWYG;
$digital360Wysiwyg->getConfig();
... Page CMS configuration ...
Inside of our boilplate.yml we have:
HtmlEditorField:
extensions:
- BolierplateWYSIWYG
How do I get this new configuration to load without requiring a page refresh?
Like #assertchris mention, my PR https://github.com/silverstripe/silverstripe-framework/pull/4259/files has now been merge so you can easily have multiple TinyMCE configs which should help you with you extension.
Setup your HTMLEditorConfig in _config.php like
HtmlEditorConfig::get('default')->setOptions....
HtmlEditorConfig::get('fancy')->setOptions....
Since you have to have an extension, you could have something like:
class BolierplateWYSIWYG extends Extension {
public function setEditorConfig($name = 'default')
{
HtmlEditorConfig::set_active($name);
}
}
The you can use it like this when setting up your CMS fields
$digital360Wysiwyg = new Digital360WYSIWYG;
$digital360Wysiwyg->setEditorConfig();
or
$digital360Wysiwyg = new Digital360WYSIWYG;
$digital360Wysiwyg->setEditorConfig('fancy');
This should work fine. Although be careful when changing some of the editor options like mode, as this can cause your refresh issue. an you shouldn't have to change theme or priority?
Might want to check this pull request: https://github.com/silverstripe/silverstripe-framework/pull/4259
You can customise your HtmlEditorField by calling setOptions in your _mysite/config.php:
HtmlEditorConfig::get('cms')->setOptions(
array(
'theme' => 'advanced',
'priority' => 1,
// More config options
)
);
This will work without the need to refresh a CMS page.
I've created a new form element class for a special, complex purpose (text input field with an add-on button to open a "search wizard" popup).
To render this element properly, I've also created a form view helper. Everything works and is fine so far.
However, if I try to render the form using the FormCollection view helper, the element is rendered as a basic input element. That's because the FormElement view helper, which the FormCollection helper relies on, uses a hard-coded series of if clauses to map the element's type to a specific form view helper. It can't map my element's class and thus falls back to FormInput.
I.e. (taken from Zend/Form/View/Helper/FormElement.php, line 41-49):
if ($element instanceof Element\Button) {
$helper = $renderer->plugin('form_button');
return $helper($element);
}
if ($element instanceof Element\Captcha) {
$helper = $renderer->plugin('form_captcha');
return $helper($element);
}
...
$helper = $renderer->plugin('form_input');
return $helper($element);
and so on.
I've got a little stuck here because this architecture doesn't really promote extensibility.
The only solution that came to my mind (except rendering the form by hand) is to extend the FormElement view helper class and thus create my own CustomFormElement view helper. However, because of its complexity, I've put the custom element into an own module. So I'd have to write this CustomFormElement helper dynamically to add custom elements from any module. I don't think this is a recommended procedure.
Is there another solution or is maybe even my complete approach unrecommended? Thanks in advance!
I think the simplest way is to extend Zend\Form\View\Helper\FormElement, handle your field types in your render() method and register your FormElement as default FormElement for your application/module. Assuming that you have your custom TestField that you would like to render:
namespace Application\Form\View\Helper;
use \Zend\Form\ElementInterface;
use \Zend\Form\View\Helper\FormElement
use \Application\Form\Element\TestField;
class MyFormElement extends FormElement
{
public function render(ElementInterface $element)
{
$renderer = $this->getView();
if (!method_exists($renderer, 'plugin')) {
// Bail early if renderer is not pluggable
return '';
}
//your custom fields go here...
if ($element instanceof TestField) {
$helper = $renderer->plugin('\Application\Form\View\Helper\FormTestField');
return $helper($element);
}
return parent::render($element);
}
}
And in Application/config/module.config.php:
'view_helpers' => array(
'invokables' => array(
'form_element' => 'Application\Form\View\Helper\MyFormElement',
)
)
Get your hands on the FormElement view helper any way you can and addType to overwrite the view helper used. i.e. in view, just before you render your form:
<?php $this->plugin('FormElement')->addType('text', 'formcustom'); ?>
This will overwrite the view helper used in the FormRow,FormCollection helpers using your view helper by the key name:
in your config
'view_helpers' => array(
'invokables' => array(
'formcustom' => 'Application\Form\View\Helper\FormCustom',
)
),
When this question was asked the method may not have been there. But it is now.
The following is what I've done and feels like the right level of keeping things separate and neat.
Given:
A new element: MyModule\Form\MyElement which extends Zend\Form\Element
A new view helper class for MyElement: MyModule\Form\View\Helper\FormMyElement which extends Zend\Form\View\Helper\AbstractHelper
Here's how you register your view helper to be used to render your element by adding the following to module.config.php:
'view_helpers' => array(
'invokables'=> array(
'formMyElement' => 'MyModule\Form\View\Helper\FormMyElement',
),
'factories' => array(
'formElement' => function($sm) {
$helper = new \Zend\Form\View\Helper\FormElement();
$helper->addClass('MyModule\Form\MyElement', 'formMyElement');
return $helper;
}
),
),
The key is that you are providing a new factory method for FormElement that still creates the same, standard class (no need to override it), but also calls the addClass method to register your custom helper as the proper helper for your custom element. If you don't want to make the short-name for your helper, you can drop the invokables section and put the FQCN in the addClass call, but I like having the short name available.
This is the best method I've found so far. Ideally, you wouldn't have to take over the construction of the FormElement and could just modify a config that gets passed to it. The downside of this approach is that if you have multiple modules that define custom form elements they are going to clash if they all try to re-define the FormElement factory. You can't specify additions in multiple modules this way. So, if someone finds a better config that can be set that simply gets passed to the FormElement::addClass() method, please let me know.
BTW, I found this page which doesn't address the view helper side of the equation, but talks about registering new form element classes and how to over-ride the built in classes: http://framework.zend.com/manual/current/en/modules/zend.form.advanced-use-of-forms.html
----custom form element-----
namespace App\Form\View\Helper;
use Zend\Form\View\Helper\FormElement as ZendFormElement;
/**
* Description of FormElement
*/
class FormElement
extends ZendFormElement
{
public function addTypes(array $types)
{
foreach ($types as $type => $plugin) {
$this->addType($type, $plugin);
}
}
}
---- application module.config.php--------------
//..........
'view_helpers' => array(
'invokables' => array(
'formRTE' => 'App\Form\View\Helper\FormRTE',
),
'factories' => array(
'formElement' => function($sm) {
$helper = new App\Form\View\Helper\FormElement();
$helper->addTypes(array(
'rte' => 'formRTE',
));
return $helper;
}
),
),
//.........
Seems like we're both running into Form issues with Zend. I think that it could be better integrated with the whole MVC structure.
I think that your approach is sound. What I might think of doing is the following
Give your elements a variable named helper like in ZF1.
Create the custom form element renderer that will ALSO check the renderer attribute of a form element to decide on how to render it.
You could re-use the ViewHelperProviderInterface or create your own interface:
class CustomElement implements ViewHelperProviderInterface
{
public function getViewHelperConfig()
{
return array('type' => '\My\View\Helper');
}
}
or
class CustomElement implements FormHelperProviderInterface
{
public function getFormHelperConfig()
{
return '\My\View\Helper';
// or
return new My\View\Helper();
}
}
Then in your FormElement class you can do the following:
if ('week' == $type) {
$helper = $renderer->plugin('form_week');
return $helper($element);
}
if ($element instanceof THEINTERFACE) {
return $renderer->plugin($element->getFormHelperConfig());
}
$helper = $renderer->plugin('form_input');
return $helper($element);
This is probably what you had in mind anyway.
You'd probably be better off creating your own interface since the first one already has some sort of meaning behind it and it might confuse someone.
Aside from that, each module would then ONLY have to provide a helper_map key in the module configuration to have it's view helpers available during rendering with the MVC components.
Users from the backend application can upload files and publish them to the frontend. Using sfWidgetFormInputFile and sfValidatorFile, I would like to keep the original filename instead of the default functionality of a random string (i.e. Meaningful_filename.docx instead of a4b25e9f48cfb6268f34b691fc18cd76fefe96b5.docx - numbers can be appended onto duplicate names). This can be useful in scenarios where the user downloads several files and would not be able to tell them apart from the file name.
$this->widgetSchema['file_name'] = new sfWidgetFormInputFile(array('label' => 'File'));
$this->validatorSchema['file_name'] = new sfValidatorFile(array(
'required' => true,
'path' => sfConfig::get('sf_upload_dir').DIRECTORY_SEPARATOR.sfConfig::get('app_dir_file_sharing').DIRECTORY_SEPARATOR,
'mime_types' => array('application/msword',
'application/vnd.ms-word',
'application/msword',
'application/msword; charset=binary')
), array(
'invalid' => 'Invalid file.',
'required' => 'Select a file to upload.',
'mime_types' => 'The file must be a supported type.'
));
Is there native functionality in the sfWidgetFormInputFile widget or is there another solution to this?
You get the file by calling $form["file_name"]->getValue(). This gives you an object of class sfValidatedFile where you can call the method getOriginalName().
To define how the file should be save you can do this:
The sfValidatorFile class accepts an option which sfValidatedFile class to use:
validated_file_class: Name of the class that manages the cleaned uploaded file (optional)
The sfValidatedFile class has a method save that calls a method generateFileName. Subclass this class and overwrite this method:
class CustomValidatedFile extends sfValidatedFile {
/**
* Generates a random filename for the current file.
*
* #return string A random name to represent the current file
*/
public function generateFilename()
{
return 'foo bar'// your custom generated file name;
}
}
Here is the function from the original class:
public function generateFilename()
{
return sha1($this->getOriginalName().rand(11111, 99999)).$this->getExtension($this->getOriginalExtension());
}
Then you set up the validator this way:
$this->validatorSchema['file_name'] = new sfValidatorFile(array(
'required' => true,
'path' => 'yourpath',
'validated_file_class' => 'CustomValidatedFile',
'mime_types' => array('application/msword',
'application/vnd.ms-word',
'application/msword',
'application/msword; charset=binary')
),
array('invalid' => 'Invalid file.',
'required' => 'Select a file to upload.',
'mime_types' => 'The file must be a supported type.')
);
Hope that helps!
After some research:
While you can extend sfValidatedFile and override generateFilename I found out that sfFormPropel checks for the existence of a method based on the column name for the model to name the file.
From symfony/plugins/sfPropelPlugin/lib/form line 292:
$method = sprintf('generate%sFilename', $column);
if (null !== $filename)
{
return $file->save($filename);
}
else if (method_exists($this, $method))
{
return $file->save($this->$method($file));
}
Therefore, if your column is called file_name, the method looks for the existence of generateFileNameFilename in the form class. This way you only have to add one method to your form class, rather than extending the sfValidatedFile widget. For instance, my function uses the original name if it is not taken, otherwise appends a sequential number (one method is to recursively check the generated filename):
public function generateFileNameFilename($file = null)
{
if (null === $file) {
// use a random filename instead
return null;
}
if (file_exists($file->getpath().$file->getOriginalName())) {
return $this->appendToName($file);
}
return $file->getOriginalName();
}
public function appendToName($file, $index = 0)
{
$newname = pathinfo($file->getOriginalName(), PATHINFO_FILENAME).$index.$file->getExtension();
if (file_exists($file->getpath().$newname)) {
return $this->appendToName($file, ++$index);
} else {
return $newname;
}
}
I can't find this documented in the symfony API anywhere which is why it took some searching the code base to find. If you are using this method in many places, extending sfValidatedFile might be a good option too.
According to the Symfony documentation "The sfValidatorFile validator validates an uploaded file. The validator converts the uploaded file to an instance of the sfValidatedFile class, or of the validated_file_class option if it is set." (Source: http://www.symfony-project.org/forms/1_4/en/B-Validators#chapter_b_sub_sfvalidatorfile)
Although the sfValidatedFile class renames files right out of the box, you can override this function by setting the validated_file_class to a custom class, and extending sfValidatedFile.
In your custom validated file class, pass your custom filename to the save() method. "If you don't pass a file name, it will be generated by the generateFilename method." (Source:
http://www.symfony-project.org/api/1_4/sfValidatedFile#method_save)
Here's one way you could do it (Source: http://forum.symfony-project.org/index.php/m/90887/#msg_90887)...
A custom validated file class:
// lib/validator/myValidatedFile.php
class myValidatedFile extends sfValidatedFile {
private $savedFilename = null;
// Override sfValidatedFile's save method
public function save($file = null, $fileMode = 0666, $create = true, $dirMode = 0777) {
// This makes sure we use only one savedFilename (it will be the first)
if ($this->savedFilename === null) $this->savedFilename = $file;
// Let the original save method do its magic :)
return parent::save($this->savedFilename, $fileMode, $create, $dirMode);
}
}
Make sure to set 'validated_file_class' => 'myValidatedFile' for the sfWidgetFormInputFile. And to set the logic for what the file name is going to be in Form's save method.