CMS Website Development

Hệ quản trị nội dung

Hệ quản trị nội dung hay còn được gọi hệ thống quản trị nội dung hoặc Content Management System (CMS) là phần mềm để tổ chức và tạo môi trường cộng tác thuận lợi nhằm mục đích xây dựng hệ thống tài liệu và các loại nội dung khác một cách thống nhất.

Ghi chú

Tham khảo thêm tại Wikipedia

Có rất nhiều nền tảng CMS nổi tiếng và phổ biến hiện nay như Wordpress, Magneto (hiện là Adobe Commerce), Joomla,... Và Odoo cũng phát triển một nền tảng CMS riêng với đầy đủ các tính năng, được tích hợp trong hai module websiteweb_editor. Chỉ với các thao tác kéo thả, người dùng có thể dễ dàng tạo ra một trang web trong một vài phút. Tuy nhiên để thêm các tính năng mới cho giao diện thì lại không hề dễ dạng như vậy. Trong phần này, chúng ta cùng tìm hiểu cách tạo ra một website có tính tương tác cao.

Các chủ đề sẽ được đề cập ở phần này

Quản lí Assets tĩnh

Giống như đa số các trang webiste hiện nay, việc quản lí các nội dung tĩnh (static assets) rất quan trọng trong việc đưa đến cho người dùng một trải nghiệm mượt mà. Khi website được load dữ liệu, các file tĩnh sẽ tạo ra các request riêng biệt tới server, lượng request càng nhiều trang website sẽ load càng chậm. Odoo cũng khắc phục việc này với cách triển khai riêng của họ.

Điểm khác biệt trong cách quản lí Assets tĩnh của Odoo

Một trường hợp phổ biến là khi chúng ta muốn load một vài assets nhưng không muốn load toàn bộ các assets lên website. Các assets này lại thuộc các module khác nhau và không chia sẻ code cùng nhau. Nên việc load assets sẽ gặp khó khăn trong Odoo.

Để khắc phục tình trạng load quá nhiều assets không cần thiết, Odoo sử dụng một khái niệm gọi là Assets Bundles - các gói assets. Nhiệm vụ của các gói này là kết hợp tất cả file JavaScript và CSS trong một file và giảm kích thước của file này. Các file JavaScript và CSS khi kết hợp sẽ bị xoá đi các comments, các khoảng trống thừa, giúp kích thước file nhỏ và tăng tốc độ loading của page.

Bằng cách này AssetsBundle không chỉ tập hợp các file, mà còn được đóng gói với nhiều tính năng.

Odoo sẽ quản lí các assets tĩnh này thông qua class AssetsBundle có thể tìm thấy trong đường dẫn

/odoo/addons/base/models/assetsbundle.py

Một số các Assets Bundle được sử dụng trong Odoo:

  • web.assets_common : Bao gồm các tiện ích cơ bản cho các ứng dụng như JQuerry, Underscore.Js, Font Awesome,...

  • web.assets_backend : Được sử dụng trong phần backend của Odoo, bao gồm các view, field widget,...

  • web.assets_frontend : Được sử dụng trong phần frontend của Odoo.

  • website.assets_editorweb_editor.summernote: Bundle này chứa những code của tính năng chỉnh sửa và kéo-thả của Website.

  • web.report_assets_common : Bundle này sẽ load bố cục của report được sinh ra thông qua QWeb report.

Ghi chú

Một số bundle được sử dụng cho các ứng dụng cụ thể: point_of_sale.assets, survey.survey_assets, mass_mailing.layout và website_slides.slide_embed_assets.

Tuỳ chỉnh Assets

Thông thường, để thêm các assets chúng ta sẽ đặt file JS, CSS, SCSS,.. vào đúng bundle cần chọn.

Ví dụ: bạn muốn file CSS của mình được áp dụng cho các module của Odoo, bạn cần phải đặt file CSS vào web.assets_common

Trong một số trường hợp nhất định, chúng ta sẽ cần phải tạo ra một AssetsBundle hoàn toàn mới. Và chúng ta hoàn toàn có thể làm được điều đó.

Dưới đây chúng ta sẽ cùng tạo một Assets Bundle mới:

Bước 1

Tạo một QWeb template mới và thêm các file JS, CSS, SCSS của bạn vào file này

<template id="assets_bundle_demo" name="New Assets Bundle">
   <link rel="stylesheet" type="text/scss" href="/module/static/src/scss/scss_demo.scss"/>
   <link rel="stylesheet" type="text/css" href="/module/static/src/scss/css_demo.css"/>
   <script type="text/JavaScript" src="/module/static/src/js/widgets/javascript_demo.js"/>
</template>
Bước 2

Tại QWeb template mà bạn muốn sử dụng bundle mới, hãy dùng t-call-assets

<template id="any_page">
...
   <head>
      <t t-call-assets="module.assets_bundle_demo" t-js="false"/>
      <t t-call-assets="module.assets_bundle_demo" t-css="false"/>
   </head>
...

Ghi chú

Thêm một bundle mới rất hiếm khi thực hiện. Bạn sẽ cần phải làm điều này khi muốn phát triển Page/App mà không muốn sử dụng các tính năng của Odoo CMS.

Mẹo

Odoo sẽ chỉ tạo ra các Assets một lần. Với những thay đổi trong quá trình phát triền sẽ cần phải khởi động lại server. Để tránh việc này, bạn hãy thêm --dev xml vào command-line. Điều này sẽ giúp cho các assets được load lại ngay lập tức mà không cần khởi động lại server

Ở topic tiếp theo, chúng ta sẽ cùng tìm hiểu cách thêm file JS/CSS tuỳ chỉnh vào một bundle có sẵn.

Thêm CSS và JavaScript cho trang Website

Trong topic này, chúng ta sẽ cùng tìm hiểu cách thêm CSS và JavaScript vào website.

Tổng quan

Chúng ta sẽ tạo một module hoàn toàn mới có tên là education.

Ghi chú

Bạn có thể tìm thấy cách tạo module mới với command-line tại đây.

Vì sẽ phải thêm các file JS/CSS/SCSS dẫn đến việc chỉnh sửa trên website, nên chúng ta cần thêm website vào file __manifest__.py như sau:

...
'depends': ['base', 'website'],
...

Các bước thực hiện

Bước 1

Tạo một file templates.xml trong thư mục views và thêm một template rỗng như bên dưới:

<odoo>
   <template id="education_assets_frontend" inherit_id="web.assets_frontend">
      <xpath expr="." position="inside">
         <!-- adding some code here -->
      </xpath>
   </template>
</odoo>

(Đừng quên thêm file này trong __manifest__.py).

Bước 2

Thêm đường dẫn tham chiếu của các file JavaScript và CSS vào template trên:

<link href="/education/static/src/scss/education_style.scss" rel="stylesheet" type="text/scss"/>
<link href="/education/static/src/css/education_style.css" rel="stylesheet" type="text/css"/>
<script src="/education/static/src/js/education.js" type="text/javascript" />
Bước 3

Thêm một đoạn code CSS vào static/src/css/education_style.css:

