Practical symfony

第四天:控制器和视图

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "Practical symfony" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Jobeet Links

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

MVC体系

布局(layout)

样式、图片、脚本

Job首页

动作(Action)

模板(Templates)

Job页面模板

槽(Slot)

Job页动作

请求与响应

请求(Request)

响应(Response)

明天见

symfony training
Be trained by symfony experts
Feb 21: Köln (Getting Started with Symfony2 - English)
Feb 27: Köln (Mastering Symfony2 - English)
Mar 05: Köln (Web Development with Symfony2 - Deutsch)
Mar 05: Montreal (Web Development with Symfony2 - English)
Mar 05: Montreal (Getting Started with Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "Practical symfony" in for the 1.2 version - Propel edition - Switch to ORM: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
This version of symfony is not maintained anymore.
If some of your projects still use this version, consider upgrading as soon as possible.
Practical symfony (Propel edition)
Support symfony!
Buy this book
or donate.
Buy Practical symfony (Propel edition) from amazon.com

昨天,我们研究了symfony是如何通过抽象化数据库引擎间的差别,将关联元素转化为面向 对象的类,来简化数据库管理的;我们使用Propel描述数据库结构、生成数据表,并给 数据库添加了一些初始数据。

今天,我们将完成昨天生成的工作模块(job moudle)。这个模块已经包括Jobeet需要的 所有代码:

代码已经有了,我们要修改模板,让它符合我们的设计。

MVC体系

如果你没有用过框架,你可能习惯于将php代码与HTML代码混编在同一个php文件中。这些 php文件可能都包含配置代码、业务逻辑、SQL语句和HTML代码。

你可能使用过模板引擎从HTML代码中分离出业务逻辑,也可能使用过数据抽象层从业务逻辑 中分离出关系模型。但是大多时候,这只会让你产生大量的代码。开发速度提高,但不久, 你就发现代码越来越难维护,因为除了你之外没有人能读懂这些代码是如何工作的。

对于这些问题,有一个非常好的解决办法——[MVC 设计模式] (http://en.wikipedia.org/wiki/Model-view-controller) ,这是现在最普遍的使用的 组织代码的方式。简单来说,MVC模式定义了一种最自然的组织代码的方式。这种模式将 代码分离为3个层:

MVC

今天,我将按第2天给出的布局修改主页和工作页,并让它们能动态显示内容。这样的话, 我们要做很多事,你可以从其中了解 symfony目录结构,及各层代码分离的方法。

布局(layout)

首先,如果你仔细研究了网页的设计图,你会发现每个页面都有很多相同的地方。 这表示我们必须在每个页面添加一些相同的代码。如你所知,代码重复是非常糟糕的, 所以我们首先要解决这个问题。

我们通常办法是:将重复代码(如头部代码和底部代码)分别写入单独的页面,然后 再让需要显示这些内容的页面去调用这些单独的页面。下面是个简单的图示:

Header and footer

这种办法看起来不错,大多数时候我们也是这么做的。但是,这个办法存在一个缺陷, 就是头部和底部两个独立的文件中会含有没有闭合的HTML标签。 为了让一切看上去很 完美,我们使用了另外一种更好方式来解决这个问题—— 装饰设计模式 (Decorator design pattern) (Decorator design pattern):就是使用全局模板(layout ),来装饰展示内容的 模板(译注:内容模板如同画,全局模板如同框,装饰模式就是用画框来装饰画。反过 来说,一个画框又可以装饰不同的画):

Layout

默认的全局模板(layout)放在layout.php文件中,可以在 apps/frontend/templates/目录下找到它。这个目录中的通常存放全局模板文件, 程序(app)中每个模块(module)都可以使用。

用下面代码替换layout.php中的代码:

<!-- apps/frontend/templates/layout.php -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Jobeet - Your best job board</title>
    <link rel="shortcut icon" href="/favicon.ico" />
    <?php include_javascripts() ?>
    <?php include_stylesheets() ?>
  </head>
  <body>
    <div id="container">
      <div id="header">
        <div class="content">
          <h1><a href="/job">
            <img src="/images/logo.jpg" alt="Jobeet Job Board" />
          </a></h1>
 
          <div id="sub_header">
            <div class="post">
              <h2>Ask for people</h2>
              <div>
                <a href="/job/new">Post a Job</a>
              </div>
            </div>
 
            <div class="search">
              <h2>Ask for a job</h2>
              <form action="" method="get">
                <input type="text" name="keywords"
                  id="search_keywords" />
                <input type="submit" value="search" />
                <div class="help">
                  Enter some keywords (city, country, position, ...)
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
 
      <div id="content">
        <?php if ($sf_user->hasFlash('notice')): ?>
          <div class="flash_notice">
            <?php echo $sf_user->getFlash('notice') ?>
          </div>
        <?php endif; ?>
 
        <?php if ($sf_user->hasFlash('error')): ?>
          <div class="flash_error">
            <?php echo $sf_user->getFlash('error') ?>
          </div>
        <?php endif; ?>
 
        <div class="content">
          <?php echo $sf_content ?>
        </div>
      </div>
 
      <div id="footer">
        <div class="content">
          <span class="symfony">
            <img src="/images/jobeet-mini.png" />
            powered by <a href="http://www.symfony-project.org/">
            <img src="/images/symfony.gif" alt="symfony framework" />
            </a>
          </span>
          <ul>
            <li><a href="">About Jobeet</a></li>
            <li class="feed"><a href="">Full feed</a></li>
            <li><a href="">Jobeet API</a></li>
            <li class="last"><a href="">Affiliates</a></li>
          </ul>
        </div>
      </div>
    </div>
  </body>
</html>

和其它的一些模板程序不大相同,symfony的模板都是纯PHP文件。在layout模板中 包含一些PHP变量和函数调用。如$sf_content就是一个非常有趣的变量:它由框架定义,包 含由动作(action)生成的HTML代码。

如果你浏览job模块(http://jobeet.localhost/frontend_dev.php/job), 你可以看到所有被layout装饰的动作。

样式、图片、脚本

这不是一个关于网页设计的教程,我们已经为读者准备好了全部的资源文件:你可以到 这里 下载图片放到web/images/目录下;从这里 下载css文件放到web/css/目录下。

在layout中包含一个收藏夹图标,你可以从这里 下载,放到web/目录下。

The job module with a layout and assets

默认的情况下,generate:project命令会自动在/web目录下生成3个目录来分别存放不 同文件:/web/images/目录用来保存图片,/web/~css|CSS~/保存css样式表(Stylesheets), /web/js/保存javascript文件。这些目录是symfony的默认目录,如果没有特别的声明, symfony会自动到这些目录寻找相应类型的文件。当然,我们也可以通过显示的说明,而 将这些类型文件放到web/下的任意目录中。

也许你会奇怪,当我们在浏览器中查看job页面源文件时,会发现页面使用一个名叫 main.css的样式文件,但是在layout页面中我们却没有发现任何调用这个文件的语句, 这是怎么回事呢?

其实main.css已经被调用了,只不过是使用了一种我们现在还不太熟悉的方式——辅助函数(helper)。 辅助函数是symfony内置辅助生成HTML代码的函数,很多情况下它可以帮助你节约书写 HTML代码的时间。include_stylesheets()就是一个辅助函数,用来生成一个调用css文件 的<link>标签。

但是如何知道include_stylesheets()函数是如何知道调用哪个css样式文件呢?

include_stylesheets()函数是通过读取view.yml文件中stylesheets项来决定调用哪些文件的。 view.yml被用来配置视图层,下面是generate:app自动生成的view.yml文件:

# apps/frontend/config/view.yml
default:
  http_metas:
    content-type: text/html
 
  metas:
    #title:        symfony project
    #description:  symfony project
    #keywords:     symfony, project
    #language:     en
    #robots:       index, follow
 
  stylesheets:    [main.css]
 
  javascripts:    []
 
  has_layout:     on
  layout:         layout

这个view.yml文件为程序下的所有模板设置默认配置,例如stylesheets 项定义程 序每个页面要调用的css文件的数组(这个数组通过include_stylesheets()调用)。 我们可以通过stylesheets项为程序中每一个页面单独定义要调用的css文件。

在默认的view.yml文件中调用的样式文件是main.css而不是/css/main.css。 事实上这是一个问题, 使用/~css|CSS~/路径当做前缀(Prefix)和不是用其实是等价的。

当定义多个样式时,程序会按照数组中文件名的顺序,依次生成标签,按顺序调用每个样式:

stylesheets:    [main.css, jobs.css, job.css]

你可以改变它们的media属性,也可以省略样式文件名的.css后缀:

stylesheets:    [main.css, jobs.css, job.css, print: { media: print }]

这条设置将转化成下面的html标签:

<link rel="stylesheet" type="text/css" media="screen"
  href="/css/main.css" />
<link rel="stylesheet" type="text/css" media="screen"
  href="/css/jobs.css" />
<link rel="stylesheet" type="text/css" media="screen"
  href="/css/job.css" />
<link rel="stylesheet" type="text/css" media="print"
  href="/css/print.css" />

view.yml也定义程序的使用全局模板,默认调用layout 模板,这个模板对应layout.php 文件。如果将has_layout项设置为false,装饰模式将被禁用。

view.yml不仅可以定义所有模板共用css文件,还可以单独为每个模块设置css样式。 将程序的view.yml文件改回只包含main.css

# apps/frontend/config/view.yml
stylesheets:    [main.css]

为了给job模块单独配置视图,我们必须在apps/frontend/modules/job/config/目录下, 给job模块创建一个新的view.yml文件,加入代码:

# apps/frontend/modules/job/config/view.yml
indexSuccess:
  stylesheets: [jobs.css]
 
showSuccess:
  stylesheets: [job.css]

indexSuccessshowSuccess两部分(对应index和show动作的模板名字,关于模板 的命名规则后面会详细讲述)可以使用程序view.yml中的所有的设置项来配置。也可以 使用all为模块中所有动作进行配置。当程序执行时,模块和程序的view.yml文件将 被合并,合并的原则是:模块(的 view.yml)中所有的设置项及模块(的view.yml) 中没有而程序(的view.yml)中有的设置项将全部保留;程序(的view.yml)中与模 块(的view.yml)中相同的设置将被覆盖;

一般来说,配置文件中的每个设置都可以使用PHP代码的动态实现,例如,你可以直接 使用use_stylesheet()辅助函数来调用view.yml中设置的css文件:

<?php use_stylesheet('main.css') ?>

你也可以在layout中使用这个辅助函数来调用一个全局css样式。

选择哪种方法只是一个爱好问题。一方面,view.yml提供了一种为模块里所有动作设置 配置的方法,这在一个模板里是不可能实现的(模板中只能配置使用这个模板的动作)。 另一方面,view.yml文件中设置是完全静态,当你需要动态调用的时候,使用 use_stylesheet() helper会非常方便,另外使用辅助函数会使你css文件和HTML代码 放在一起,修改起来会方便许多。

在jobeet中我们将使用use_stylesheet()辅助函数的方式,你不需要配置view.yml文件。

<!-- apps/frontend/modules/job/templates/indexSuccess.php -->
<?php use_stylesheet('jobs.css') ?>
 
<!-- apps/frontend/modules/job/templates/showSuccess.php -->
<?php use_stylesheet('job.css') ?>

相应的,javascript文件的调用可以通过view.ymljavascripts项设置,也可以 通过在模板使用use_javascript()辅助函数来调用。

Job首页

在第3天教程中,job首页已经通过job模块中index动作生成。index动作是首页 的控制层部分,关联的模板indexSuccess.php是视图层部分:

apps/
  frontend/
    modules/
      job/
        actions/
          actions.class.php
        templates/
          indexSuccess.php

动作(Action)

每个动作都是类的一个方法。对于工作主页来说,类为jobActions(命名方式: 模块名+Action后缀),方法为executeIndex()(命名方式:execute+以驼峰式书写 的动作名),这个动作从数据库中获取全部的job记录:

// apps/frontend/modules/job/actions/actions.class.php
class jobActions extends sfActions
{
  public function executeIndex(sfWebRequest $request)
  {
    $this->jobeet_job_list = JobeetJobPeer::doSelect(new Criteria());
  }
 
  // ...
}

让我们仔细看一下这段代码:executeIndex()方法(控制层)调用JobeetJobPeer方法检索 所有job记录(new Criteria()),这个方法返回一个包含所有JobeetJob对象(symfony将 数据表中每条记录转化成一个对象)的数组,并将这个数组赋值给jobeet_job_list对象属性。

所有这样对象属性(jobeet_job_list)都会自动传递给模板(the View)。要将数据从 控制层传送到视图(或者说从动作传给模板),只需要定义一个新属性:

public function executeFooBar(sfWebRequest $request)
{
  $this->foo = 'bar';
  $this->bar = array('bar', 'baz');
}

你可以直接在模板中用$foo$bar变量访问这个两个属性。

模板(Templates)

默认情况下,模板的名称与动作的名称是相对应的(模板的命名规则: 动作名+Success后缀)。

indexSuccess.php模板为所有工作生成HTML表格,用来展示工作信息:

<!-- apps/frontend/modules/job/templates/indexSuccess.php -->
<?php use_stylesheet('jobs.css') ?>
 
<h1>Job List</h1>
 
<table>
  <thead>
    <tr>
      <th>Id</th>
      <th>Category</th>
      <th>Type</th>
<!-- more columns here -->
      <th>Created at</th>
      <th>Updated at</th>
    </tr>
  </thead>
  <tbody>
    <?php foreach ($jobeet_job_list as $jobeet_job): ?>
    <tr>
      <td>
        <a href="<?php echo url_for('job/show?id='.$jobeet_job->getId()) ?>">
          <?php echo $jobeet_job->getId() ?>
        </a>
      </td>
      <td><?php echo $jobeet_job->getCategoryId() ?></td>
      <td><?php echo $jobeet_job->getType() ?></td>
<!-- more columns here -->
      <td><?php echo $jobeet_job->getCreatedAt() ?></td>
      <td><?php echo $jobeet_job->getUpdatedAt() ?></td>
    </tr>
    <?php endforeach; ?>
  </tbody>
</table>
 
<a href="<?php echo url_for('job/new') ?>">New</a>

在模板代码中, foreach遍历所有job对象($jobeet_job_list),输出数据表中 每条job记录中每个字段(column)值。我们可以通过调用存取器、很容易的访问记录中 的每个字段,存取器有统一的命名规则:是以get+驼峰式书写的字段(column)名(getColumn), 例如 getCreatedAt()方法,可以获取一条记录的created_at值【字段名中下划线(_) 被自动省略】。

我们修改一下模板,只显示需要的内容:

<!-- apps/frontend/modules/job/templates/indexSuccess.php -->
<?php use_stylesheet('jobs.css') ?>
 
<div id="jobs">
  <table class="jobs">
    <?php foreach ($jobeet_job_list as $i => $job): ?>
      <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
        <td class="location"><?php echo $job->getLocation() ?></td>
        <td class="position">
          <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>">
            <?php echo $job->getPosition() ?>
          </a>
        </td>
        <td class="company"><?php echo $job->getCompany() ?></td>
      </tr>
    <?php endforeach; ?>
  </table>
</div>

Homepage

模板中url_for()也是一个辅助函数,生成标签,我们明天再说它。

Job页面模板

现在我们定制job页模板,打开showSuccess.php,用下面代码替换现有的内容:

<!-- apps/frontend/modules/job/templates/showSuccess.php -->
<?php use_stylesheet('job.css') ?>
<?php use_helper('Text') ?>
 
<div id="job">
  <h1><?php echo $job->getCompany() ?></h1>
  <h2><?php echo $job->getLocation() ?></h2>
  <h3>
    <?php echo $job->getPosition() ?>
    <small> - <?php echo $job->getType() ?></small>
  </h3>
 
  <?php if ($job->getLogo()): ?>
    <div class="logo">
      <a href="<?php echo $job->getUrl() ?>">
        <img src="/uploads/jobs/<?php echo $job->getLogo() ?>"
          alt="<?php echo $job->getCompany() ?> logo" />
      </a>
    </div>
  <?php endif; ?>
 
  <div class="description">
    <?php echo simple_format_text($job->getDescription()) ?>
  </div>
 
  <h4>How to apply?</h4>
 
  <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p>
 
  <div class="meta">
    <small>posted on <?php echo $job->getCreatedAt('m/d/Y') ?></small>
  </div>
 
  <div style="padding: 20px 0">
    <a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">
      Edit
    </a>
  </div>
</div>

这个模板直接使用$job变量显示job信息,我们前边提到过,这个变量是在动作里定义的 一个对象属性。这里我们将$jobeet_job改成了$job,所以要相应的修改show动作里 的对象属性名(注意,有两处要修改):

// apps/frontend/modules/job/actions/actions.class.php
public function executeShow(sfWebRequest $request)
{
  $this->job = JobeetJobPeer::retrieveByPk($request->getParameter('id'));
  $this->forward404Unless($this->job);
}

注意,有些propel存取器(Accessors)可以携带参数。如,created_at字段的类型为timestamp, 可以设置getCreatedAt()存取器方法的第一个参数来格式化输出日期:

$job->getCreatedAt('m/d/Y');

工作说明使用simple_format_text() 辅助函数格式化为HTML,将carriage替换为<br />, 这个辅助函数属于Text 辅助函数组,这个组的辅助函数不会被程序自动加载, 需要使用use_helper()加载。

Job page

槽(Slot)

现在,我们已经在layout中定义所有页面的<title>标签:

<title>Jobeet - Your best job board</title>

但对于工作页,我们希望显示更有意义的title,比如公司名和工作地点。

symfony中,当layout中一个区域依赖模板显示(依赖于模板中定义的内容,而显示的位 置却在模板之外)时,需要定义一个槽:

Slots

给layout添加一个槽,让标题动态显示:

// apps/frontend/templates/layout.php
<title><?php include_slot('title') ?></title>

每个槽都通过一个名字(如title)定义,并通过include_slot()调用。现在, 在showSuccess.php模板开头,定义一个槽:

// apps/frontend/modules/job/templates/showSuccess.php
<?php slot(
  'title',
  sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()))
?>

如果要生成复杂的title,slot()辅助函数可以写成下面代码块的形式:

// apps/frontend/modules/job/templates/showSuccess.php
<?php slot('title') ?>
  <?php echo sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()) ?>
<?php end_slot(); ?>

一般像首页这样的页面,我们只需要普通的title,我们只需要在layout定义一个默认title:

// apps/frontend/templates/layout.php
<title>
  <?php if (!include_slot('title')): ?>
    Jobeet - Your best job board
  <?php endif; ?>
</title>

如果名为title的槽已经定义,include_slot()将返回true,显示title槽 定义内容,否则显示默认title。

我们已经看了不少以include_开头的辅助函数,这些辅助函数可以直接输出HTML代码, 而以get_开头的helper,大多数时候都只返回内容(需要输出语句显示内容):

<?php include_slot('title') ?>
<?php echo get_slot('title') ?>
 
<?php include_stylesheets() ?>
<?php echo get_stylesheets() ?>

Job页动作

工作页由show动作生成,show动作在job模块的executeShow()方法中定义:

class jobActions extends sfActions
{
  public function executeShow(sfWebRequest $request)
  {
    $this->job = JobeetJobPeer::retrieveByPk($request->getParameter('id'));
    $this->forward404Unless($this->job);
  }
 
  // ...
}

index动作一样,show动作也继承自JobeetJobPeer类,不过它是使用 retrieveByPk()方法来获得一条job记录,这个方法的参数是数据表的主键。

propel生成的model类包含许多有用的方法,这些方法与项目对象一起使用。花些时间 去浏览一下位于lib/om/目录下这些文件中模型类中各个方法,将很有帮助。

如果请求的job记录不存在,forward404Unless()显示404错误页面, 它的第一参数为布尔型变量,如果为 trueforward404Unless()会停止执行当前进程。 当forward方法通过抛出sfError404Exception异常停止执行动作时,不需要使用返回语句。

生产环境(Environments)和开发环境的404页面有些区别:

404 error in the dev environment

404 error in the prod environment

在部署Jobeet网站到生产服务器上之前,你将会学会如何定制默认的404页面。

请求与响应

当你在浏览器中浏览/job/job/show/id/1页面时,你便与服务器进行了一次交互, 浏览器发送一个请求(request)到服务器而服务器返回一个响应 (response)。

我们已经看到symfony将请求封装到sfWebRequest对象中(看一下executeShow()方法)。 作为一个面向对象的框架,响应也被封装到sfWebResponse对象中。你可以通过调用 $this->getResponse()访问响应对象。

这些对象提供许多方便的方法,访问PHP函数和PHP全局变量中的信息。

symfony为什么要打包已经存在的PHP功能?第一,因为symfony方法比PHP相应的方法更 加强大。第二,当你测试一个程序,symfony方法更容易模拟一个请求或回应对象,这 比起反复折腾全局变量或使用PHP中类似header()的函数强很多。

请求(Request)

sfWebRequest类包含了$_SERVER, $_COOKIE, $_GET, $_POST, $_FILES PHP全局数组:

Method name PHP equivalent
getMethod() $_SERVER['REQUEST_METHOD']
getUri() $_SERVER['REQUEST_URI']
getReferer() $_SERVER['HTTP_REFERER']
getHost() $_SERVER['HTTP_HOST']
getLanguages() $_SERVER['HTTP_ACCEPT_LANGUAGE']
getCharsets() $_SERVER['HTTP_ACCEPT_CHARSET']
isXmlHttpRequest() $_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest'
getHttpHeader() $_SERVER
getCookie() $_COOKIE
isSecure() $_SERVER['HTTPS']
getFiles() $_FILES
getGetParameter() $_GET
getPostParameter() $_POST
getUrlParameter() $_SERVER['PATH_INFO']
getRemoteAddress() $_SERVER['REMOTE_ADDR']

我们已经用过getParameter()方法访问请求的参数,它返回$_GET$_POST 全局变量的值或~PATH_INFO~变量。

如果你想确定请求参数究竟属于上面哪一类,你需要使用getGetParameter(), getPostParameter(), 和 getUrlParameter()

如果你想限制一个动作只接受某种特定HTTP method传递的参数, 比如,当你需要表单是否是通过POST方式提交的,你可以使用isMethod()方法: $this->forwardUnless($request->isMethod(’POST’));

响应(Response)

sfWebResponse类,包含~header|HTTP Headers~()setraw~cookie|Cookies~()PHP方法:

Method name PHP equivalent
setCookie() setrawcookie()
setStatusCode() header()
setHttpHeader() header()
setContentType() header()
addVaryHttpHeader() header()
addCacheControlHttpHeader() header()

当然,sfWebResponse也可以设置响应内容(setContent()),和发送响应(send())到 浏览器的方法。

今天开始时候,我们讲了如何在view.yml文件和模板中管理样式表和javascript脚本。 现在我们用响应对象的addStylesheet()addJavascript()方法可以实现同样的效果。

sfAction, sfRequestsfResponse类提供了 许多有用的方法。不要犹豫了,去 API documentation学习更多的 symfony 内置类。

明天见

今天,我们已经描述了一些symfony使用的设计模式。希望项目结构现在会更有意义。 我们已经通过操作layout和模板文件,改变了模板。并使用插槽和动作让模板显示的 更加动态。

明天,我们将学习更多关于url_for()辅助函数的用法,并通过它给子框架设置路由。

第五天:路由配置  »
« 第三天:数据模型

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.