使用创建我的照片网站 CubicWeb 第五部分:让我们让它更人性化

步骤1:厌倦了默认外观?

好啊。。。现在我们的网站有了它最想要的功能。但是…我想让它看起来有点像 my 网站。毕竟它不是www.cubicweb.org。让我们先解决这个问题!

我们能做的第一件事就是改变商标。实现这一点的方法多种多样。最简单的方法是 logo.png 文件到多维数据集的 data 目录。当根据多维数据集顺序(CubicWeb资源排在最后)查看数据文件时,将选择该文件而不是CubicWeb的文件。

注解

当静态资源的位置被缓存时,您必须重新启动实例才能考虑到这一点。

尽管有些情况下你不想使用 logo.png 文件。例如,如果它是一个jpeg文件。您仍然可以通过在多维数据集的 uiprops.py 文件:

LOGO = data('logo.jpg')

注解

如果文件 uiprops.py 不存在于多维数据集中,只需创建它即可。

uiprops机制用于定义一些静态文件资源,例如徽标、默认的javascript/css文件以及css属性(稍后我们将看到)。

注解

此文件是由 CubicWeb ,具有预定义的名称空间,例如包含 data 函数,指示文件位于多维数据集或CubicWeb的数据目录中。

这样做的一个副作用是它不能作为常规的Python模块导入。

好的是,在调试模式下,改为 uiprops.py 检测到文件,然后自动重新加载。

现在,因为这是一个照片网站,我想有一张我的照片作为背景…经过一些试验,我在这里没有详细说明,我找到了一个工作配方解释 here .我所要做的就是重写默认CubicWeb用户界面的一些内容,以便按照说明应用它。

第一件事就是 <img/> 标记为后面的第一个元素 <body> 标签。如果你知道通过简单地在CSS中指定图像来避免这种情况,请告诉我!最简单的方法是重写 HTMLPageHeader 视图,因为这是直接调用 <body> 已经写好了。我是怎么找到这个的?通过查看 cubiweb.web.views.basetemplates 模块,因为我知道全局页面布局就在那里。我也可以把“身体”标签放进去 cubicweb.web.views ……发现这是最困难的部分。现在我只需要定制它来写 img 标签,如下所示 views.py

from cubicweb.web.views import basetemplates


class HTMLPageHeader(basetemplates.HTMLPageHeader):
    # override this since it's the easier way to have our bg image
    # as the first element following <body>
    def call(self, **kwargs):
        self.w(u'<img id="bg-image" src="%sbackground.jpg" alt="background image"/>'
               % self._cw.datadir_url)
        super(HTMLPageHeader, self).call(**kwargs)


def registration_callback(vreg):
    vreg.register_all(globals().values(), __name__, (HTMLPageHeader))
    vreg.register_and_replace(HTMLPageHeader, basetemplates.HTMLPageHeader)

你可能已经猜到了,我的背景图像在 background.jpg 多维数据集中的文件 data 目录,但是这里仍然有一些事情需要向新来者解释:

  • 这个 call() 方法是视图的主要访问点。它是由视图调用的 render() 方法。它不是视图的唯一访问点,但稍后将详细介绍。

  • 调用 self.w 将某些内容写入输出流。除了二进制视图(不生成文本)之外,它 must 传递一个Unicode字符串。

  • 获取文件的正确方法 data 目录将使用 datadir_url 传入请求的属性(例如 self._cw

我不会再解释了 registration_callback() 东西,你现在应该明白了!如果没有,请返回 previous post in the series :)

好的。现在,我要做的就是添加一点CSS,让它表现得很好(目前还不是这样)。我会把这些放在 cubes.sytweb.css 文件,像往常一样存储在我们的 data 目录:

/* fixed full screen background image
 * as explained on http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
 *
 * syt update: set z-index=0 on the img instead of z-index=1 on div#page & co to
 * avoid pb with the user actions menu
 */
img#bg-image {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
}

div#page, table#header, div#footer {
    background: transparent;
    position: relative;
}

/* add some space around the logo
 */
img#logo {
    padding: 5px 15px 0px 15px;
}

/* more dark font for metadata to have a chance to see them with the background
 *  image
 */