body main {
   background: rgb(171,214,153);
   background: linear-gradient(30deg, rgba(171,214,153,1) 50%, rgba(255,226,106,1) 100%);
}
Bước 4

Thêm một đoạn code SCSS vào static/src/css/education_style.scss:

$edu-text-color: #FFFFFF;

footer.o_footer {
   color: $edu-text-color;
}
Bước 5

Thêm một đoạn code JavaScript vào /static/src/js/education.js:

odoo.define('education', function (require) {
   var core = require('web.core');
   alert(core._t('This is a test notification'));
   return {}
});

Khởi chạy server và update module, bạn sẽ nhận được website mới với giao diện màu sắc và thông báo như hình bên dưới:

Viindoo CMS

Ảnh 14.1 - Website sau khi được thêm file JavaScript, SCSS, CSS

Ghi chú

Odoo cải thiện hiệu năng bằng cách tải JavaScript tối thiểu cho người dùng. Tất cả file JavaScript sẽ được tải chậm cho đến khi trang được load hết.

Tạo mới và tuỳ chỉnh Template QWeb

Trong topic này, chúng ta sẽ thêm giao diện cho website, hiển thị các khoá học và thông tin tương ứng cho khoá học trên website với QWeb Template.

Các bước thực hiện

Để có thể thực hiện các thao tác tại topic này, chúng ta có thể sử dụng lại ví dụ từ topic Adding CSS and JS for a website.

Bước 1

Thêm controller vào file controllers/main.py như bên dưới:

from odoo import http
from odoo.http import request


class Main(http.Controller):
   @http.route('/course', type='http', auth="user", website=True)
   def course(self):
      return request.render(
         'education.course', {
            'courses': request.env['course'].search([]),
         })

Ghi chú

Tham số website=True giúp cho website hỗ trợ đa ngôn ngữ, đồng thời cải thiện việc hiển thị các exception một cách trực quan.

Bước 2

Thêm template mới vào file views/templates.xml như bên dưới (nhớ rằng hãy thêm views/templates.xml vào file manifest):

<odoo>
   <template id="course">
      <t t-call="website.layout">
         <!-- add some code here-->
      </t>
   </template>
</odoo>
Bước 3

Trong website.layout thêm đoạn code hiển thị các khoá học như phía dưới:

<div class="container">
   <div class="row">
      <t t-foreach="courses" t-as="course">
         <div class="col-sm-4">
            <div class="card m-3" style="width: 20rem;">
               <img src="https://image.freepik.com/free-photo/coding-man_1098-18084.jpg"
                  class="card-img-top" alt="image"/>
               <div class="card-body" id="card_body">
                  <h3 t-field = "course.name" class="card-title"/>
                  <div class = "mt-1" id="tags">
                  <p t-field = "course.type_of_course" class="badge bg-danger"/>
                  <p t-field = "course.level_of_course" class="badge badge-success"/>
               </div>
                  <p class="card-text">With <t t-esc = "course.number_of_lesson" /> lessons</p>
                  <a href="#" class="btn btn-primary">Start learning</a>
               </div>
            </div>
         </div>
      </t>
   </div>
</div>

Bây giờ, bạn hãy khởi động server và truy cập vào đường dẫn http://your-server-url:8069/course để nhìn thấy kết quả.

Viindoo CMS

Ảnh 14.2 - Giao diện của các khoá học sau khi hoàn thành các bước trên.

Cơ chế hoạt động

QWeb là một công cụ tạo mẫu cơ bản được sử dụng trong Odoo. Nó là một công cụ tạo mẫu XML và được sử dụng với mục đích chính là tạo ra các thành phần và trang cho HTML.

bước 1, chúng ta có controller có thể tuỳ chỉnh các giá trị. Những giá trị này sẽ được truyền từ controller tới bản mẫu của QWeb.

Ở những bước tiếp theo, chúng ta tạo ra một bản mẫu đặt tên là course được sử dụng để tạo ra code HTML khi hiển thị danh sách các khoá học. Tất cả các phần code này được đặt trong thẻ t với thuộc tính t-call. Khi t-call được gọi, Odoo sẽ render trang web với bản mẫu website.layout và chèn nội dung của chúng ta vào bên trong bản mẫu này.

Với cách này, chúng ta sẽ có một trang web Odoo với đầy đủ: menu, footer và tính năng chỉnh sửa trang mà không lặp lại code ở tất cả các page.

Một số chỉ thị của QWeb template (Template directives) được sử dụng trong ví dụ trên:

t-foreach

Khi cần làm việc với recordsets, QWeb template cung cấp cho chúng ta một cơ chế để lặp qua các bản ghi có trong một recordsets. Ví dụ:

<t t-foreach="[apple, orange, banana]" t-as="fruit">
   <p><t t-esc ="fruit"/></p>
</t>

Sẽ được render thành đoạn code như dưới:

<p>apple</p>
<p>orange</p>
<p>banana</p>

t-field

Khi sử dụng t-field, người dùng có thể thay đổi nội dung của thẻ với t-field trong chế độ chỉnh sửa (edit-mode). Khi lưu lại, dữ liệu sẽ được cập nhật vào trong database. Tuy nhiên chỉ với những người dùng có quyền (permission) mới có thể sử dụng tính năng này.

Ghi chú

Điểm khác biệt giữa t-esct-fieldt-field hiển thị dữ liệu dựa trên ngôn ngữ của người dùng, trong khi t-esc hiển thị dữ liệu (raw-value) từ trong database.

t-call

Khi phát triển một ứng dụng lớn, việc quản lí các templates càng trở nên khó khăn. Và việc chia nhỏ để quản trị cần thiết để việc phát triển trở nên hiệu quả hơn. Ngoài ra, việc chia nhỏ các template giúp cho bạn có thể sử dụng lại code của mình với lệnh t-call. ví dụ:

<template id="root_template">
   <div> Test Template </div>
</template>
<template id="child_template">
   <t t-call="root_template"/>
</template>

Tham khảo thêm

Để thiết kế QWeb Templates một cách hiệu quả không phải là việc đơn giản. Ngoài những phần đã được đề cập trong ví dụ trên còn có thể sử dụng view inheritance để tuỳ chỉnh các templates đã có. Các template directives khác mà Odoo mang đến có thể liệt kê như:

  • t-attt-attf: Directive có thể giúp đặt các giá trị động (dynamic-value) cho các thuộc tính

  • t-if: Directive có chức năng tương đương với câu lệnh if trong python

Ngoài ra bạn có thể tham khảo thêm các nguồn khác:

  • Boostrap: Bạn nên sử dụng Boostrap cho việc thiết kế giao diện trở nên mạch lạc hơn Boostrap Docs

  • Kế thừa view trong Odoo : Backend Views

  • Những kiến thức về controllerrouting : Web Server Development

Quản lí điều hướng động

Ở topic trước, chúng ta đã học được cách thêm và tuỳ chỉnh một QWeb template mới. Nhưng vẫn còn một số điều chưa được hoàn thiện ở trang web của chúng ta:

  • Khi nhấn vào nút Start Learning, chúng ta vẫn chưa đặt đường dẫn cho từng khoá học.

  • Ảnh cho từng khoá học chưa được lưu trữ riêng biệt.

