让我们让它更人性化

第1步:让我们改进网站对访问者的可用性

我注意到的第一件事是,通过登录/密码验证向其发送照片链接的人会丢失,因为他们不知道自己必须通过单击“验证”链接登录。这很可能是因为他们在试图访问未经授权的文件夹时只得到404,而网站并没有明确说明1。你没有被认证,2.你可以通过认证自己获得更多的内容。

因此,为了改善这种情况,我决定:

  • 让一个匿名登录框出现,这样他们第一眼就能看到一个放置我提供的登录/密码信息的地方。

  • 自定义404页,建议匿名登录。

这是代码,来自我多维数据集的示例 views.py 文件:

from cubicweb import _
from cubicweb.web import component
from cubicweb.web.views import error
from cubicweb.predicates import anonymous_user


class FourOhFour(error.FourOhFour):
    __select__ = error.FourOhFour.__select__ & anonymous_user()

    def call(self):
        self.w(u"<h1>%s</h1>" % self._cw._('this resource does not exist'))
        self.w(u"<p>%s</p>" % self._cw._('have you tried to login?'))


class LoginBox(component.CtxComponent):
    """display a box containing links to all startup views"""
    __regid__ = 'sytweb.loginbox'
    __select__ = component.CtxComponent.__select__ & anonymous_user()

    title = _('Authenticate yourself')
    order = 70

    def render_body(self, w):
        cw = self._cw
        form = cw.vreg['forms'].select('logform', cw)
        form.render(w=w, table_class='', display_progress_div=False)

第一个类提供了一个针对404错误的默认页面的新的特定实现,以向匿名用户显示一条经过调整的消息。

注解

由于选择Mecanism,它将被选为异常用户,因为 anonymous_user() 选择器给它比默认值更高的分数,并且不进行身份验证,因为在这种情况下,此选择器将返回0(因此对象将不可选)。

第二个类定义了一个简单的框,默认情况下,由于默认值,该框将显示在左列中。 component.CtxComponent 选择器。HTML被写入以匹配默认的CubicWeb框样式。代码获取实际的登录表单并呈现它。

登录框/404屏幕截图

匿名访问者的登录框和自定义404页面(法语翻译)

步骤2:提供自定义索引页

另一件我们可以很容易地做的事情就是…更好的索引页(例如,访问网站时获得的第一页)!默认情况下,这是相当令人生畏的(在不久的将来会改变)。我将提供一个简单得多的索引页,只列出可用的文件夹(如该站点中的相册)。

这是代码,来自我多维数据集的示例 views.py 文件:

from cubicweb.web.views import startup


class IndexView(startup.IndexView):
    def call(self, **kwargs):
        self.w(u'<div>\n')
        if self._cw.cnx.session.anonymous_session:
            self.w(u'<h4>%s</h4>\n' % self._cw._('Public Albums'))
        else:
            self.w(u'<h4>%s</h4>\n' % self._cw._('Albums for %s') % self._cw.user.login)
        self._cw.vreg['views'].select('tree', self._cw).render(w=self.w)
        self.w(u'</div>\n')


def registration_callback(vreg):
    vreg.register_all(globals().values(), __name__, (IndexView,))
    vreg.register_and_replace(IndexView, startup.IndexView)

如您所见,我们将覆盖在 cubicweb.web.views.startup ,因为我们重写了顶级视图的 call 方法。

注解

在这种情况下,我们希望索引视图 代替 现有的那个。为此,我们必须实施 registration_callback 函数,在该函数中,我们要求注册模块中的所有内容 but 我们的索引视图,然后我们注册它而不是以前的索引视图。

此外,我们还添加了一个标题,试图使访问者是否经过身份验证更加明显。希望人们现在能得到它!

默认索引页面屏幕快照

默认索引页

新的索引页屏幕快照

我们的简单、不那么吓人的索引页(仍然用法语翻译)

第3步:更多导航改进

还有一些问题我想解决…

  • 文件夹中的图像以某种随机顺序显示。我想让他们按文件名排序(通常,在给定的文件夹中,也会按日期和时间排序照片)

  • 从相册视图单击照片时,您必须返回到图库视图才能转到下一张照片。这很烦人…

  • 此外,在查看图像时,没有关于此图像所属文件夹的线索。