div.metadata {
    color: black;
}

您可以在这里看到引用页面中解释的内容,在注释中只解释了一点修改,加上一些额外的规则,使事情变得更清晰:

  • 徽标周围有点填充物

  • 默认情况下在内容下方显示的较暗的元数据(页面中的白色框架)

为了让这个CSS文件在站点的任何地方都能使用,我必须修改 uiprops.py 以上介绍的文件:

STYLESHEETS = sheet['STYLESHEETS'] + [data('cubes.sytweb.css')]

注解

sheet 是包含已由进程定义的值的另一个预定义变量 :file: uiprops.py``文件,尤其是cubicWeb的文件。

在这里,除了CubicWeb的基本CSS文件之外,我们只需要我们的CSS,因此我们重新定义了 STYLESHEETS 现有CSS的变量(通过 sheet 变量)。我也可以这样做:

sheet['STYLESHEETS'].append(data('cubes.sytweb.css'))

但这并不有趣,因为我们没有看到覆盖机制…

此时,网站应该开始看起来不错,背景图像的大小正在调整以适应屏幕。

../../_images/tutos-photowebsite_background-image.png

最后一点:让我们自定义CubicWeb的CSS以减少橙色……只需添加

contextualBoxTitleBg = incontextBoxTitleBg = '#AAAAAA'

重新加载我们刚才看到的页面,我们知道有一个漂亮的灰色盒子而不是橙色的:

../../_images/tutos-photowebsite_grey-box.png

这是因为CubicWeb的CSS包含一些变量,这些变量由中定义的值展开。 uiprops.py 文件。在我们的例子中,我们控制了CSS的属性 background 具有css类的框的属性 contextualBoxTitleBgincontextBoxTitleBg .

步骤2:配置框

向用户提供一些使用应用程序的方法。我们先在 views.py 文件:

from cubicweb.predicates import none_rset
from cubicweb.web.views import bookmark
from cubicweb_zone import views as zone
from cubicweb_tag import views as tag


# change bookmarks box selector so it's only displayed on startup views
bookmark.BookmarksBox.__select__ = bookmark.BookmarksBox.__select__ & none_rset()
# move zone box to the left instead of in the context frame and tweak its order
zone.ZoneBox.context = 'left'
zone.ZoneBox.order = 100
# move tags box to the left instead of in the context frame and tweak its order
tag.TagsBox.context = 'left'
tag.TagsBox.order = 102
# hide similarity box, not interested
tag.SimilarityBox.visible = False

我们的想法是移动左栏中的所有框,这样我们就可以获得更多的照片空间。现在,重要的是:我想要一个类似于标签框的框,但是要处理 Person displayed_on File 关系。我们可以通过添加 AjaxEditRelationCtxComponent 我们视图的子类,如下所示:

from cubicweb import _
from logilab.common.decorators import monkeypatch
from cubicweb import ValidationError
from cubicweb.web.views import uicfg, component
from cubicweb.web.views import basecontrollers


# hide displayed_on relation using uicfg since it will be displayed by the box below
uicfg.primaryview_section.tag_object_of(('*', 'displayed_on', '*'), 'hidden')


class PersonBox(component.AjaxEditRelationCtxComponent):
    __regid__ = 'sytweb.displayed-on-box'
    # box position
    order = 101
    context = 'left'
    # define relation to be handled
    rtype = 'displayed_on'
    role = 'object'
    target_etype = 'Person'
    # messages
    added_msg = _('person has been added')
    removed_msg = _('person has been removed')
    # bind to js_* methods of the json controller
    fname_vocabulary = 'unrelated_persons'
    fname_validate = 'link_to_person'
    fname_remove = 'unlink_person'


@monkeypatch(basecontrollers.JSonController)
@basecontrollers.jsonize
def js_unrelated_persons(self, eid):
    """return tag unrelated to an entity"""
    rql = "Any F + ' ' + S WHERE P surname S, P firstname F, X eid %(x)s, NOT P displayed_on X"
    return [name for (name,) in self._cw.execute(rql, {'x' : eid})]