Trong topic này, chúng ta cùng tìm hiểu cách thêm những URL cho từng khoá học hay còn gọi là quản lí các điều hướng động (Managing dynamic routes)

Các bước thực hiện

Chúng ta vẫn sử dụng ví dụ từ topic trước với model course. Hãy thêm vào model hai trường mới như phía dưới:

from odoo import models, fields

class Course(models.Model):
    _name = 'course'
    _description = 'Course in education'

    name = fields.Char(string = 'Name', required=True)
    number_of_lesson = fields.Integer(string='Number of lessons', help='Number of lesson in this course')
    level_of_course = fields.Selection(string='Level of course', help='This field will determine level of this course',
                                       selection = [('beginner', 'Beginner'),
                                                    ('intermediate', 'Intermediate'),
                                                    ('professional', 'Professional')])
    type_of_course = fields.Selection(string='Type of course', help='This field will determine type of this course',
                                      selection = [('course', 'Course'),
                                                   ('career', 'Career Path')],
                                      default = 'course')
    # Adding new fields
    image = fields.Binary(string='Image', attachment=True)
    html_description = fields.Html(string='Description')
Bước 1:

Thêm một điều hướng mới (route) vào file controllers/main.py như bên dưới:

@http.route('/course/<model("course"):course>', type='http', auth="user", website=True)
def course_detail(self, course):
  return request.render( 'education.course_detail', {'courses': course,})
Bước 2:

Thêm một template mới dành cho course_detail để hiển thị chi tiết của khoá học vào file templates.xml như bên dưới:

<template id="course_detail" name="Courses Detail">
   <t t-call="website.layout">
      <t t-foreach="courses" t-as="course">
         <section class="pt32 pb32 bg-secondary oe_custom_bg">
            <div class="container text-center">
               <h1><t t-esc = "course.name"/></h1>
               <a href="#" class="btn btn-primary mt-1"
                  style="box-shadow: 2px 2px 0px 0px rgba(255,255,255,1);
                  width: 5rem;">
                   Start
               </a>
            </div>
         </section>
         <div class="container mt-3">
            <div class="text-center">
               <h2><b>Overview</b></h2>
            </div>
            <div t-field="course.html_description"/>
         </div>
      </t>
   </t>
</template>
Bước 3:

Thêm đường dẫn động cho ảnh của khoá học và chi tiết khoá học vào template course trong file templates.xml như bên dưới:

 <template id="course" name="Course">
    <t t-call="website.layout">
      ...
      <span t-field="course.image" t-options="{
         'widget': 'image',
         'class': 'card-img-top'}"/>
      ...
      <a t-attf-href="/course/#{course.id}" class="btn btn-primary">
         Start learning</a>
      ...
    </t>
</template>

Cuối cùng, cập nhật lại module education, khởi động server và truy cập vào đường dẫn http://your-server-url:8069/course để thấy kết quả.

Viindoo CMS

Ảnh 14.3 - Giao diện của các khoá học sau khi hoàn thành các bước trên và thêm ảnh khoá học.

Viindoo CMS

Ảnh 14.4 - Giao diện của chi tiết các khoá học sau khi hoàn thành các bước trên và thêm mô tả khoá học.

Cơ chế hoạt động

Tương tự như topic trước, chúng ta thêm một route để điều hướng đến trang chi tiết của các khoá học. Nhưng ở đây là một dynamic route.

Odoo sẽ sinh ra một URL dạng /course/course.id và render thành course/1, tương ứng với id của bản ghi có trong model course. Khi URL này được truy cập, Odoo sẽ fetches bản ghi course đó và đưa vào function course_detail() như một tham số. Sau đó sẽ render template course_detail với tham số được truyền vào.

Ở các bước tiếp theo, chúng ta xây dựng một template QWeb để render ra trang chi tiết khoá học và thêm các điều hướng động vào nút Start learning và ảnh của từng khoá học.

Ghi chú

Odoo sử dụng thư viện werkzeug để xử lí các request HTTP. Bạn có thể thấy ở cú pháp /course/<model("course"):course> trong ví dụ trên. Bạn có thể điều hướng bằng những cách khác:

  • /page/<int:page>

  • /page/<any (about, help):page_name>

  • /pages/<page>

  • /pages/<category>/<int:page>

Bạn có thể tham khảo thêm tại đây về routes với thư viện werkzeug

Cung cấp snippets tĩnh cho người dùng

Tổng quan

Odoo mang đến một tính năng đặc biệt trong website editor, đó là người dùng có thể kéo thả các block (khối mã) vào trong trang web và chỉnh sửa chúng nếu cần thiết. Các block này là những snippets (đoạn code đã đóng gói). Có nhiều kiểu snippets nhưng có thể phân thành hai loại chính:

  • Static snippet: Cố định và không thay đổi cho đến khi người dùng muốn tuỳ chỉnh chúng.

  • Dynamic snippet: Dựa vào sự thay đổi dữ liệu trong database và thay đổi khi giá trị của bản ghi thay đổi.

Trong topic này, chúng ta sẽ tìm hiểu cách tạo ra một static snippet.

Các bước thực hiện

Trong topic này chúng ta cùng tạo ra một snippet nhỏ với nội dung là bài học trong khoá học. Layout của snippet bài học sẽ sử dụng Boostrap Media Object để hiển thị hình ảnh và nội dung một cách ngắn gọn. Chúng ta vẫn sử dụng ví dụ từ những topic trước với model course

Bước 1:

Thêm một file mới có tên views/s_course_lessons.xml như bên dưới, đồng thời thêm file này vào trong manifest:

<odoo>
   <!-- Adding new template here -->
</odoo>
Bước 2:

Trong file views/s_course_lessons.xml, tạo một template mới có tên là Course Lessonsid="s_course_lessons":

<template id="s_course_lessons" name="Course Lessons">
   <section class="pt-3 pb-3">
      <div class="container">
         <div class="media">
            <img class="mr-3" src="https://img.icons8.com/wired/50/000000/paper.jpg" alt="Generic placeholder image"/>
            <div class="media-body">
               <h5 class="mt-0">Lesson</h5>
               Lorem Ipsum is simply dummy text of the printing and typesetting industry.
               Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
               when an unknown printer took a galley of type and scrambled it to make a type specimen book. sum.
            </div>
         </div>
      </div>
   </section>
</template>

Mẹo

Luôn dùng thẻ <section> để snippet tránh lỗi no location to drop-in

Bước 3:

Tạo một file mới tên views/snippets.xml, thêm đoạn code như phía dưới vào trong file và thêm file này vào manifest:

<odoo>
   <template id="course_snippets_options" inherit_id="website.snippets">
      <xpath expr="//div[@id='snippet_structure']/div[hasclass('o_panel_body')]" position="inside">
         <t t-snippet="education.s_course_lessons"
            t-thumbnail="/education/static/src/img/icon.webp"/>
      </xpath>
   </template>
</odoo>
Bước 4:

Thêm các ảnh của snippet và ảnh bài học vào trong đường dẫn /education/static/src/img/. Khởi động lại server và mở edit-mode trong website bạn sẽ thấy kết quả như dưới. Snippet có thể kéo-thả, chỉnh sửa ngay trên giao diện website.

Viindoo CMS

Ảnh 14.5 - Giao diện của snippet trong chế độ Website Editor.

Cơ chế hoạt động

Sau khi tạo ra một QWeb Template ở bước 1 và bước 2 để hiển thị thông tin các bài học, chúng ta thêm snippet mới vào trong một list các snippet của Odoo. Lúc này bạn sẽ cần kế thừa website.snippets.

Ghi chú

website.snippets là một template chứa các snippets cơ bản và bạn có thể khám phá rất nhiều snippets có sẵn của Odoo ở file /addons/website/views/snippets/snippets.xml

Trong chế độ website editor, các snippets sẽ được chia ra thành từng phần dựa trên công dụng của chúng. Ở trong ví dụ này chúng ta thêm snippet mới vào section Structure thông qua thẻ xpath. Thuộc tính t-snippett-thumbnail sẽ cung cấp thông tin của snippet bao gồm XML ID của QWeb template và ảnh hiển thị của snippet để thêm vào list các snippets đã có.

Snippets options (một số những lựa chọn cho snippet) hỗ trợ các thuộc tính như data-exclude, data-drop-neardata-drop-in để xác định xem snippet này sẽ được đặt ở vị trí nào khi kéo-thả.

Fact

Static snippet đơn giản chỉ là một khối mã HTML.

Cung cấp snippets động cho người dùng

Trong topic này, chúng ta cùng tìm hiểu hiểu cách tạo ra một dynamic snippet.

Các bước thực hiện

Chúng ta vẫn sẽ sử dụng ví dụ từ topic trước với model course. Snippet trong phần này sẽ là một slide các hình ảnh của các khoá học. Layout của snippet sẽ sử dụng layout Boostrap Carousel

Bước 1:

Thêm một file mới có tên views/s_course_carousel.xml và thêm một template s_course_carousel để render slide ảnh, đồng thời thêm file này vào trong manifest:

<odoo>
   <template id="s_course_carousel" name="Course Carousel">
      <section class="pt-3 pb-3 s_course_carousel">
         <div id="carouselSlides" class="carousel slide" data-ride="carousel">
            <div class="carousel-inner">
               <div class="carousel-item active">
                  <img id = "img_carosouel-1"
                     src="https://image.freepik.com/free-photo/coding-man_1098-18084.jpg"
                     class="d-block w-100"/>
               </div>
               <div class="carousel-item">
                  <img id = "img_carosouel-2"
                     src="https://image.freepik.com/free-photo/coding-man_1098-18084.jpg"
                     class="d-block w-100"/>
               </div>
               <div class="carousel-item">
                  <img id = "img_carosouel-3"
                     src="https://image.freepik.com/free-photo/coding-man_1098-18084.jpg"
                     class="d-block w-100"/>
               </div>
            </div>
         </div>
      </section>
   </template>
</odoo>
Bước 2:

Trong file views/snippets.xml thêm đoạn code để hiển thị snippet trên giao diện web như phía dưới (đừng quên thêm ảnh hiển thị snippet vào thư mục /static/src/img/):

<template id="course_snippets_options" inherit_id="website.snippets">
   <xpath expr="//div[@id='snippet_structure']/div[hasclass('o_panel_body')]" position="inside">
      ...
         <!-- Dynamic snippets -->
      <t t-snippet="education.s_course_carousel"
         t-thumbnail="/education/static/src/img/ranking.jpg"/>
   </xpath>
</template>
Bước 3:

Thêm file mới /static/src/snippets.js để có thể render được template silde ảnh như phía dưới:

odoo.define('course.carousel.snippet', function (require) {
   'use strict';
var publicWidget = require('web.public.widget');

publicWidget.registry.books = publicWidget.Widget.extend({
   selector: '.s_course_carousel',
   disabledInEditableMode: false,
   start: function (){
   var self = this;
      this._rpc({
         model: 'course',
         method: 'search_read',
         domain: [],
         fields:['image'],
         orderBy: [{name: 'write_date', asc: false}],
         limit: 3
         }).then(function (course) {
            $('#img_carosouel-1').attr('src', 'data:image/;base64,' + course[0].image)
            $('#img_carosouel-2').attr('src', 'data:image/;base64,' + course[1].image)
            $('#img_carosouel-3').attr('src', 'data:image/;base64,' + course[2].image)
        })
    }
});
});
Bước 4:

Thêm file /static/src/snippets.js vào website.assets_frontend để có thể load file này lên trang web khi khởi động server. Tạo file mới có tên views/assets.xml và thêm vào file manifest:

<odoo>
   <template id="assets_frontend" inherit_id="website.assets_frontend">
      <xpath expr="." position="inside">
         <script src="/education/static/src/js/snippet.js" type="text/javascript" />
      </xpath>
   </template>
</odoo>

Sau khi cập nhật module, bạn hãy khởi động server và sẽ thấy snippet có tên Course Carousel như hình phía dưới

Ảnh 14.6 - Giao diện của dynamic snippet trong chế độ Website Editor.

Ảnh 14.7 - Giao diện của snippet sau khi thêm vào Website.

Cơ chế hoạt động

Tương tự như khi tạo một static snippet, chúng ta cần tạo một template để render ra giao diện cho snippet. Ở ví dụ này, chúng ta sử dụng layout Carousel của Boostrap để có thể tạo nhanh một bố cục cho slide ảnh như mong muốn của chúng ta.

Bước tiếp theo, chúng ta khai báo thông tin của snippet mới vào website.snippets để có thể hiển thị snippet lên giao diện Website Editor.

Nhưng nếu dừng ở đây, chúng ta mới chỉ có khung cho một slide ảnh. Slide này đang hoàn toàn rỗng, chưa có data. Do đó ở bước 3, chúng ta thêm một đoạn code JavaScript để có thể lấy dữ liệu từ trong database và đưa vào các vị trí đã tạo sẵn ở template.

Để ánh xạ một đối tượng JavaScript sang các phẩn từ HTML, Odoo sử dụng PublicWidget trong đó có một thuộc tính quan trọng là selector. Thông qua selector, bạn có thể truyền dữ liệu đến các phần tử HTML nhờ các CSS selector của các phần tử đó. Vậy nên bạn sẽ cần các CSS selector để có thể tạo đích đến cho các dữ liệu từ database.

Bạn có thể nhận thấy một phần code của _rpc trong đoạn mã JavaScript trên. RPC (Remote procedure call) được gọi khi có một request đến từ phía client về server. Ở đây, khi this._rpc() được gọi nó cần các tham số truyền vào để thực hiện một method ORM. Cụ thể là một lệnh tìm kiếm trong model course, lấy ra các dữ liệu về image của các bản ghi và sắp xếp theo thứ tự được tạo gần đây. Chúng ta sẽ quay lại với method _rpcMaking RPC calls to the server.

Cuối cùng, chúng ta thêm file JS vào bundle website.assets_frontend.

Ghi chú