我先解释一下订购问题。默认情况下,使用ORM的API访问相关实体时,应该根据目标的类对它们进行排序。 cw_fetch_order .如果我们查看文件多维数据集的模式,我们可以看到:

class File(AnyEntity):
    """customized class for File entities"""
    __regid__ = 'File'
    fetch_attrs, cw_fetch_order = fetch_config(['data_name', 'title'])

默认情况下, fetch_config 将返回 cw_fetch_order 将对列表中的第一个属性排序的方法。所以,我们可以期望得到按名称排序的文件。但我们没有。医生怎么了?

问题是文件与使用 filed_under 关系。这种关系是模棱两可的。 File 实体,也包括 Folder 实体。在这种情况下,由于两种实体类型都不共享要排序的属性,因此我们将根据公共属性(通常 modification_date

为了解决这个问题,我们必须帮助ORM。我们将使用 ITree 文件夹的适配器,用于在文件夹的主视图中显示文件夹的内容。这是我放在多维数据集中的代码 entities.py 文件,因为它比查看内容更符合逻辑:

from cubicweb_folder import entities as folder


class FolderITreeAdapter(folder.FolderITreeAdapter):

    def different_type_children(self, entities=True):
        rql = self.entity.cw_related_rql(self.tree_relation,
                                         self.parent_role, ('File',))
        rset = self._cw.execute(rql, {'x': self.entity.eid})

        if entities:
            return list(rset.entities())

        return rset


def registration_callback(vreg):
    vreg.register_and_replace(FolderITreeAdapter, folder.FolderITreeAdapter)

如您所见,我们简单地从中定义的适配器继承 folder 多维数据集,然后我们重写 different_type_children 提供ORM线索的方法 cw_related_rql 方法,该方法负责生成rql以通过 filed_under 关系(的值 tree_relation 属性)。线索是我们只想考虑 File 目标实体类型。通过这样做,我们消除了模糊性,并返回一个RQL查询,该查询根据文件的 data_name 属性。

注解

  • 如前所述,我们希望 代替 文件夹的 ITree 适配器由我们的实现,因此自定义 registration_callback 方法。

哎呀。那是个棘手的问题…

现在更简单的部分。首先,我们在文件的主视图上添加一些链接,以查看同一文件夹中的上一个/下一个图像。CubicWeb提供了一个这样做的组件。要让它出现,人们必须适应 IPrevNext 接口。这是从多维数据集的 views.py 文件:

from cubicweb.predicates import is_instance
from cubicweb.web.views import navigation


class FileIPrevNextAdapter(navigation.IPrevNextAdapter):
    __select__ = is_instance('File')

    def previous_entity(self):
        rset = self._cw.execute('File F ORDERBY FDN DESC LIMIT 1 WHERE '
                                'X filed_under FOLDER, F filed_under FOLDER, '
                                'F data_name FDN, X data_name > FDN, X eid %(x)s',
                                {'x': self.entity.eid})
        if rset:
            return rset.get_entity(0, 0)

    def next_entity(self):
        rset = self._cw.execute('File F ORDERBY FDN ASC LIMIT 1 WHERE '
                                'X filed_under FOLDER, F filed_under FOLDER, '
                                'F data_name FDN, X data_name < FDN, X eid %(x)s',
                                {'x': self.entity.eid})
        if rset:
            return rset.get_entity(0, 0)

这个 IPrevNext 由适配器实现的接口只包含在 previous_entity / next_entity 方法,分别返回上一个/下一个实体或 None .我们进行一个RQL查询,以获取同一文件夹中的文件,顺序相似(例如 data_name 属性)。我们设置了上升/下降顺序,并与当前文件名(表示当前文件的“x”变量)进行了严格的比较。

注意,这个查询假设在同一个文件夹中不会有两个同名文件,否则可能会出错。修复此问题超出了本教程的范围。正如我希望在某个时候拥有一个更智能、上下文敏感的上一个/下一个实体一样,我可能永远不会修复这个查询(尽管如果必须,我可能会选择在模式中添加一个约束,这样我们就不能在一个文件夹中添加两个同名的文件)。

还有一件事:默认情况下,组件将显示在内容区域(白色背景)的下方。您可以通过UI在站点的属性中更改此值,但也可以通过修改 context 组件的属性 FileIPrevNextAdapter

navigation.NextPrevNavigationComponent.context = 'navcontentbottom'

注解

context 可能是“navtop”、“navbottom”、“navcontenttop”或“navcontentbottom”中的一个;前两个位于主内容区域之外,其余两个位于主内容区域内。

上一个/下一个实体组件的屏幕快照

上一个/下一个实体组件,位于主内容区域的底部。

现在,我的待办事项列表中唯一剩下的东西就是查看文件的文件夹。我将使用标准的breadcrumb组件来实现这一点。与我们以前看到的类似,这个组件由 IBreadCrumbs 接口,因此我们必须为 File 实体,告诉文件的父实体是其文件夹:

from cubicweb.web.views import ibreadcrumbs

class FileIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
    __select__ = is_instance('File')

    def parent_entity(self):
        if self.entity.filed_under:
            return self.entity.filed_under[0]

在这种情况下,我们只需使用ORM提供的属性表示法来获取当前文件所在的文件夹(例如 self.entity )位于。

注解

这个 IBreadCrumbs 接口是一个 breadcrumbs 方法,但默认 IBreadCrumbsAdapter 为其提供默认实现,该实现将查看其返回的值 parent_entity 方法。它还为适应 ITree 接口,但作为我们的 File 没有,我们必须提供一个自定义适配器。

Breadcrumb组件的屏幕快照

breadcrumb组件出现在文件实体上,现在显示父文件夹。

步骤4:准备发布并迁移实例

现在这大大增强了我们的多维数据集,是时候发布它来升级生产站点了。稍后我可能会详细介绍该过程,但目前我只是将新代码传输到运行该网站的服务器上。

不过,我今天还是要在尊重方面采取一些措施来妥善完成工作……

首先,当我添加了一些可翻译字符串时,我必须运行:::

$ cubicweb-ctl i18ncube sytweb

更新多维数据集的getText目录(多维数据集下的.po文件) i18n 目录)。执行上述命令后,我将更新翻译。

