пятница, 14 ноября 2008 г.

Symfony 1.2: file upload in admin

Это черновик

/backend/modules/author/lib/form/myAuthorForm.class.php

/**
* Author form.
*
* @package any
* @subpackage form
* @author Your name here
* @version SVN: $Id: sfPropelFormTemplate.php 10377 2008-07-21 07:10:32Z dwhittle $
*/
class myAuthorForm extends BaseAuthorForm
{
public function configure()
{
$this->setWidget('description', new myWidgetFormRichTextarea(array('editor'=>'fck')));
$values = $this->getValues();
$this->setWidget('pic', new myWidgetFormInputFileEditable(
array(
'is_image' => true,
'delete_label' => 'удалить'
)));
//$this->setWidget('pic', new sfWidgetFormInputFile());
$this->setWidget('avatar', new sfWidgetFormInputFile());
/* VALIDATORS */
$this->setValidators(array(
'f_name' => new sfValidatorString(
array(),
array('required' => 'Обязательное поле')
),
'l_name' => new sfValidatorString(
array(),
array('required' => 'Обязательное поле')
),
'email' => new sfValidatorEmail(
$options = array(),
$messages = array(
'invalid' => 'Введите правильный Email',
'required' => false)
),
'pic' => new sfValidatorFile(array(
'required' => false,
'max_size' => '102400', // bytes (1MB)
'mime_types' => array('image/png', 'image/jpeg',)
)),
));

//$this->widgetSchema->setNameFormat('%s');

$this->validatorSchema->setOption('allow_extra_fields', true);
$this->validatorSchema->setOption('filter_extra_fields', false);
}//configure

public function updateObject($values = null)
{
$object = parent::updateObject();
$path = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.
sfConfig::get('sf_web_dir_name').DIRECTORY_SEPARATOR.sfConfig::get('sf_upload_dir');
$object->setPic(str_replace($path.'/', '', $object->getPic()));
return $object;
}
/* do save*/
protected function doSave($con = null){
$values = $this->getValues();

$path = sfConfig::get('sf_root_dir').'/'.
sfConfig::get('sf_web_dir_name').'/'.sfConfig::get('sf_upload_dir');

if (isset($values['pic_delete'])){
$currentFile = $path.'/'.$this->getObject()->getPic();
if (is_file($currentFile)){
unlink($currentFile);
}
$this->getObject()->setPic('');
}

$file = $values['pic'];

if(!empty($file)){
if (file_exists(sfConfig::get('sf_upload_dir').'/'.$this->getObject()->getPic())){
@unlink($this->getObject()->getPic());
}
$filename = sha1($file->getOriginalName()).$file->getExtension($file->getOriginalExtension());
$path = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.sfConfig::get('sf_web_dir_name').DIRECTORY_SEPARATOR.sfConfig::get('sf_upload_dir');
$file->save($path.'/'.$filename);
}

return parent::doSave($con);
}
}//class

/lib/myWidgetFormInputFileEditable.class.php

/*
* This file is part of the symfony package.
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* sfWidgetFormInputFileEditable represents an upload HTML input tag with the possibility
* to remove a previously uploaded file.
*
* @package symfony
* @subpackage widget
* @author Fabien Potencier
* @version SVN: $Id: sfWidgetFormInputFileEditable.class.php 11544 2008-09-14 17:40:07Z fabien $
*/
class myWidgetFormInputFileEditable extends sfWidgetFormInputFile
{
/**
* Constructor.
*
* Available options:
*
* * file_src: The current image web source path (required)
* * edit_mode: A Boolean: true to enabled edit mode, false otherwise
* * is_image: Whether the file is a displayable image
* * with_delete: Whether to add a delete checkbox or not
* * delete_label: The delete label used by the template
* * template: The HTML template to use to render this widget
* The available placeholders are:
* * input (the image upload widget)
* * delete (the delete checkbox)
* * delete_label (the delete label text)
* * file (the file tag)
*
* In edit mode, this widget renders an additional widget named after the
* file upload widget with a "_delete" suffix. So, when creating a form,
* don't forget to add a validator for this additional field.
*
* @param array $options An array of options
* @param array $attributes An array of default HTML attributes
*
* @see sfWidgetFormInputFile
*/
protected function configure($options = array(), $attributes = array())
{
parent::configure($options, $attributes);

$this->setOption('type', 'file');
$this->setOption('needs_multipart', true);

//$this->addRequiredOption('file_src');
$this->addOption('file_src', '');
$this->addOption('is_image', false);
$this->addOption('edit_mode', true);
$this->addOption('with_delete', true);
$this->addOption('delete_label', 'remove the current file');
$this->addOption('template', '%file% %input% %delete% %delete_label%');
}

/**
* @param string $name The element name
* @param string $value The value displayed in this widget
* @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
* @param array $errors An array of errors for the field
*
* @return string An HTML tag string
*
* @see sfWidgetForm
*/
public function render($name, $value = null, $attributes = array(), $errors = array())
{

$input = parent::render($name, $value, $attributes, $errors);

if (!$this->getOption('edit_mode'))
{
return $input;
}

if ($this->getOption('with_delete')){
$deleteName = ']' == substr($name, -1) ? substr($name, 0, -1).'_delete]' : $name.'_delete';
$delete = $this->renderTag('input', array_merge(array('type' => 'checkbox', 'name' => $deleteName), $attributes));
$deleteLabel = $this->renderContentTag('span', $this->getOption('delete_label'), array_merge(array('for' => $this->generateId($deleteName))));
} else {
$delete = '';
$deleteLabel = '';
}
if ($value){
$picLink = " || <a class='thickbox' target='_blank' href='/uploads/".$value."'>show me</a>";
} else {
$picLink = "";
$delete = '';
$deleteLabel = '';
}

return strtr($this->getOption('template'), array(
'%input%' => $input,
'%delete%' => $delete,
'%delete_label%' => $deleteLabel,
'%file%' => $this->getFileAsTag($attributes, $value))
).$picLink;
}

protected function getFileAsTag($attributes, $value=null)
{
if ($this->getOption('is_image'))
{
return false !== $value ? $this->renderTag('img', array_merge(array('src' => $value)), $attributes) : '';
}
else
{
return $this->getOption('file_src');
}
}
}