Bạn có thể khám phá các snippet options ở trong file addons/website/static/src/js/editor/snippets.options.js

Nhập thông tin của người dùng từ website

Trong quá trình phát triển Website, bạn sẽ cần phải tạo form để có thể lấy được dữ liệu nhập vào của người dùng. Ở topic này, chúng ta cùng tạo ra một trang HTML cho phép người dùng có thể báo lỗi về một khoá học trên hệ thống.

Chuẩn bị

Chúng ta sẽ tiếp tục sử dụng module education và cần phải thêm một model mới để lưu trữ vấn đề của khoá học được gửi từ người dùng.

  • Thêm model course.issue và thêm một trường One2Many vào model course như phía dưới:

class Course(models.Model):
   ...
   issue_ids = fields.One2many('course.issue', 'course_id')

class CourseIssue(models.Model):
   _name = 'course.issue'

   course_id = fields.Many2one('course', string = 'Course')
   submitted_by = fields.Many2one('res.users')
   issue_description = fields.Text()
  • Thêm group Course Issue vào trong backend view của course:

...
<group string="Course Issue">
   <field name="issue_ids" nolabel="1">
      <tree>
         <field name="create_date"/>
         <field name="submitted_by"/>
         <field name="issue_description"/>
      </tree>
   </field>
</group>
...
  • Thêm quyền truy cập vào model course.issue vào ir.model.acess.csv như bên dưới:

access_education_course_issue,education.course.issue,model_course_issue,base.group_user,1,1,1,1

Các bước thực hiện

Bước 1

Thêm một route mới vào file main.py như dưới:

@http.route('/course/submit-issues', type='http', auth="user", website=True)
def course_issues(self, **post):
   if post.get('course_id'):
      course_id = int(post.get('course_id'))
      issue_description = post.get('issue_description')
      request.env['course.issue'].sudo().create({
        'course_id': course_id,
        'issue_description': issue_description,
        'submitted_by': request.env.user.id
      })
      return request.redirect('/course/submit-issues?submitted=1')
    return request.render('education.course_issue_form',{
      'courses': request.env['course'].search([]),
      'submitted': post.get('submitted', False)
    })
Bước 2

Thêm một template mới vào file views/course_issue.xml như dưới:

<odoo>
   <template id="course_issue_form" name="Course Issues Form">
      <t t-call ="website.layout">
         <div class="container text-center">
            <!-- Adding some code here -->
         </div>
      </t>
   </template>
</odoo>
Bước 3

Thêm phần header có điều kiện vào template như dưới:

<t t-if="submitted">
   <h3 class="alert alert-success mt16 mb16">
      <i class="fa fa-thumbs-up"/>
      Course Issue submitted successfully
   </h3>
   <h1> Report the another course issue </h1>
</t>
<t t-else="">
   <h1> Report the book issue </h1>
</t>
Bước 4

Thêm phần <form> để gửi báo lỗi vào template như dưới:

<div class="row mt16 justify-content-center">
   <form method="post">
      <input type="hidden" name="csrf_token"
         t-att-value="request.csrf_token()"/>
      <div class="form-group">
         <label>Select Course</label>
         <select class="form-control" name="course_id">
            <t t-foreach="courses" t-as="course">
               <option t-att-value="course.id">
                  <t t-esc="course.name"/>
               </option>
            </t>
         </select>
      </div>
      <div class="form-group">
         <label>Issue Description</label>
         <textarea name="issue_description" class="form-control"
            placeholder="e.g. syntax errors, wrong terms"/>
      </div>
      <button type="submit" class="btn btn-primary">
         Submit
      </button>
   </form>
</div>

Cập nhật module education và truy cập đường dẫn /course/submit-issues để thấy kết quả. Từ trang này, bạn có thể gửi vấn đề về khoá học. Sau khi nhấn nút submit, bạn có thể tìm thấy các vấn đề khoá học trong giao diện backend

Viindoo CMS

Ảnh 14.8 - Giao diện của trang báo cáo lỗi khoá học.

Viindoo CMS

Ảnh 14.9 - Giao diện backend sau khi người dùng gửi vấn đề khoá học.

Cơ chế hoạt động

Ở bước 1, chúng ta tạo ra một route để có thể gửi các vấn đề khoá học. Tham số **post trong hàm course_issues sẽ chấp nhận mọi tham số truy vấn trong URL. Nhờ đó, bạn có thể lấy dữ liệu đã được submit từ tham số **post. Nếu chúng ta tìm được dữ liệu trong **post, chúng ta sẽ tạo một issue mới trong model course.issue và điều hướng người dùng đến trang báo lỗi với các tham số truy vẫn đã được sumit để người dùng nhận biết được vấn đề của họ đã gửi thành công và có thể gửi tiếp vấn đề khác nếu như người dùng có mong muốn.

Ghi chú

Trong phần route, chúng ta sử sudo() để tạo vấn đề khoá học vì người dùng thông thường (visitor) không có quyền truy cập vào model course.issue.

Ở các bước tiếp theo, chúng ta tạo template cho trang vấn đề khoá học. Bạn sẽ thấy một method có tên csrf_token(). Method này được dùng để tránh kỹ thuật tấn công Cross-Site Request Forgery (CSRF). Đây là một kỹ thuật tấn công vào người dùng, dựa vào đó hacker có thể thực thi những thao tác phải yêu cầu sự chứng thực. Nêú bạn không dùng method này trong form, người dùng sẽ không thể gửi được các vấn đề khoá học.

Mẹo

Trong một số trường hợp, bạn có thể tắt csrf xác thực bằng cách đặt csrf=False trong route như sau:

@http.route('/url', type='http',auth="user", website=True, csrf=False)

Mẹo

Bạn có thể hạn chế các request get bằng cách thêm tham số method như bên dưới:

@http.route('/url', type='http', method='POST' auth="user", website=True)

Quản lí các tuỳ chọn SEO

Odoo cung cấp tính năng SEO cho các templates. Tuy nhiên, một số templates được sử dụng trong nhiều URL. Ví dụ, trong một cửa hàng online, các trang được render với các template giống nhau nhưng khác về dữ liệu. Với những trường hợp như vậy, chúng ta sẽ phải xử lí dữ liệu SEO cho từng URL.

Nếu như bạn đã thử tuỳ chỉnh SEO trong Website cho từng khoá học, bạn sẽ nhận thấy khi thay đổi dữ liệu SEO cho một khoá học sẽ ảnh hưởng tới toàn bộ khoá học. Chúng ta sẽ giải quyết vấn đề đó trong phần này.

Các bước thực hiện

Bước 1

Thêm kế thừa website.seo.metadata cho model course như phía dưới:

...
class Course(models.Model):
   _name = 'course'
   _inherit = ['website.seo.metadata']
...
Bước 2

Thêm đoạn code vào route course_detail như phía dưới:

...
@http.route('/course/<model("course"):course>', type='http', auth="user", website=True)
def course_detail(self, course):
   return request.render('education.course_detail', {
                         'courses': course,
                         'main_object': course})
...

Cập nhật module education và thử thay đổi SEO trên từng khoá học, bạn sẽ thấy có sự khác biệt như bên dưới.