要查看我的测试实例上的一切是否都正常,我会执行以下操作::

$ cubicweb-ctl i18ninstance sytweb_instance
$ cubicweb-ctl pyramid -D sytweb_instance

第一个命令为我的测试实例编译i18n目录(例如生成“.mo”文件)。第二个命令以调试模式启动它,这样我就可以打开浏览器并浏览网站,查看是否一切正常…

注解

在“cubicWeb ctl i18ncube”命令中, sytweb refers to the cube, while in the two other, it refers to the instance (if you can't see the difference, reread CubicWeb's concept chapter !).

一旦我检查过它是好的,我只需要在 __pkginfo__ 模块来触发迁移,一旦我更新了生产站点上的代码。我可以检查,然后检查迁移是否正常,首先从生产站点恢复一个转储,然后升级我的测试实例。

要从生产站点生成转储,请执行以下操作:

$ cubicweb-ctl db-dump sytweb_instance
# if it's postgresql
pg_dump -Fc --username=syt --no-owner --file /home/syt/etc/cubicweb.d/sytweb/backup/tmpYIN0YI/system sytweb
# if it's sqlite
gzip -c /home/psycojoker/etc/cubicweb.d/sytweb_instance/sytweb_instance.sqlite
-> backup file /home/syt/etc/cubicweb.d/sytweb/backup/sytweb-2010-07-13_10-22-40.tar.gz

我现在可以取回转储文件了 (sytweb-2010-07-13_10-22-40.tar.gz )到我的测试机(使用 scp 例如)要还原它并开始迁移,请执行以下操作:

$ cubicweb-ctl db-restore sytweb_instance /path/path/to/sytweb-2010-07-13_10-22-40.tar.gz
$ cubicweb-ctl upgrade sytweb_instance

你可能需要回答一些问题,正如我们在 a previous part .

现在所有的东西都经过了测试,我可以将新代码传输到生产服务器, pip install CubicWeb及其依赖项,并最终升级生产实例。