/backend/config/view.yml
default:
http_metas:
content-type: text/html
metas:
title: Admin
stylesheets: [main, thickbox]
javascripts: [jq/jquery.pack.js, jq/thickbox.pack.js]

Symfony: sfGuard custom login form

Немного улучшим дизайн формы ввода логина.


myGuard.css
.error_list { color: #ff0000; }
.errorField{
background: #ffffcc; border: 2px solid red;
}
.okField{
background: #ffffcc; border: 2px solid lightgreen;
}
.inputField{
background: #ffffff;
}

#loginForm {
display: none;
background: khaki; width: 280px;
position: absolute;
z-index: 9999; font-family: Tahoma; font-size: 12px;
border: 0px solid red;
}

#loginFormHeader {
background: #cccc33; padding: 10px;
border-bottom: 1px solid gray;
font-weight: bold;
}
#loginFormBody {
border-top: 1px solid #ececec;
border-bottom: 1px solid gray;
padding: 4px 0px 4px 0px;
}

#loginFormPic {
padding: 10px 0px 0px 10px;
display: block;
float: left;
}
#loginFormBody ul{
float: left;
display: block;
border: 0px solid navy;
padding: 4px; list-style: none;
}

#loginFormBody ul li{
border: 0px solid #f63;
padding: 4px;
}

#loginFormBody label
{
display: block;
border: 0px solid green;
padding: 0 1em 3px 0;
float: left;
text-align: left;
width: 40px;
color: black;
font-weight: normal !important;
}
#loginFormFooter {
padding: 10px;
text-align: center;
border-top: 1px solid #ececec;
}


/backend/config/view.yml
signinSuccess:
javascripts: [jq/jquery.pack.js, jq/jquery.dropshadow.js]
stylesheets: [main, myGuard]
metas:
title: login

/backend/modules/sfGuardAuth/signinSuccess.php
<?php $errorFieldClass = "errorField" ?>
<?php $defaultFieldClass = ($sf_request->isMethod('post') ? "okField" : "inputField" )?>

<form action="<?php echo url_for('@sf_guard_signin') ?>"
method="post">
<div id="loginForm">
<div id="loginFormHeader">Требуется авторизация</div>
<div id="loginFormBody">
<div id="loginFormPic"><img src="/sf/sf_default/images/icons/lock48.png" /></div>
<ul>
<li><label for="signin_username">Логин:</label>
<?php echo $form['username']->render(
array(
'class' => $form['username']->hasError() ? $errorFieldClass : $defaultFieldClass))
?>
</li>
<li><label for="signin_password">Пароль:</label>
<?php echo $form['password']->render(
array(
'class' => $form['password']->hasError() ? $errorFieldClass : $defaultFieldClass))
?>
</li>
<li><label for="signin_remember"></label>
<?php echo $form['remember']->render() ?> Запомнить?
</li>
</ul>
<br clear="all" />
</div>
<div id="loginFormFooter">
<input type="submit" value="войти" />
</div>
</div>
</form>
<script>
$(document).ready(function() {
$("#loginForm").hide();
var popupX = Math.round( ($(window).width() - $("#loginForm").width()) / 2) ;
var popupY = $(document).scrollTop() + Math.round($(window).height()/2) - Math.round($("#loginForm").height()/2);
$("#loginForm").css({top: popupY+"px", left: popupX+"px"});
$("#signin_username").focus();
$("#loginForm").show();
$("#loginForm").dropShadow();//{left: -2, top: -2, blur: 4, color: "#03f"}
});
</script>

вторник, 11 ноября 2008 г.

Symfony 1.2: вышло обновление - beta2

Очень оперативно работают парни, 10 дней прошло после выхода beta1, и вот вам обновление.
Список "фиксов" тут:
http://trac.symfony-project.org/query?status=closed&milestone=1.2.0+BETA2

Теперь о неприятном ;)
После обновления я сразу получил ошибку
500 | Internal Server Error | sfConfigurationException
The route "articles_collection" does not exist.


Видимо опять что то изменилось в системе роутинга, беглый просмотр офиц.сайта не дал ответа что далать, пришлось "методом научного тыка" сделать костыль.
articles_collection:
url: /article/:action/*

articles:
class: sfPropelRouteCollection
options:
model: Article
module: article
with_show: false
collection_actions: { filter: post, batch: post }

Не знаю, прав ли я???

среда, 5 ноября 2008 г.

Symfony 1.2: подводные камни

1. В схеме нельзя давать имя модели Comment, это имя теперь зарезервировано Propel 1.3, используйте BlogComment и т.п.

2. Новый админ-генератор использует routing.yml, перед генерацией админки надо обязательно вписать в него блок такого типа:
articles:
class: sfPropelRouteCollection
options:
model: BlogArticle
module: article
collection_actions: { filter: post, batch: post }

Внимание!!!
Блок вставлять в начало routing.yml иначе получите ошибку
404 | Not Found | sfError404Exception
Action "articles/index" does not exist.

3. Помимо генерации модели и форм командами propel:generate-model, propel:generate-forms, теперь еще надо генерировать и фильтры(при первом взгляде те же формы) командой propel:generate-filters. Либо просто propel:generate-all.

...to be continued...

воскресенье, 2 ноября 2008 г.