Viindoo CMS

Ảnh 14.10 - SEO metadata mặc định cho mọi URL về khoá học.

Viindoo CMS

Ảnh 14.11 - SEO metadata sau khi được gắn cho từng URL.

Cơ chế hoạt động

Để kích hoạt SEO trên từng bản ghi trong model, bạn sẽ cần kế thừa vào lớp website.seo.metadata. Điều này sẽ thêm một số fields và method vào model course, những trường và phương thức này được sử dụng để lưu trữ dữ liệu cho từng khoá học.

Mẹo

Bạn có thể khám phá các fields và methods của website.seo.metadata

ở đường dẫn sau addons/website/models/website.py

Các phần code về SEO được viết trong website.layout và sẽ lấy toàn bộ các thông tin về SEO từ recordset đưa vào main_object. Do đó, ở bước 2, chúng ta truyền vào một object course với khoá main_object, giúp cho website.layout lấy được tất cả các thông tin SEO về khoá học. Nếu như bạn không đưa main_object vào controller, thì template recordset sẽ được truyền vào như main_object. Đó là lí do bạn có thể thấy các khoá học đều có dữ liệu SEO giống nhau.

Ghi chú

Trong Odoo, bạn có thể thêm các thẻ <meta> tuỳ chỉnh cho OpenGraph và Twitter khi chia sẻ. Để thực hiện được điều này, bạn cần phải override lại _default_website_meta() sau khi đã kế thừa website.seo.metadata. Và khi chia sẻ URL của khoá học, bạn có thể thấy hình ảnh và các thông tin của khoá học trên nền tảng social media.

Quản lí sitemaps của website

Website sitemaps (Sơ đồ trang web) là một thành phần quan trọng với bất kỳ trang web nào. Công cụ tìm kiếm sẽ dùng sitemaps để đánh chỉ mục cho các trang trong Website. Trong topic này, chúng ta sẽ cùng tìm hiểu cách thêm trang của từng khoá học vào sitemaps.

Bạn có thể kiểm tra sitemap hiện tại của website trên Odoo bằng cách truy cập đường dẫn <your-odoo-server>/sitemap.xml. Và như bạn thấy nó không có các trang của khoá học.

Viindoo CMS

Ảnh 14.12 - Nội dung của sitemap.xml mặc định trong Odoo.

Các bước thực hiện

Bước 1

Import các các method dưới đây vào file main.py:

from odoo.addons.website.models.ir_http import sitemap_qs2dom
from odoo.addons.http_routing.models.ir_http import slug
Bước 2

Thêm method sitemap_course vào fiel main.py như bên dưới:

...
def sitemap_course(env, rule, qs):
   Courses = env['course']
   dom = sitemap_qs2dom(qs,'/course',Courses._rec_name)
   for f in Courses.sudo().search(dom):
      loc = '/course/%s' % slug(f)
      if not qs or qs.lower() in loc:
         yield{'loc':loc}
...
Bước 3

Thêm sitemap_course vào tham số của route course_detail:

...
@http.route('/course/<model("course"):course>', type='http', auth="user", website=True,
   sitemap=sitemap_course) #adding sitemap
...

Cập nhật module để áp dụng thay đổi. File sitemap.xml được sinh ra và lưu trữ trong Atttachments. Và file sẽ được sinh lại sau một khoảng thời gian nhất định. Để thấy được kết quả các bước trên, bạn cần phải xoá file sitemap đã có trong Atttachments. Hãy truy cập Setting|Technical|Database Structure|Attachments, tìm kiếm sitemap và xoá file. Bây giờ hãy truy cập URL /sitemap.xml và bạn sẽ thấy các trang khoá học đã được thêm vào sitemaps.

Viindoo CMS

Ảnh 14.13 - Nội dung của sitemap.xml sau khi đã hoàn thành các bước trên.

Cơ chế hoạt động

Ở bước đầu tiên, chúng ta import một vài method vào controller, trong đó:

  • slug dược sử dụng để tạo ra một URL thân thiện dựa trên tên của bản ghi.

  • sitemap_qs2dom: được sử dụng để tạo ra domain dựa trên route và chuỗi truy vấn.

Ở bước tiếp theo, chúng ta thêm một function sitemap_course(). Function này được gọi mỗi khi sitemap được sinh ra. Trong lúc thực thi, nó sẽ nhận vào ba tham số: env - enviroment trong Odoo, rule - quy tắc của route và qs - querry string. Trong function này, chúng ta sẽ tạo ra domain với sitemap_qs2dom. Sau đó, chúng ta sẽ sử dụng domain này để tìm kiếm các bản ghi của course, tên của các bản ghi này sẽ được sử dụng để sinh ra URL nhờ slug.

Cuối cùng, đưa sitemap_course() vào tham số sitemap trong route Course Detail.

Lấy thông tin quốc gia của người dùng truy cập website

Odoo CMS có hỗ trợ tính năng tích hợp cho GeoIP.

Ghi chú

GeoIP là một phương pháp xác định vị trí địa lý của thiết bị đầu cuôi máy vi tính, di động kết nối internet bằng cách xác định địa chỉ IP của thiết bị đầu cuối đó.

Trong topic này chúng ta cùng tìm hiểu cách ẩn một số khoá học dựa theo quốc gia của người truy cập trang web thông qua địa chỉ IP.

Để chuẩn bị cho các bước tiếp theo, chúng ta sẽ sủ dụng lại ví dụ từ topic trước về module education. Bạn sẽ cần phải tải về database của GeoIP, sau đó gán database đó vào trong command-line như bên dưới:

./odoo-bin -c config_file --geoip-db=location_of_geoip_DB

Mẹo

Một số database về GeoIP:

  • IPGeolocation.io (API & Database)

  • Abtract IP Geolocation API (API only)

  • Maxmind's GeoLite2 Database (API & Database)

  • IP2Location Lite

  • ip2c.org

  • GeoIP Nekudo

  • IP API.

Các bước thực hiện

Bước 1:

Thêm trường Many2many restrict_country_ids vào model course như bên dưới:

class Course(models.Model):
   _name = 'course'
   ...
   restrict_country_ids = fields.Many2many('res.country')
   ...
Bước 2:

Thêm trường restrict_country_ids vào form view của course như bên dưới:

<group string="Course detail">
...
   <field name="restrict_country_ids" widget="many2many_tags"/>
...
</group>
Bước 3:

Cập nhật phần code trong controller /course để hạn chế khoá học theo quốc gia như bên dưới:

@http.route('/course', type='http', auth="user", website=True)
def course(self):
   country_id = False
   country_code = request.session.geoip and request.session.geoip.get('country_code') or False
   if country_code:
      country_ids = request.env['res.country'].sudo().search([('code', '=', country_code)])
      if country_ids:
         country_id = country_ids[0].id
      domain = ['|', ('restrict_country_ids', '=', False),
                     ('restrict_country_ids', 'not in', [country_id])]
   return request.render(
      'education.course', {
      'courses': request.env['course'].search(domain),
   })