@monkeypatch(basecontrollers.JSonController)
def js_link_to_person(self, eid, people):
    req = self._cw
    for name in people:
        name = name.strip().title()
        if not name:
            continue
        try:
            firstname, surname = name.split(None, 1)
        except:
            raise ValidationError(eid, {('displayed_on', 'object'): 'provide <first name> <surname>'})
        rset = req.execute('Person P WHERE '
                           'P firstname %(firstname)s, P surname %(surname)s',
                           locals())
        if rset:
            person = rset.get_entity(0, 0)
        else:
            person = req.create_entity('Person', firstname=firstname,
                                            surname=surname)
        req.execute('SET P displayed_on X WHERE '
                    'P eid %(p)s, X eid %(x)s, NOT P displayed_on X',
                    {'p': person.eid, 'x' : eid})


@monkeypatch(basecontrollers.JSonController)
def js_unlink_person(self, eid, personeid):
    self._cw.execute('DELETE P displayed_on X WHERE P eid %(p)s, X eid %(x)s',
                     {'p': personeid, 'x': eid})

您基本上可以使用一些类属性进行子类配置。这个 fname_* 属性给出了应该在JSON控件上定义的方法的名称,以使小部件的Ajax部分工作:一个用于获取词汇表,一个用于添加关系,另一个用于删除关系。这些方法必须以 js_ 前缀和使用添加到控制器 @monkeypatch 装饰师。在我的例子中,最复杂的方法是添加一个关系,因为它试图查看这个人是否已经存在,否则会自动创建它,假设用户输入了“名字姓氏”。

让我们看看它在文件主视图上的外观:

../../_images/tutos-photowebsite_boxes.png

太好了,现在我很容易把我的照片链接到人,而不是标记他们。此外,参观者可以得到这两条信息的一致显示。

注解

UI组件系统已在中重构 CubicWeb 3.10 也介绍了 AjaxEditRelationCtxComponent 班级。

步骤3:配置方面

我们今天要添加的最后一个特性是方面配置。如果您访问了“/file”URL,您将看到一组“facets”出现在左边的列中。方面提供了一种直观的方法来逐步构建查询,通过向用户建议各种方法来限制结果集。例如,CubicWeb根据创建实体的人提出要限制的方面;标记多维数据集根据标记提出要限制的方面;Zoe多维数据集根据地理位置提出要限制的方面,等等。在这个要点中,我想提出一个方面来限制基于图片上显示的人。为此,在 cubicweb.web.facet 模块,它只需要使用类属性进行配置,就像我们在框中所做的那样。在我们的例子中,我们将定义 RelationFacet .

注解

因为这是用户界面的东西,我们将继续在下面添加代码 views.py 文件。尽管我们开始有很多不同的代码,所以现在是将视图模块分为 view 包裹。对于简单的应用程序(粘合)多维数据集,我们可以开始使用下面的布局:::

views/__init__.py   # uicfg configuration, facets
views/layout.py     # header/footer/background stuff
views/components.py # boxes, adapters
views/pages.py      # index view, 404 view
from cubicweb.web import facet


class DisplayedOnFacet(facet.RelationFacet):
    __regid__ = 'displayed_on-facet'
    # relation to be displayed
    rtype = 'displayed_on'
    role = 'object'
    # view to use to display persons
    label_vid = 'combobox'

假设我们还想根据 visibility 属性。这更简单,因为我们只需要从 AttributeFacet 班级:

class VisibilityFacet(facet.AttributeFacet):
    __regid__ = 'visibility-facet'
    rtype = 'visibility'

现在,如果我在我的网站上搜索一些图片,我可以获得以下方面:

../../_images/tutos-photowebsite_facets.png

注解

默认情况下,方面必须适用于结果集中的每个实体,并至少提供两个要显示的词汇表元素(例如,您不会看到 created_by 方面(如果同一用户创建了所有实体)。这可以解释为什么你看不到你的…

结论

我们开始看到框架提供的基础设施背后的力量,无论是在纯UI(CSS、javascript)方面还是在Python方面(组件的高级通用类,包括框和方面)。我们现在有了一个功能齐全、外观个性化的网站,只需几行代码。

当然,随着时间的推移,我们可能会想要更多,但现在我们可以集中精力制作好的照片,出版专辑,并与朋友分享…