Cập nhật module education để áp dụng những thay đổi trên. Thêm quốc gia của bạn vào trường quốc gia hạn chế của khoá học và thử truy cập vào URL /course các khoá học để thấy kết quả. Những khoá học bị hạn chế sẽ không được hiển thị trên web.

Cảnh báo

Các bước thực hiện bên trên sẽ không thể hoạt động trên local server. Bởi vì với máy chủ local, bạn sẽ lấy được địa chỉ IP local, nhưng địa chỉ này không liên hệ đến quốc gia nào. Do đó cần phải thực hiện trên hosted server. Bạn cũng sẽ cần phải cấu hình NGINX để phù hợp với ví dụ trên.

Cơ chế hoạt động

Ở bước một và bước hai, chúng ta thêm vào model course và form view của model trường Many2many restrict_country_ids, để người dùng có thể chỉnh sửa quốc gia bị hạn chế của khoá học. Nếu GeoIP và NGINX được cấu hình đúng, Odoo sẽ thêm GeoIP vào request.session.geoip và bạn có thể lấy được mã quốc gia từ đó.

Ở bước ba, chúng ta fetched mã quốc gia từ GeoIP, theo sau là recordset của quốc gia dựa trên country_code. Sau khi lấy được thông tin quốc gia của người dùng, chúng ta sẽ lọc khoá học với domain là các quốc gia hạn chế.

Mẹo

Nếu như bạn không có một máy chủ thật và bạn muốn thử các bước trên, bạn có thể thêm mã quốc gia mặc định vào controller như sau:

country_code = request.session.geoip and request.session.geoip.get('country_code') or 'VN'

Theo dõi hoạt động tiếp thị

Return on Investment (ROI) làm một khái niệm quan trọng trong bất kỳ mô hình kinh doanh nào. Chỉ số ROI cho chúng ta biết về sự hiệu quả hay khả năng tạo ra lợi nhuận trong các khoản đầu tư. Nó được tính toán dựa trên số tiền đầu tư và lợi nhuận nhờ khoản đầu tư đó, được biểu thị dưới dạng phần trăm. Đặc biệt, việc đầu tư trong quảng cáo (advertise) có thể được theo dõi thông qua UTM trong Odoo. UTM (Urchin Tracking Module) là một chuỗi nhỏ được thêm vào URL của đối tượng mà bạn muốn theo dõi và nó có thể giúp bạn theo dõi campains, sourcesmedia của đối tượng trên. Trong topic này chúng ta sẽ thêm UTM cho các issue được tạo cho từng khoá học.

Các bước thực hiện

Để thực hiện ví dụ trong topic này, chúng ta tiếp tục sử lại module education.

Bước 1

Thêm module utm vào phần depends của file main.py:

'depends': ['base','website','utm'],
Bước 2

Kế thừa utm.mixin cho model course như phía dưới:

...
class CourseIssue(models.Model):
   _name = 'course.issue'
   _description = 'This model about issue of course'
   _inherit = ['utm.mixin']
...
Bước 3:

Thêm trường campaign_id vào tree view của course như phía dưới:

...
<group string="Course Issue">
  <field name="issue_ids" nolabel="1">
     <tree>
        <field name="create_date"/>
        <field name="submitted_by"/>
        <field name="issue_description"/>
        <field name="campaign_id"/>
     </tree>
  </field>
</group>
...

Cuối cùng, hãy update module education để áp dụng thay đổi.

Để test tính năng UTM, bạn cần làm theo các bước sau:

  • Trong Odoo, UTM sẽ được xử lí dựa trên cookies và một số trình duyệt không hỡ trợ cookies với localhost, do đó nếu bạn muốn test trên localhost, hãy truy cập instance với URL: http://127.0.0.1:8069.

Mặc định, UTM tracking sẽ chặn với những người bán hàng (salespeople). Do đó bạn hãy login với tư cách là một portal user.

  • Truy cập vào URL http://127.0.0.1:8069/course/submit_issues?utm_campaign=sale.

  • Gửi bản báo lỗi khoá học và kiểm tra bản ghi báo lỗi đó ở giao diện backend. Bạn sẽ thấy được kết quả như bên dưới.

Viindoo CMS

Ảnh 14.14 - Campaign được thêm vào bản ghi báo lỗi khoá học sau khi người dùng gửi báo lỗi.

Cơ chế hoạt động

UTM trong Odoo có ba trường kèm theo:

  • campaign_id: trường Many2one với model utm.campain, được sử dụng để theo dõi các chiến dịch khác nhau ví dụ như Summer hoặc Christmas.

  • source_id: trường Many2one với model utm.source, được sử dụng để theo dõi các nguồn truy cập vào trang web ví dụ qua các search engine như Google, Bing,... hoặc các tên miền khác.

  • medium_id: trường Many2one với model utm.medium, được sử dụng để theo dõi các đa phương tiện truyền thông khác nhau như: email, banner quảng cáo,...

Để có thể dùng UTM, bạn cần chia sẻ URL trên các phương tiện truyền thông có dạng như:

url?utm_campaign=campaign_name&utm_medium=medium_name&utm_source=source_name

Khi người dùng truy cập trang web từ bất kỳ phương tiện nào, các thông số của UTM sẽ được tự động ghi lại vào database.

Ghi chú

Bạn có thể tạo những chiến dịch, nguồn truy cập, phương tiện truyền thông mới bằng cách vào module Link Tracker -> UTMs trong chế độ developer

Quản lí nhiều trang website

Odoo hỗ trợ tích hợp cho nhiều trang website. Điều đó có nghĩa là cùng một phiên bản Odoo có thể chạy trên nhiều tên miền và sẽ hiển thị các bản ghi khác nhau. Trong topic này, chúng ta sẽ ẩn đi các khoá học dựa trên từng website.

Các bước thực hiện

Bước 1

Thêm website.multi.mixin vào phần kế thừa cho model course như bên dưới:

class Course(models.Model):
   _name = 'course'
   _inherit = ['website.seo.metadata', 'website.multi.mixin']
 ...
Bước 2

Thêm trường website_id vào form view của course như bên dưới:

...
<group string="Course detail">
   <field name="name"/>
   <field name="website_id"/>
   ...
</group>
...
Bước 3

Tuỳ chỉnh lại phần domain cho controller /course như bên dưới:

@http.route('/course', type='http', auth="user", website=True)
def course(self, **post):
   ...
   domain = request.website.website_domain()
   return request.render(
      'education.course', {
      'courses': request.env['course'].search(domain),
   })
Bước 4

Import thư viện werkzeug và tuỳ chỉnh controller course_detail như bên dưới:

import werkzeug
...
@http.route('/course/<model("course"):course>', type='http', auth="user", website=True,
            sitemap=sitemap_course) #adding sitemap
def course_detail(self, course, **post):
   # adding here
   if not course.can_access_from_current_website():
      raise werkzeug.exceptions.NotFound()
   return request.render( 'education.course_detail', {
                          'courses': course,
                          'main_object': course}) #ađding SEO

Cập nhật lại module education để áp dụng thay đổi. Để test chức năng này, hãy đặt các website khác nhau cho từng trang web. Hãy mở URL /course và kiểm tra danh sách khoá học. Để chuyển đổi giữa các trang web trong module Website, hãy dùng Website switcher để có thể thấy kết quả.

Viindoo CMS

Ảnh 14.15 - Giao diện backend của module Education khi thêm website cho từng khoá học.

Viindoo CMS

Ảnh 14.16 - Truy cập vào URL /course với website: My Website 2 để hiển thị danh sách các khoá học.

Cơ chế hoạt động

Ở các bước đầu, chúng ta thêm website.multi.mixin. Đây là class có chức năng xử lí nhiều trang web cho model. Đi theo class website.multi.mixin là trường website_id dùng để xác định bản ghi sẽ được gắn cho website nào.

Ở bước ba, chúng ta tuỳ chỉnh domain để tìm danh sách khoá học. request.website.website_domain() sẽ trả về domain đã được lọc các khoá học không gắn cùng trên một website.

Ghi chú

Chúng ta sẽ gặp trường hợp muốn bản ghi được hiển thị ở tất cả các tên miền. Chúng ta có thể xử lí bằng cách không đặt giá trị cho trường website_id trong backend.

Nếu như bạn để ý ở bước cuôi, chúng ta thêm **post vào controller. Mục đích là bởi vì nếu không có tham số này, **post/course/course//<model("course"):course> se không chấp nhận các tham số truy vấn. Khi chúng ta chuyển đổi giữa các website sẽ dẫn đến lỗi.

Điều hướng URL cũ

Khi bạn chuyển hệ thống hoặc website đã có đến Odoo website, bạn sẽ phải điều hướng những URL cũ tới URL mới. Với sự điều hướng thích hợp, toàn bộ SEO rankings của bạn sẽ được chuyển tới trang mới. Trong topic này, chúng ta sẽ tìm hiểu cách điều hướng URL cũ tới URL mới trong Odoo.

Các bước thực hiện

Chúng ta hãy giả định rằng bạn đang có một trang website và muốn chuyển website đó về Odoo. Ở website cũ, các khoá học được liệt kê tại URL /course-list. Bây giờ, chúng ta sẽ thêm quy tắc điều hướng trong Odoo để điều hướng URL /course-list tới URL /course mới.

Bước 1:

Bật chế độ người phát triển (developer mode).

localhost:8069/?debug=1
Bước 2:

Mở Website|Configuration|Redirects.

Bước 3:

Nhấn Create để thêm một quy tắc mới.

Bước 4:

Nhập giá trị vào form. Trong mục URL from, nhập /course-list và trong mục URL to, nhập /course.

Bước 5

Chọn Action với giá trị 301 Moved permanently

Bước 6:

Lưu lại bản ghi. Sau khi điền đầy đủ thông tin ở các bước, bạn sẽ thấy như hình dưới.

Viindoo CMS

Ảnh 14.17 - Tạo quy tắc điều hướng cho URL cũ.

Cơ chế hoạt động

Điều hướng trang trong Odoo được thực hiện một cách đơn giản như các bước trên. Có một số các tuỳ chọn điều hướng mà Odoo cung cấp:

  • 404 Not Found: Tuỳ chọn này được sử dụng nếu bạn muốn trả về thông báo 404 Not Found trên URL cũ.

  • 301 Moved permanently: Tuỳ chọn này sẽ điều hướng URLs cũ tới URLs mới vĩnh viễn. Với kiểu này, tất cả SEO rankings sẽ được chuyển về URLs mới.

  • 302 Moved temporarily: Tuỳ chọn này đièu hướng URLs cũ tới URLs mới tạm thời. Sử dụng tuỳ chọn này khi bạn muốn điều hướng tới URLs mới trong một khoảng thời gian nhất định và các SEO rankings sẽ không chuyển về URLs mới.

  • 308 Redirect/Rewrite: Với tuỳ chọn này bạn có thể thay đổi/viết lại URLs cũ thành URLs mới.

Bạn có thể bật/tắt quy tắc điều hướng với trường Active. Bạn cũng có thể sử dụng trường Website khi sử dụng tính nắng nhiều trang web.

Quản lí xuất bản cho các bản ghi liên quan tới website

Trong quá trình kinh doanh, có một số trường hợp bạn sẽ cần phải cho phép/thu hồi quyền truy cập trang của người dùng thông thường (public user). Ví dụ trong thương mại điện tử, bạn sẽ ra mắt sản phẩm/thu hồi sản phẩm đó dựa vào số lượng sản phẩm hiện có.

Trong topic này, chúng ta sẽ tìm cách giúp bạn xuất bản/không xuất bản(publish/unpublish) khoá học với người dùng thông thường.

Ghi chú

Trong các routes /course/course/<model("course"):course>, bạn sẽ thấy tham số auth = 'user'. Hãy thay đổi auth = 'public' để người dùng thông thường có thể truy cập vào các URLs trang web.

Các bước thực hiện

Chúng ta vẫn sẽ tiếp tục sử dụng ví dụ ở các topic trước với module education.

Bước 1:

Thêm website.published.mixin vào model course như bên dưới:

class Course(models.Model):
   _name = 'course'
   _inherit = ['website.seo.metadata', 'website.multi.mixin', 'website.published.mixin']
   ...
Bước 2:

Thêm một record rule vào file education/security/rules.xml như bên dưới. Sau đó hãy thêm file này vào manifest:

<odoo noupdate="1">
   <record id="books_rule_portal_public" model="ir.rule">
      <field name="name">Portal/Public user: read published course</field>
      <field name="model_id" ref="education.model_course"/>
      <field name="groups" eval="[(4, ref('base.group_portal')),(4, ref('base.group_public'))]"/>
      <field name="domain_force">[('website_published','=', True)]</field>
      <field name="perm_read" eval="True"/>
   </record>
</odoo>

Cập nhật module education để áp dụng các thay đổi trên. Bây giờ, bạn có thể xuất bản/không xuất bản các khoá học trên trang web bằng cách tắt/bật ngay trên thanh công cụ website.

Viindoo CMS

Ảnh 14.18 - Chuyển đổi giữa publish/unpublish trong website.

Cơ chế hoạt động

Khi thêm website.published.mixin vào phần kế thừa của model course, ở bước này sẽ thêm các trường và phương thức cần thiết để publishunpublish khoá học. Khi đó, bạn có thể thấy một nút chuyển đổi trạng thái ở trang chi tiết khoá học.

Ghi chú

Chúng ta gửi bản ghi của khoá học như một main_object từ route chi tiết khoá học. Không có điều này, bạn sẽ không thể thấy được nút publish/unpublish trên trang chi tiết khoá học.

Bước tiếp theo, chúng ta thêm một rule để có thể cho phép người dùng thông thường truy cập được vào các URLs khoá học. Bạn có thể học thêm về record rule trong Security Access.

Như vậy chúng ta học được cách làm chủ các tính năng nổi bật trong Odoo CMS. Odoo cung cấp cho người dùng và lập trình viên một trải nghiệm thân thiện trong môi trường phát triển Website.

Ở phần tiếp theo, chúng ta sẽ tiếp tục tìm hiểu về frontend của Odoo với nội dung chính là Framework GUI của Odoo.