liferay development

78
Liferay Development Out line I: Khái quát về liferay portal: Phần này chúng ta sẽ tìm hiểu về một số vấn đề cơ bản của liferay portal: Portal là gì, các khái niệm về portal? Liferay portal là gi? - Liferay’s Users - Các roles - Communities - Organizations Sử dụng liferay để xây dựng portal như thế nào? Chi tiết: 1. Portal? PORTAL: một cổng thông tin dựa trên nền Web cho phép người dùng có thể tìm, và xây dựng nội dung thông tin cũng như sử dụng các ứng dụng thông thường trên portal web đó với hiệu suất cao nhất, nói nôm na là tất cả mọi thứ sẽ có trên portal, thông tin tập trung tại portal, bạn chẳng cần đi nơi khác tìm => hiệu suất tìm và sử dụng của bạn sẽ đạt mức cao nhất. Khái niệm trên phần nào cũng khái quát nên thể nào là một portal về cách hiểu chung chung, nhưng đối với những người lập trình thì bạn cần hiểu rõ ràng hơn: Ở đây, PORTAL chính là 1 môi trường Web độc lập, bạn chạy được các ứng dụng mà mình viết ra trên đó, và những ứng dụng này được tích hợp với nhau theo cách của Portal và cách của người lập trình một cách có hệ thống và đồng bộ. Lịch sử về Java Portal:(optional) Khi mà Java Portal lần đầu tiên được công bố nó hứa hẹn sẽ là một giải pháp cho rất nhiều vẫn đề mà các công ty cũng như các kỹ sư trưởng dự án đang đối mặt. Lúc đó Web cũng đang phát triển, các giao thức độc quyền trước đó, cuối cùng cùng được chuẩn hóa vá sử dụng rộng rãi như là TCP/IP, HTTP, SOAP, IMAP, SMTP,.. Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Upload: van-thuan-ngo

Post on 01-Jan-2016

947 views

Category:

Documents


2 download

TRANSCRIPT

Liferay Development

Out line

I: Khái quát về liferay portal:

Phần này chúng ta sẽ tìm hiểu về một số vấn đề cơ bản của liferay portal:

Portal là gì, các khái niệm về portal? Liferay portal là gi?

- Liferay’s Users- Các roles- Communities- Organizations

Sử dụng liferay để xây dựng portal như thế nào?

Chi tiết:1. Portal?

PORTAL: một cổng thông tin dựa trên nền Web cho phép người dùng có thể tìm, và xây dựng nội dung thông tin cũng như sử dụng các ứng dụng thông thường trên portal web đó với hiệu suất cao nhất, nói nôm na là tất cả mọi thứ sẽ có trên portal, thông tin tập trung tại portal, bạn chẳng cần đi nơi khác tìm => hiệu suất tìm và sử dụng của bạn sẽ đạt mức cao nhất.

Khái niệm trên phần nào cũng khái quát nên thể nào là một portal về cách hiểu chung chung, nhưng đối với những người lập trình thì bạn cần hiểu rõ ràng hơn:Ở đây, PORTAL chính là 1 môi trường Web độc lập, bạn chạy được các ứng dụng mà mình viếtra trên đó, và những ứng dụng này được tích hợp với nhau theo cách của Portal và cách của người lập trình một cách có hệ thống và đồng bộ.Lịch sử về Java Portal:(optional)Khi mà Java Portal lần đầu tiên được công bố nó hứa hẹn sẽ là một giải pháp cho rất nhiều vẫn đề mà các công ty cũng như các kỹ sư trưởng dự án đang đối mặt. Lúc đó Web cũng đang phát triển, các giao thức độc quyền trước đó, cuối cùng cùng được chuẩn hóa vá sử dụng rộng rãi như là TCP/IP, HTTP, SOAP, IMAP, SMTP,..Các dịch vụ, ứng dụng, email vận hành trên các giao thức mở đó, các sản phẩn đáng tin cậy cũng được sử dụng cho Web rộng rãi. Và khi tất cả đã có chung tiêng nói, chúng ta cần một cách để mang tất cả mọi thứ đó cùng nhau tới người dùng. Java Portal muốn đồng bộ mọi thứ, nó không chỉ là những ứng dụng nội bộ của một tổ chức, nó còn như một nơi để các giao tiếp giữa doanh nghiệp với doanh nghiệp (B2B), doanh nghiệp với người tiêu dùng (B2C), doanh ngiệp với công nhân (B2E), và thậm chí giữa chính phủ và cộng đồng (G2P) hội tụ. Những ứng dụng, trang tĩnh hay động trên các server riêng rẽ cuối cùng có thể được tích hợp cùng nhau trên Portal.

Nghe thì có vẻ rất nhiều hứa hẹn mà công nghệ mới này mang lạị. Nhưng thực sự dời không đẹp như mơ, đã có 3 vấn đề lớn nảy sinh với java portal:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Thứ 1:Phát triển ứng dụng sử dụng portal là khá khó. Thời kì đầu API của Portal rất là hạn chế. Thay vì cung cấp cho developers tất cả các công cu phát triển các chức năng 1 cách xuyên xuốt, nó lại chỉ định nghĩa những tối thiểu tất yếu để các hãng khác có thể chuẩn hóa theo nó,và bỏ lại những phần việc còn lại cho các hãng riêng lẻ đó. Ví dụ: 1 Portal sơ khai còn không hề đề cập tới việc làm sao để các portlets có thể giao tiếp với nhau.. Thứ 2, một server portal thường lớn và khá phức tạp, thường mất cả ngày để cài đặt.Thường lập trình viên khi thử làm việc với portal có khá nhiểu vấn đề này sinh, nó không như thông thường. Thứ 3, Web 2.0 thời điểm này đang dần trở nên phổ biến, portlet cũng chiếm hết vị thế trong môi trường mà tạo ra nhiều trải nghiệm thông tin phong phú cho người dùng. Để cạnh tranh được, các hãng portal bắt đầu mở rộng những ứng dụng độc quyền trên nền portal của mình, Điều này tạo ra các vendor lock-in (khách hang đang sử dụng các sản phẩn từ portal của họ sẽ khó chuyển sang portal khác nếu không chịu chi hầu bao cho sư thay đổi), => như là những gì để xác định các tiêu chuẩn , hãy tránh xa chúng nếu bạn không muốn gặp rắc rối với họ, rào cản đã tạo ra với java portal.Đồng thời, java portal cũng đối mặt với những điều không tốt lành gì. Myspace, Facebook ra đời và “đẹp” hơn rất nhiều so với java portal, và những trang này ngày càng phổ biến, bất ngờ Những trang khác tiêu biểu có Amazon cùng phần mềm JIRA , họ đã liên kết mình được với các trang xã hội, thực thi được các chức năng của chúng trên chính trang của mình với giao diện bóng bảy mượt mà chưa hề có từ trước đó, thậm chí còn hỗ trợ cả ajax. Điều gì tạo ra sự mới mẻ và nhiều cải tiến đột phá tới vậy? Chúng ta có thể đoán ra, đó chính là nhờ mã nguồn mở, trong khi java portal ngày càng đóng kín.Mã nguồn mở mang đến những điều tuyệt vời nhất:

Giải quyết các tiềm tàng mà java portal gặp phải, các dự dán mã nguồn mã chẳng phải đợi sự thông qua của bất kỳ ủy ban nào, cứ có vẫn để là giải quyết, đáp ứng nhu cầu người dung nhanh nhất có thế. Không hề có rào cản nào với mã nguồn mở, mọi thứ đều Free. Hơn nữa các sản phẩm mã nguồn mở tạo ra thường có xu hượng nhẹ hơn so với java portal và chẳng mấy tốn kém tài nguyên. Phát triển cũng nhanh hơn, bạn không cần phải học hành tỷ mỷ để hiểu về toàn bộ hệ thống. Bạn không cần tốn 1 khoản đầu tư lớn ban đầu. Facebook là 1 ví dụ hoàn hảo, phát triển nhiều trên PHP( 1 nền tảng web mã nguồn mở), lấy nhu cầu người dung làm tiêu chí để nâng cấp hệ thống. Đây mới thực sự là những gì chúng ta mong muốn. Liferay portal cũng tương tự như vậy, liferay portal cho phép bạn làm được điều đó.

Liferay Portal:Liferay Portal để cài đặt được thì cực kỳ đơn giản, nếu dung bản .war kích thước chỉ tử 125MB, bạn có thể dung nó deploy vào bất kỳ server nào mà ít phải dung đển các bước cấu hình quá phức tạp cho server đó, đương nhiên nó cũng ảnh hưởng đến server, rõ rệt nhất, server của bạn sẽ là 1 server portal, điểu này nhiều môi trường cộng tác không mong muốn.Nếu bạn sử dụng bản zip, mọi chuyện đơn giản hơn, unzip, thêm thư viện và chỏ đến cơ sỏ dữ liệu bạn muốn liên kết là xong.

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Liferay không phải là 1 portal rỗng, nó có khoảng 60 portlet được xây dựng sẵn, bạn có thể dung nó thoải mái. Những portlet này sẽ cung cấp cho bạn các chức năng tối thiểu cho viêc quản trị nội dung, wiki, forum…Liferay là portal đầu tiên thực thi web 2.0 (sự mới mẻ, bóng bẩy). Liferay ra đời là để canh tranh với portal bản quyền, Liferay vs oracle portal.Bạn còn có những trải nghiệm thú vị với việc kéo và thả portlet vào trang tùy vị trí mong muốn,Liferay dưa trên tiêu chuẩn JSR-286 portlet, thêm nữa Liferay có tiện ích ServiceBuilder, nó sẽ tự tạo ra giao thức với hệ quản trị cơ sở dữ liệu ( thực ra Service builder dựa trên Hidernate và Spring thông thường)

Để hiểu rõ hơn về Liferay chúng ta sẽ tiếp cận tới các chi tiết tỷ mỉ hơn:

Giao diện cơ bản lúc bắt đầu liferay

1. Liferay là một tập hợp các ứng dụng:Sau khi login Dockbar sẽ xuất hiện bạn có thể thêm vào trang các ứng dụng mong muốn bằng cách chọn Add More …

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

2. Liferay là 1 CMS hoàn chỉnh:Chọn Web Content Display từ AddMore Content Management

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Web content display( góc trên, phải) được thêm vào nhưng chưa có nội dungSauk hi chon Add web content

Về cơ bản nếu không cần xay dựng nội dung mang tính nghiệp vụ thì bạn hoàn toàn có thể tao nên 1 page cho riêng mình bằng CMS của liferay.

3. Liferay chứa đựng nhiều công cụ tích hợp

Bạn có thể truy cập vào Document library

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Tất cả các chức năng được xây dựng bởi liferay, bạn không những sử dụng được mà còn phát triển mở rộng được chúng.

4. Bạn có thể thay đổi look and feel trong liferay

Phần việc này liên quan đến tạo theme và layout trong liferay

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Chúng ta đã có cái nhìn sơ bộ về liferay, bây giờ sẽ tìm hiểu về cấu trúc portal của nó:

1 liferay server có thể bao gôm 1 hay nhiều liferay portal instance

1 portal instance có các users(người dùng) các users sẽ được phân loại vào các collections(tập hợp),

Có nhiều cách để phân loại những users này: như phân loại theo role(vai trò), organizations(tổ chức), community(cộng đồng), groups(nhóm) hay thậm chí là tập hợp của các cách phân loại sẵn có đó.

Loại phân loại Mô tảRoles Tập hợp các users cùng sở hữu chức năng

nhất định, roles sẽ thể hiện qua : sự cho phép user để sử dụng những chức năng đó

organizations Tập hợp các users theo vị trí của họ trong cấu trúc phân cấp(hierarchy), một tổ chức sẽ được cấu trúc theo hình cây, user chính là các node trên cây đó, chịu sự áp đặt về quyền lợi theo level

community Tập hợp các users có cùng mối quan tâm, user có thể tham gia hay ra khỏi cộng đồng bất cứ khi nao ngay cả user đó được gán những quyền lợi nhất định

User group Tập hợp các user trên phạm vi toàn portal, cái này Admin sẽ phân chia theo mục đích riêng

Thực chất, các danh sách tập hợp trên là chưa hề đủ trên 1 portal, liferay còn phân loại theo pages(trang). Các trang này hoàn toán có thê browse tới được

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Communities và organizations sẽ có tập hợp các page riêng được định nghĩa cho chúng. Trang được chia ra public page và private page.

Các public page của Organizations và Communities sẽ cho phép member và non-member truy cập, còn private page đương nhiên là chỉ dành cho member.

User group có thể không có tập hợp page được định nghĩa cho họ, nhưng họ có page template.

Mặc định thì mỗi user sẽ có 1 cộng đồng của cá nhân họ (personal community), sẽ có 1 trang public và 1 trang private, về cơ bản user sẽ được phép thêm các portlet nhất định vào trang của mình cả public và private, nhưng admin lai có 1 quyền khác đối với các page template này, admin có thể quyết định những portlets sẽ có sẵn từ ban đầu và khi admin thay đổi gì đó trên page template, toàn bộ user group dung page template đó sẽ thay đổi theo sự điều chỉnh của admin.

Đến bây giờ thì hình như Roles còn chưa có sự liên quan ở đây, bản thân role chẳng có page nào nhưng role chính là sự cho phép, nếu bạn gán sự cho phép để xem 1 trang web nhất định tức là đã định nghĩa 1 role. Như vậy, chúng ta có thể hiểu cách mà roles có thể tương tác với organizations, community, user group trong hệ sinh thái portal của chúng ta như thế nào , chính là nhờ page.

Ngoài ra 1 điều nữa chúng ta cần quan tâm , Portlet’ scope: phạm vi của portlet:

Roles là loại tập hợp được chia làm 2 phạm vi:

- Phạm vi portal- Phạm vi organizations /community

Liferay portlet có 2 loại : instanceable portlet and non-instanceable portlet

Non-instanceable portlet: portlet không trạng thái

- Dữ liệu của portlet được lưu trữ trong kho dữ liệu của riêng nó thuộc scope mà nó được bao chứa

- Chỉ thêm được 1 portlet trên 1 page- Nếu config scope default là organization/community thì với các pages thuộc

cùng scope này nếu cùng thêm non-instanceable portlet đó, tất cả portlet sẽ hiển thị nội dung giống nhau, nếu muốn sự khác biệt trên mỗi trang cần config scope là Page Scope, hoặc vượt ra khỏi scope của nó. Có được điều này là vì trong cùng scope các non-instanceable portlet cùng sử dụng chung 1 dữ liệu

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Ví dụ về wiki: 1 non-instanceable portlet

- Công dụng: nói đến đây chúng ta đã hình dung được công dụng của nó:+ chỉ cần thêm thông tin cho 1 portlet, tất cả các portlet trong scope sẽ nhận được sự thay đổi

Instanceable portlet: portlet có trạng thái

- Dữ liệu của portlet được lưu trên đĩa, người dùng có thể tiếp cận được tùy dữ liệu khác nhau dựa vào mỗi quan hệ tập hợp của họ trong portal.

- Có thể thêm 1 hay nhiều portlet trên cùng 1 trang/page, mỗi portlet có thể được config để có được 1 instance của riêng nó, từ đó thông tin và mục đích có thể khác nhau giữa các portlet cùng tên đó

Cách thay đổi scope cho 1 portlet: lấy wiki portlet là 1 ví dụClick vào biểu tượng giống thanh c-lê ở góc phải trên của 1 portlet chọn cấu hình/configuration sau đó tap đến scope/phạm vi tab->

Như vậy đến thời điểm này phần nào bạn cũng hiểu về cấu trúc của liferay portal có gì khó hiểu xin liên hệ và chờ giải đáp tại [email protected]: mọi đóng góp của các bạn là rất quý giá đối với chúng tôi

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Chúng ta cùng nhau nhìn lại liferay portal:

Đây là Dockbar1. Pin Icon:

Phía bên trái ngoài cùng là pin icon, nó có tác dụng trng trường hợp page của bạn quá dài, nêu bạn pin dockbar cho dù bạn có kéo xuống đi nữa dockbả vẫn di chuyển theo, giống như phím back-to-top của nhiều trang web

2. –Add/Thêm:

Bạn có thể chọn Add page để tạo 1 page mới cho liferay

3. Quản lý / Manage:Bạn có thể quản lý các trang cùng các đặc điểm của trang trong mục này 1 số chúng như: Giao diện/ look and feel, page/trang,..

4. Toggle edit controls/hiển thị cấu hình ứng dụngdùng để ẩn hiên thanh sửa đổi title của mỗi portlet

5. Goto/Chuyển tớiBạn có nhiều lựa chọn thay đổi cho portal và tài khoản trên portal của mình thông qua Control panel ,Trong control panel có các phần như là:User, Content, portal, server

Chắc hẳn bạn đã hiểu khá nhiều về liferay dưới góc nhìn của người dung cuối, tiếp theo chúng ta cùng khám phá nó dưới góc độ của người lập trình

Chương 2:Tiếp cận nền tảng Liferay

Trong chương này chúng ta sẽ tìm hiểu về:

- Cài đặt liferay server và setup database- Cài đặt liferay SDK plugins- Cách tạo 1 project dựa vào plugins- Viết portlet đầu tiên

2.1 Set up Liferay server

Step 1/ cài đặt java SDK , cặt đặt biến JAVA_HOME, các bạn đã quá quen thuộc với việc này, nêu chưa biết thì có thể google search

Step2/ Cài đặt liferay bunbles:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Các bạn download bundle từ trang chủ của liferay, http://www.liferay.com/downloads/liferay-portal/available-releases

Các bạn nên chọn server là tomcat vì các ví dụ trong cuốn sách này chủ yếu hướng dẫn trên tomcat, khi download các bạn cũng nên chú ý tới version, nó sẽ có ảnh hưởng tới công việc dev của bạn

Unzip vào thư mục mong muốn.

Các bạn browse tới thư mục bin của tomcat chon startup.bat(đối với windows)/startup.sh (với họ linux) để start server

Thế là đã có 1 server chạy được, nhưng chỉ là server kết nối với database mặc định HSQL của liferay server , muốn có được kết nối với database riêng của mình các bạn thì công việc tiếp theo:

2.1.1 Config database:

Tôi sẽ hướng dẫn các bạn cách kết nối với oracle database: Công việc cần là: cách thêm file JDBC và config sao cho liferay nhận được database:

Step 1. Thêm JDBC:

Các bạn có thể thấy khi browse vào folder của server liferay:

[home-server]tomcat[-version]libsext, đây là thư mục chứa thư viện sử dụng được cho toàn portal, bạn có thể add bất cứ thư viện nào dung được vào đó, nhưng

Liferay hỗ trợ sẵn các JDBC cho Mysql, postgresql nhưng chưa có JDBC cho oracle,

Dựa theo version oracle của mình các bạn tải file OJDBC tương ứng, copy vào thư múc ext trên

Step2. Config file:

Browse tới thư mục classes trong tomcat:

…tomcat[-version]webappsrootweb-inf classes

Nếu đã có file portal-ext.properties thì các bạn thay đổi thông tin trong đó thành, còn nếu chưa có thì tạo mới file và thêm thông tin, như sau

File portal-ext.properties

# # Oracle # jdbc.default.driverClassName=oracle.jdbc.driver.OracleDriver jdbc.default.url=jdbc:oracle:thin:@[host]:[port]:[database_name] jdbc.default.username=[user_name] jdbc.default.password=[password]

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Công việc khá đơn giản, đẻ hoàn tất thủ tục đương nhiên các bạn cần 1 valid user_name, password, database. Đối với những ai mới bắt đầu làm việc với liferay, tôi khuyến khích là tạo mới 1 user , và bắt tay vào công việc từ mức căn bản nhất, vì liferay không như server tomat thường, bản thân nó đã có các service,table dùng riêng sẽ phân bố vào user/chema của bạn khi start up lần đâu, nếu có bất cứ sự không tương thích giữa các thông số với nhau, server sẽ startup thất bại

Để có thể kết nối với các DBMS khác các bạn lặp lại bước 1, đến bước 2 có thể tham khảo ở đây:

http://www.liferay.com/community/wiki/-/wiki/Main/Database+Portal+Properties

Còn 1 cách khác là cầu hình trong root.xml, nhưng cách trên đơn giản hơn và được khuyến khích.

2.2 Cài đặt plugins SDK

Để phát triển các portlet, theme, layout chạy được trên liferay server, liferay cung cấp bộ SDK cho mục đích này:

Các bạn có thể download được SDK theo đúng phiên bản server liferay của mình theo đường link này http://sourceforge.net/projects/lportal/files/Liferay%20Plugins/

Download về các bạn giải ra cùng thư mục với liferay server cho dễ quản lý

SDK có thể deploy trên Ant hay Maven, ở đây tôi giới thiệu ant:

Các bước cài đặt:

Step 1:

Cài đặt Ant(nếu máy bạn chưa có)

Ant bạn có thể download tai http://ant.apache.org tìm file apache-ant-[version]-src.zip

Sau đó cũng để dễ quản lý các ban giải nén vào thư mục chứa liferay server

Tiếp theo là cài đặt biến ANT_HOME việc cài đặt sẽ giống như JAVA_HOME

Step 2:

Confithuwbuild path cho liferay SDK của bạn:

Browse tới thư mục của SDK các bạn sẽ thấy file build.properties; các thông tin trong đó sẽ là deploy dir, server dir,…

Bạn hoàn toàn có thể setup link tới liferay server của bạn bằng cách thay đổi các thông tin tương ứng ngay trên file này, nhưng chúng ta không làm thế:

Tạo 1 file build.[-tên_máy-].properties

Điền các thông số như sau:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

app.server.portal.dir = [-liferay-server-home-]\\tomcat-[-version-]\\webapps\\ROOTapp.server.lib.global.dir = [-liferay-server-home-]\\tomcat-7.0.27\\lib\\extapp.server.deploy.dir = [-liferay-server-home-]\\tomcat-[-version-]\\webappsapp.server.type = tomcatapp.server.dir = [-liferay-server-home-]\\tomcat-[-version-]

Ví dụ: nếu server liferay của bạn: tại thư mục E: =>

app.server.portal.dir = E:\\liferay-portal-6.1.1-ce-ga2\\tomcat-7.0.27\\webapps\\ROOTapp.server.lib.global.dir = E:\\liferay-portal-6.1.1-ce-ga2\\tomcat-7.0.27\\lib\\extapp.server.deploy.dir = E:\\liferay-portal-6.1.1-ce-ga2\\tomcat-7.0.27\\webappsapp.server.type = tomcatapp.server.dir = E:\\liferay-portal-6.1.1-ce-ga2\\tomcat-7.0.27

Bây giờ chúng ta đã có thể bắt đầu lập trình với SDK:

Step1: Khởi tạo:

Chọn vào 1 thư mục trong SDK bạn muốn phat triển ví dụ thư mục portet,

Run in command line câu lệnh

Create.bat [-portlet-name-] “[-ten-hien-thi-]”

Hoặc create.sh [-portlet-name-] “[-ten-hien-thi-]”

Ví dụ, tạo portelt tên là my-portlet tên hiển thị là: “My First Portlet”

Chúng ta viết câu lệnh create.bat my-porlet “My First Porttlet”;

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Trong folder portlet lúc này sẽ tạo ra thư mục my-portlet-portlet: liferay sẽ gán thêm vào tên portlet từ “-portlet” và folder project sẽ là [-tên-portlet-]-portlet, điều này cũng xảy ra tương tự khi tao theme hay layouttpl

Về cấu trúc gồm:

-docroot

-Web-INF

-build.xml

Để deploy portlet bạn

Run lệnh sau:

ant deploy

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Sau đó file .war sẽ được tao ra tại thư mục dist trong SDK, nếu deploy không tự động , bạn copy vao thư mục deploy của liferay server, => auto deploy bắt đầu

Vì bạn không thay đổi gì bên trong my-portlet portlet này sẽ có trong AddMoresample

/ThêmThêm Mẫu

My First Portlet

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Bây giờ bạn có thể thêm “My first portlet” vào tùy chỗ bạn thích trên trang Web, nhưng portlet lúc này chưa có gì,

Trong các thư mục của My First Portlet

Docroot sẽ là phần quan trọng nhất:

Docroot: thư mục gốc của portlet

Web-INF/src: thư mục chứa mã nguồn của portlet

Trong web-inf ta xúng thấy có các file config như:

-liferay-display.xml: file này chứa cấu hình để cho portlet hiển thị tại danh mục xác định trong hộp thoại AddMore/ThêmThêm ví dụ:

<display>

<category name="category.sample">

<portlet id="my-portlet" />

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

</category>

</display>

Nếu để mặc đinh category.sample , thì portlet sẽ hiển thị trong AddMoreSample

Nếu thay category.sample bằng 1 cái [tên-danh-mục] nào đó và redeploy portlet thì portlet sẽ thuộc AddMore[tên-danh-mục]

Hiển nhiên là đặt được nhiều portlet trong cùng 1 danh mục, chúng ta sẽ nói đến sau,

Trong phần này, bạn để ý có <portlet id="my-portlet" /> , id chính là tên của portlet được định nghĩa ở file config: portlet.xml

File nay sẽ định nghĩa các thông tin cấu hình của portlet như

<portlet-name>my-portlet</portlet-name>

<display-name>My First Portlet</display-name>

<portlet-lass>com.liferay.util.bridges.mvc.MVCPortlet</portlet-class>

<init-param>

<name>view-template</name>

<value>/view.jsp</value>

</init-param>

<expiration-cache>0</expiration-cache>

<supports>

<mime-type>text/html</mime-type>

</supports>

<portlet-info>

<title>My First Portlet</title>

<short-title>My First Portlet</short-title>

<keywords>My First Portlet</keywords>

</portlet-info>

<security-role-ref>

<role-name>administrator</role-name>

</security-role-ref>

<security-role-ref>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<role-name>guest</role-name>

</security-role-ref>

<security-role-ref>

<role-name>power-user</role-name>

</security-role-ref>

<security-role-ref>

<role-name>user</role-name>

</security-role-ref>

Các định nghĩa thêm cho portlet sẽ được bổ sung trong file config:

Liferay-portlet.xml: như về css, javascript, instance portlet,…

Và liferay-plugin-package.properties: file config này dung để định nghĩa các file .jar mà cần cho portlet ví dụ các bạn có thể đinh nghĩa thêm thư viên của JSTL như sau:

Thêm đoạn sau vào cuối file:

portal-dependency-jars=\ jstl-api.jar,\ jstl-impl.jar,\ struts.jarportal-dependency-tlds=c.tld

Các file .jar của JSTL sẽ được copy tới WEB-INF/lib

Định nghĩ như vậy sẽ giúp liferay nhận biết và không include những files .jar đã có sẵn trong thư viện Sping, Strut, Hibernate, bằng không bạn hoàn toàn có thể copy thư viện của JSTL vào thẳng Web-INF/lib

Chú ý*: Các tag trong file config phải được viết theo đúng thứ tự thì mới compile được, tạm thời chỉ giới thiệu sơ qua về các file trên, chúng ta sẽ tìm hiểu kỹ hơn ngay khi viết portlet

Build.xml : chứa các script của Ant để biên dịch và deploy portlet

Portlet sẽ có it nhất 3 phần:- Phần 1: java source code : WEB-INF/src- Phần 2: các file config: WEB-INF/portlet.xml,liferay-portlet.xml,liferay-

display.xml, liferay-plugin-package.properties- Phần 3: các trang client: .jsp, .css, .js, ….

Mở rộng “My First Portlet”:

Chúng ta sẽ sử dụng 3 API chính để mở rộng My First Portlet:

+ Portlet Mode ((View, Edit, and Help))

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

+ Portlet preference

+ Portlet action

Trong portlet.xml có dòng:

<portlet-class>com.liferay.util.bridges.mvc.MVCPortlet</portlet-class>

Định nghĩa file java sẽ thực thi các chức năng trong portlet , hoàn toàn giống như 1 file control

com.liferay.util.bridges.mvc.MVCPortlet là mặc định , ta sẽ định nghĩa class riêng extends MVCPortlet hay GenericPortlet để thực thi chức năng cho “My First Portlet”

Tạo file .java trong WEB-INF/src như sau vn.neo.training.portlet.MyPortlet ( nghĩa là tạo thư mục vn neo training portlet MyPortlet.java)

Sau đó sử đổi

<portlet-class> vn.neo.training.portlet.MyPortlet </portlet-class>

Định nghĩa portlet Mode cho “My First Portlet”:

Bạn cần khai báo portlet sẽ hỗ trợ 2 modes là view và edit, không thì chỉ co view là mặc định

Thay đổi tag supports:

<supports>

<mime-type>text/html</mime-type>

<portlet-mode>view</portlet-mode>

<portlet-mode>edit</portlet-mode>

</supports>

Mỗi mode tức là sẽ có 1 file jsp trực tiếp để hiện thì thông tin: ngay dưới đinh nghĩa <portlet-class> tiến hành sửa đổi và thêm <init-param> như sau

<init-param>

<name>view-jsp</name>

<value>/view.jsp</value>

</init-param>

<init-param>

<name>edit-jsp</name>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<value>/edit.jsp</value>

</init-param>

Mọi việc với portlet.xml đã hoàn tất, tiếp theo là implement MyPortlet.java như sau

package vn.neo.trainning.portlet;

import java.io.IOException;

import javax.portlet.ActionRequest;

import javax.portlet.ActionResponse;

import javax.portlet.GenericPortlet;

import javax.portlet.PortletException;

import javax.portlet.PortletMode;

import javax.portlet.PortletPreferences;

import javax.portlet.PortletRequestDispatcher;

import javax.portlet.PortletURL;

import javax.portlet.RenderRequest;

import javax.portlet.RenderResponse;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

/**

* The My First Portlet, a simple example demonstrating

* Portlet Modes, Portlet Actions, and Portlet Preferences.

*

* @author vanthuan

*

*/

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

public class MyPortlet extends GenericPortlet {

public void init()

throws PortletException {

editJSP = getInitParameter("edit-jsp");

viewJSP = getInitParameter("view-jsp");

}

public void doEdit(

RenderRequest renderRequest, RenderResponse renderResponse)

throws IOException, PortletException {

renderResponse.setContentType("text/html");

PortletURL addNameURL = renderResponse.createActionURL();

addNameURL.setParameter("addName", "addName");

renderRequest.setAttribute("addNameURL", addNameURL.toString());

include(editJSP, renderRequest, renderResponse);

}

public void doView(

RenderRequest renderRequest, RenderResponse renderResponse)

throws IOException, PortletException {

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

PortletPreferences prefs = renderRequest.getPreferences();

String username = (String) prefs.getValue("name", "no");

if (username.equalsIgnoreCase("no")) {

username = "";

}

renderRequest.setAttribute("userName", username);

include(viewJSP, renderRequest, renderResponse);

}

public void processAction(

ActionRequest actionRequest, ActionResponse actionResponse)

throws IOException, PortletException {

String addName = actionRequest.getParameter("addName");

if (addName != null) {

PortletPreferences prefs = actionRequest.getPreferences();

prefs.setValue("name", actionRequest.getParameter("username"));

prefs.store();

actionResponse.setPortletMode(PortletMode.VIEW);

}

}

protected void include(

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

String path, RenderRequest renderRequest,

RenderResponse renderResponse)

throws IOException, PortletException {

PortletRequestDispatcher portletRequestDispatcher =

getPortletContext().getRequestDispatcher(path);

if (portletRequestDispatcher == null) {

_log.error(path + " is not a valid include");

}

else {

portletRequestDispatcher.include(

renderRequest, renderResponse);

}

}

protected String editJSP;

protected String viewJSP;

private static Log _log = LogFactory.getLog(MyPortlet.class);

}

Trong MyPortlet ta thấy có các method như init(), đoEit(), doView()

init() phương thức này khá giống init() của servlet có thể có portletConfig giống như servletConfig, sẽ gọi các init-param ngay trước khi portlet làm các công việc tiếp theo

Sau init() portlet sẽ hỏi đển doView() thực thi View Mode

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Bạn thấy 2 tham sổ của RenderRequest và RenderResponse, đây là request và response của portlet cũng khá giống như HttpServletRequest và HttpServletResponse về chức năng

Vì chúng ta extends GenericPortlet nên việc render/chuyển trang sẽ không tự động được nên chúng ta phải dispatch bằng cách dung PortletRequestDispatcher, cái này cũng khá giống RequestDispatcher của servlet, Chỉ có 1 điều mới ở đây là javax.portlet.PortletPreferences, thực ra portletPreference dung để lưu các thông tin của portlet, các thông tin sẽ nguyên vẹn cho đến khi portlet còn tồn tại trên page.

Tiếp theo là doEdit() thực thi Edit Mode

public void doEdit(

RenderRequest renderRequest, RenderResponse renderResponse)

throws IOException, PortletException {

renderResponse.setContentType("text/html");

PortletURL addNameURL = renderResponse.createActionURL();

addName.setParameter("addName", "addName");

renderRequest.setAttribute("addNameURL", addNameURL.toString());

include(editJSP, renderRequest, renderResponse);

}

contentType text/html thường là mặc định, đôi khi không set cũng không sao

trong edit mode, chúng ta tạo ra 1 portlet action là addName chuyển đến edit.jsp

cả quá trình sẽ là init() doView() doEdit()

ngoài ra còn 1 method là processAction() tham số truyền vào là của ActionRequest, ActionResponse, dung để catch addName action

Tiếp theo là tạo 2 file client trong docroot view.jsp và edit.jsp

Như sau:

View.jsp

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>

<jsp:useBean id="userName" class="java.lang.String" scope="request" />

<portlet:defineObjects />

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<p>This is the My First portlet.</p>

<p>Hello <%= userName %>!</p>

Ta thấy

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>

Dùng để gọi các định nghĩa custom tag <portlet: lên trang .jsp

<portlet:defineObjects />

Sẽ định nghĩa các biến dung cho jsp page như là: renderRequest/request/actionRequest, renderResponse/response/actionResponse, portletSession, portletPreferences, portletName, config,…

Có nhiều sự tương đồng với servlet-jsp thông thường

File edit.jsp:

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>

<jsp:useBean class="java.lang.String" id="addNameURL" scope="request" />

<portlet:defineObjects />

<form

id="<portlet:namespace />form"

action="<%= addNameURL %>"

method="post">

<table>

<tr>

<td>Name:</td>

<td><input type="text" name="username"></td>

</tr>

</table>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<input type="submit" id="nameButton" title="Add Name" value="Add Name">

</form>

Form action submit được xử lý rất thông thường như 1 jsp page khác,

Ngoài cách tạo actionURL trong doEdit() bạn có thể tạo ngay trong edit.jsp bằng cách portlet tag như sau:

<portlet:actionURL var="addNameURL"><portlet:param name="addName" value="addName"/></portlet:actionURL>

Trong edit.jsp trên đây ta thấy 1 điều nữa:

id="<portlet:namespace />form”, nếu bạn đặt id cho 1 html tag nào đó nó nên gán thêm <portlet:namespace/> hoặc <%=renderResponse.getNamespace()%> vào trước, nó sẽ đám bảo id trong portlet của bạn được phân biết với các portlet khác trong nhiều trường hợp, ví dụ: với My First Portlet

<portlet:namespace/> : chính là: _myportlet_WAR_myportlet_

Về cấu tạo: namespace là: _[-tên-portlet-]_WAR_[-tên-project-]_

Tất cả các tên sẽ được chuyển về dang viết thường, không dấu gạch ngang, dấu cách

Một portlet đơn giản đã hoàn thành, bây giờ bạn: ant deploy một lần nữa

Và refresh trang web chúng ta sẽ thấy “My First Portlet” trở thành:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Để chuyển qua edit mode bạn click vào menu title(chỗ thanh c-lê góc trái trên portlet) của My First Portlet chọn Cấu hình/Configuration (không phải cấu hình có biểu tượng hình bánh răng) ta sẽ có:

Và bây giờ bạn add name sau đó

Khi Add Name được nhấn action sẽ được bắt bởi processAction() method trong MyPortlet.java nó sẽ check xem action đang tới có phải “addName” hay không để thực hiện các công việc tiếp

Nếu dừng lại ở đây, các bạn hoàn toàn có thể sử dụng mô hình này để tạo ra những portlet tùy biến cho mình, nhưng với liferay còn có nhưng mô hình code khác , có thể nhanh hay hiệu quả hơn tùy tình huống mà bạn áp dụng, trước khi đến với 1 MVC hoàn chỉnh trong liferay tôi sẽ giới thiệu cho ác bạn cách phát triển nhanh hơn dùng

IDE eclipse, chi tiết như sau:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Trước hết các bạn nên copy toàn bộ thư mục của liferay server trên sang một ổ đĩa khác, hoặc tạo 1 folder mới copy chúng vào đó, đừng để path quá dài nêu không sẽ không deploy được, và sau đó tiến hành kết nối IDE với server mới này, tại sao pahir làm như vậy có 2 lý do:

- Server deploy bằng tay nếu có thêm các config của IDE vào sẽ không chạy được nữa

- IDE chỉ dung cho develop không thể dung cho chạy thật như vậy rất nặng và dễ bị tắt mất server bị nó cũng chỉ là 1 editor đặc biệt.

Còn về database , schema, bạn có thể dung chung, nhưng nếu ai cẩn thận hơn thi cũng tạo mới 1 cái schema khác khi đó nhớ config lại portal-ext.properties.

Step 1:

Download IDE tại địa chỉ này; http://www.liferay.com/downloads/liferay-projects/liferay-ide, chọn đúng bản 32 bits hay 64 bits, windows hay mac

Step 2:

Run IDE

2.1 Config server liferay

WindowPreferenceserverRuntime environment add

Browse tớ thư mục tomcat trong liferay server

Chon JRE là jdk_...

Cách cài đặt khá giống server tom cat thường

2.2 Config SDK

Chọn WindoePreference LiferayInstalled Plugins SDK Add

Browse tới folder SDK ok

Step 3:

Tạo 1 server mới từ server tap của IDE

Running!

Tạo mới Portlet trong IDE

New Liferay project chọn Portlet,

Ok như vậy 1 portlet mới sẽ được tao ra trong thư mục portlets của SDK có chứa các thông số của IDE để dùng được chúng trong SDK của server deploy bằng tay các bạn chỉ cần copy sang folder tương ứng (portlets) trong SDK, rồi xóa các config của IDE rồi

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

compile và ant deploy lại, nếu bạn dùng bản SDK cao hơn cho IDE thì tốt nhất phải xóa cả các file trong WEB-INF/classes của portlet để SDK thấp hơn compile lại,

(thực ra dùng đúng phiên bản sẽ tốt hơn, nhưng do mục đích của nhiều devs thì họ không muốn sau khi ant deploy portlet của họ sẽ tự động copy vào thư mục deploy của server mà vào 1 thư mục khác, điều này khá quan trọng vì nếu không may deploy có vẫn đề gì xảy ra cả server có thể chết, vì vậy họ thường dùng bản SDK cao hơn 1 chút kiểu 6.1.1.1 ngày 20/ thay cho 6.1.1.1 ngay mùng 1, như vậy file .war ngoài tồn tại trong thư mục dist của SDK còn được copy sang 1 thư mục khác là bundles/deploy/ cùng thư mục gốc với SDK đó mà không hot deploy thẳng vào server đang chạy, cho file .war đi test vài lần rồi cho chạy thật) trên server đang chạy tôi có dùng phiên bản server build 6010(thông số này cùng thể hiện trong table Release_ ở database) nhưng SDk là 6011, nhưng chú ý là chỉ portlet được tạo ra create.bat bởi 6010 mới chạy được trên server 6010 thôi, 6011 chỉ dung để compile ,ant deploy!)

Chương 3: Viết applications Trên Liferay:

Trong chương này chúng ta sẽ tìm hiểu về:

- Liferay Portlet có tương tác cơ sở dữ liệu- Liferay service builder- Thiết kế ứng dụng dựa trên DAOs, DTOs- Định nghĩa các quan hệ trong service builder

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Nhìn vào hình vẽ ta thấy Lớp Portlet layer sẽ gọi các method của ServiceLayer thông qua DATs và Service layer sẽ gọi các method của persistence layer, Sping và Hibernate thông qua DAOs, tiếp đến Spring và Hibernate sẽ thực hiện các transaction với Database thông qua JDBC thông thường.

Service Builder bao gồm DATs + service layer + DAOs , phần việc của devs trong liferay chỉ là làm việc với Portlet UI layer và service layer, giảm thiểu một lượng lơn công việc so với các framework thông thường.

Hãy tiếp cân một mô hình thư viện, tạo 1 book Portlet dùng IDE để hiểu hơn về quan hệ giữa các lớp:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Click finish;

I: Service layer

Tạo package: vn.neo.training.library trong Web-INF/src

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Click chuột phải vào project book-portlet vừa tạo chọn newotherLiferay Service Builder

Browse chọn package path là path vừa tạo ở trên vn.neo.training.library và điền namespace là NEO ( namepace này khác hoàn toàn so với <portlet:namespace/>)

Click finish

Như vậy bạn đã có file service.xml trong docroot/WEB-INF , việc tạo ra các entity trong service.xml với các column tương ứng như viết các table và thêm các attributes cho chúng

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Edit service.xml như sau:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.1.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_1_0.dtd"><service-builder package-path="vn.neo.training.library">

<author>vanthuan</author><namespace>NEO</namespace>

<entity name="LMSBook" table="book" local-service="true" remote-service="false">

<!-- PK fields --><column name="bookId" type="long" primary="true"/><!-- --><column name="companyId" type="int" /><column name="groupId" type="int" />

<!-- UI fields --><column name="bookTitle" type="String" /><column name="author" type="String" /><!-- Audit fields --><column name="dateAdded" type="Date" /><!-- order --><order by="desc">

<order-column name="dateAdded"/></order><!-- finder --><finder return-type="Collection" name="DateAdded">

<finder-column name="dateAdded"></finder-column></finder><finder return-type="Collection" name="CompanyId">

<finder-column name="companyId"></finder-column></finder>

</entity></service-builder>

Một vài chú thích về service.xml:- tag <entity>:o name: tên của entity cũng là tên model class, sẽ là 1 phần tên

của table trong database nêu trường table không có, vi dụ nếu table không được định nghĩa thì trog database sẽ là: [-namespace-]_[-tên-entity-] ví dụ: NEO_LMSBook

o table: tên table trong database, ví du?: table=”book”, trong database sẽ có bảng BOOK thay vì NEO_LMSBook

o uuid: nếu uuid=”true” service sẽ tạo giúp bạn mỗi key độc nhất đồng thể cho mỗi record trong table (uuid là: unique uniform identifier)

o …- Tag <column>o name: định nghĩa các attributes của table và field của entity

class. Như vậy quy tắc đặt tên cho column nên theo kiểu hướng đối tượng ví dụ bookTitle thay vì các dạng khác như book_title,…

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

o type: định nghĩa kiểu dữ liệu cho attribute của table và field của entity class; String <=> varchar2; int , long number trong oracle, không thể tọ ra long long mặc dù vậy với database oracle không cho phép bạn định nghĩa nhiều hơn 1 trường kiểu long trong table vì vậy bạn nên hạn chế dùng kiểu long nhất có thể( việc này cần nhiều cân nhắc vì tất cả các trường số trong table mặc định của liferay hầu như đều dùng kiểu long)

- tag <order > <order-column>sẽ định nghĩa sự sắp xếp các bản ghi của table theo trường và thứ tự mà được định nghĩa bởi <order-column> ví dụ:

<order by="desc"><order-column name="dateAdded"/>

</order> Hay:

- tag<finder>, <finder-column> công cụ này khá hữu dụng bạn sẽ định nghĩa các method query table dựa theo 1 hay nhiều trường khác nhau mà được định nghĩa bởi <finder-column>,các method này sẽ dùng được sau khi build service thông qua 1 biến persistence local của service class vi dụ:

- Đối với tag <finder> có các attribute là name=”” và return-type=”” thì name sẽ định nghĩa 1 phần tên của method : findBy[-tên-finder], và nếu dữ liệu trả về 1 list các entity thìreturn-type=”Collection” và nếu 1 entity thì return-type=”Collections”ví dụ:

<finder return-type="Collection" name="DateAdded"><finder-column name="dateAdded"></finder-column>

</finder>

Sẽ tạo ra một phương thức findByDateAdded(date: Date): List<LMSBook> và gọi được thông qua lmsBookPersistence Còn 1 số tag của service builder cũng khá quan trong tôi sẽ gioqis thiểu sau

Bây giờ nều bạn click phải chuột vào service.xmlliferaybuild service thì

Service builder sẽ gen ra rất nhiều thứ, chúng ta chỉ tập trung vào các package chính như:

vn.neo.training.library.service.impl: giúp chúng ta tùy biến được các công việc thêm, sửa, xóa, lọc dữ liệu với database thông qua service builder, ta chú ý vào các class [-entity-name-]LocalServiceImpl.java ví dụ LMSBookLocalServiceImpl.java, ta trực tiếp viết các method để tùy biễn service builder tại đây.

Ví dụ như:

package vn.neo.training.library.service.impl;

import java.util.Date;import java.util.List;import com.liferay.portal.kernel.exception.PortalException;

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

import com.liferay.portal.kernel.exception.SystemException;

import vn.neo.training.library.model.LMSBook;import vn.neo.training.library.service.base.LMSBookLocalServiceBaseImpl;

/** * The implementation of the l m s book local service. * * <p> * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the {@link vn.neo.training.library.service.LMSBookLocalService} interface. * * <p> * This is a local service. Methods of this service will not have security checks based on the propagated JAAS credentials because this service can only be accessed from within the same VM. * </p> * * @author vanthuan * @see vn.neo.training.library.service.base.LMSBookLocalServiceBaseImpl * @see vn.neo.training.library.service.LMSBookLocalServiceUtil */public class LMSBookLocalServiceImpl extends LMSBookLocalServiceBaseImpl {

/* * NOTE FOR DEVELOPERS: * * Never reference this interface directly. Always use {@link

vn.neo.training.library.service.LMSBookLocalServiceUtil} to access the l m s book local service.

*/public LMSBook addLMSBook(LMSBook newBook, int userId) throws

PortalException, SystemException{LMSBook lmsBook =

lmsBookPersistence.create(counterLocalService.increment(LMSBook.class.getName()));

lmsBook.setAuthor(newBook.getAuthor());lmsBook.setBookTitle(newBook.getBookTitle());lmsBook.setCompanyId(newBook.getCompanyId());lmsBook.setDateAdded(newBook.getDateAdded());lmsBook.setGroupId(newBook.getGroupId());/* thêm resource LMSBook mới cho organization, user group,

*/

resourceLocalService.addResources(newBook.getCompanyId(),newBook.getGroupId(),userId,LMSBook.class.getName(),lmsBook.getPrimaryKey(), false, true, true);

return lmsBookPersistence.update(lmsBook, false);}

public List<LMSBook> getBookByDateAdded(Date date) throws SystemException{

return lmsBookPersistence.findByDateAdded(date);}

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

public LMSBook getOldestBook(){return lmsBookFinder.findOldestBook();

}}

Class này ban đầu trống, tôi chỉ thêm vài method vào đó để minh họa

vn.neo.training.library.service.persistence: giúp tạo ra các tuy biến cao hơn, dùng các custom-sql để truy vẫn được linh hoạt hơn xuống database bằng cách tạo ra các class: [-entity-name-]FinderImpl extends BasePersistenceImpl<[-entity-]> implements @interface [-entity-name-]Finder

ví dụ: như:

package vn.neo.training.library.service.persistence;

import java.util.List;

import vn.neo.training.library.model.LMSBook;

import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;

public class LMSBookFinderImpl extends BasePersistenceImpl<LMSBook> implements LMSBookFinder{ public List<LMSBook> findOrderBooks(int start , int end){

return null; // tạm thời chưa implement } public LMSBook findOldestBook(){

return null; // tạm thời chưa implement }}

Bạn tự hỏi hoàn toàn chưa có interface LMSBookFinder , và IDE báo nỗi, bạn cần build lại service thì sẽ không còn lỗi, và các method ta vừa định nghĩa mới có thể dùng được thông qua lmsBookPersistence( đối vơi method định nghĩa thông qua finder)hay lmsBookFinder(đối với các method được định nghĩa qua LMSBookFinderImpl)(chú ý: đây cũng chỉ là 2 ví dụ mang tính minh họa, LMSBookFinderImpl)

Và sau khi build service các method của class LMSBookLocalServiceImpl sẽ được gọi qua LMSBookLocalServiceUtil như 1 static method nếu muốn sử dụng ở Portlet code layer

Ví dụ

LMSBookLocalServiceUtil.addLMSBook(newbook); hay LMSBookLocalServiceUtil.getByDateAdded(date);

2. Portlet UI layer

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Có 2 phần việc phải làm là: tạo các client site .jsp, .css, .js

Và file control .java

Client site: tạo folder book/jsp trong docroot

Để tiện cho không phải import nhiều lần chúng ta nên tạo 1 file init.jsp chứa các import tag và những định nghĩa cần thiết rồi import vào các file khác

File init.jsp trong book/jsp/init.jsp c

Tạo file docroot/book/jsp/view.jsp với thông tin là

%@include file= "/book/jsp/init.jsp" %

<!-- tạo renderUrl để link tới mục update book -->

<portlet:renderURL var="updateBookURL">Liferay Portlet Development – A definitive guide 11<portlet:param name="jspPage" value="/book/jsp/update.jsp"/></portlet:renderURL><br/><a href="<%=updateBookURL %>">Add new Book &raquo;</a>

Đoạn code trên cần import

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %><portlet:defineObjects />

Thêm vào init.jsp

Tạo file book/jsp/update.jsp với thông tin như sau

<%@include file="/book/jsp/init.jsp" %><%PortletURL updateBookURL = renderResponse.createActionURL();updateBookURL.setParameter(ActionRequest.ACTION_NAME, "updateBook");%><aui:form name="fm" method="POST" action="<%=updateBookURL.toString() %>"><aui:input name="bookTitle" /><aui:input name="author"/><aui:button type="submit" value="Save"/></aui:form><a href="<portlet:renderURL/>">&laquo;Go Back</a>

Các import cần thiết là:

<%@page import="javax.portlet.PortletURL"%><%@page import="javax.portlet.ActionRequest"%><%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>

Dùng alloy UI khá tiện trong liferay, một aui:input đã bao gồm trong đó 1 lable và 1 thẻ input, và hơn nữa nó sẽ tiện cho việc hỗ trợ đa ngôn ngữ

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Nếu ta không định nghĩa thêm id thì mỗi thẻ aui: sẽ có id là <portlet:namespace>[-tên-của-thẻ-aui-] chú ý là giá trị của <portlet:namespace/> ví dụ:

<aui:input name="author"/>Id là:_book_WAR_bookportlet_author

Trừ thẻ aui:input type=”checkbox” thì id sẽ thêm CheckBox vào cuối nữaVí dụ:<aui:input name="check" type="checkbox"/>

Có id là: book_WAR_bookportletCheckBox

Tiếp tục Open portlet.xml, thay đổi tag<init-param>

<init-param><name>view-jsp</name><value>/book/jsp/view.jsp</value>

</init-param>

Nếu compile portlet bằng cách , phải chuột vào portlet, chọn run on server hay, phải chuột vào 1 server chon Add and Remove Add book-portlet finish

Sau đó add book portlet trong Add Add sample thì sẽ có lỗi file quote not found bởi vị tên portlet của ta bị trung với tên folder book, ta tiến hành thay đổi tên portlet trở thành: book-portlet,

<portlet-name>book-portlet</portlet-name>

Open liferay-portlet.xml và liferay-display.xml sửa tên và id theo tên portlet tương ứng

<portlet-name>book-portlet</portlet-name><icon>/icon.png</icon><instanceable>false</instanceable>

<portlet id="book-portlet" />

Redeploy lại book-portlet

Remove and Add book-portlet lại, nếu xem source của trang Web chứa portlet đó

<div class="portlet-body" id="aui_3_4_0_1_1378"> <br><a href="http://localhost:8088/web/microsite/tranning?p_p_id=bookportlet_WAR_bookportlet&amp;p_p_lifecycle=0&amp;p_p_state=normal&amp;p_p_mode=view&amp;p_p_col_id=column-1&amp;p_p_col_count=1&amp;_bookportlet_WAR_bookportlet_jspPage=%2Fbook%2Fjsp%2Fupdate.jsp" id="aui_3_4_0_1_1377">Add new Book »</a> </div>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<portlet:renderUrl/> sẽ tạo ra 1 đường link với tham số jspPage với giá trị là escape của /book/jsp/update.jsp(tham số này ta dùng như mặc định để chuyển đến một trang trong portlet,nó cũng giống như struts_action chỏ tới một strut action nào đó hay redirect chỉ định 1 trang nào đó sẽ tự động(cũng tùy nhiều trường hợp sẽ không tự động) được chuyển đến khi xong 1 submition,..)

Nếu click Add new Book

Đây là dạng hiển thị của aui:input và form source của nó:

<div class="portlet-body" id="aui_3_4_0_1_520"> <form action="http://localhost:8088/en/web/microsite/tranning?

p_auth=YDykMhK8&amp;p_p_id=bookportlet_WAR_bookportlet&amp;p_p_lifecycle=1&amp;p_p_state=normal&amp;p_p_mode=view&amp;p_p_col_id=column-2&amp;p_p_col_count=1&amp;_bookportlet_WAR_bookportlet_javax.portlet.action=updateBook" class="aui-form yui3-widget aui-form-validator aui-form-validator-content aui-form-validator-focused" id="_bookportlet_WAR_bookportlet_fm" method="POST" name="_bookportlet_WAR_bookportlet_fm"> <input name="_bookportlet_WAR_bookportlet_formDate" type="hidden" value="1380417921678">

<span class="aui-field aui-field-text" id="aui_3_4_0_1_519"> <span class="aui-field-content" id="aui_3_4_0_1_518">

<label class="aui-field-label" for="_bookportlet_WAR_bookportlet_bookTitle"> Book Title </label>

<span class="aui-field-element " id="aui_3_4_0_1_517">

<input class="aui-field-input aui-field-input-text" id="_bookportlet_WAR_bookportlet_bookTitle" name="_bookportlet_WAR_bookportlet_bookTitle" type="text" value="">

</span> </span> </span> <span class="aui-field aui-field-text" id="aui_3_4_0_1_555"> <span class="aui-field-content" id="aui_3_4_0_1_554"> <label class="aui-field-label"

for="_bookportlet_WAR_bookportlet_author"> Author </label> <span class="aui-field-element " id="aui_3_4_0_1_553"> <input class="aui-field-input aui-field-input-

text" id="_bookportlet_WAR_bookportlet_author" name="_bookportlet_WAR_bookportlet_author" type="text" value="">

</span>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

</span> </span> <span class="aui-button aui-button-submit"> <span class="aui-button-content"> <input class="aui-button-input aui-button-input-submit"

type="submit" value="Save"> </span> </span>

</form> <a href="http://localhost:8088/en/web/microsite/tranning?p_p_id=bookportlet_WAR_bookportlet&amp;p_p_lifecycle=0&amp;p_p_state=normal&amp;p_p_mode=view&amp;p_p_col_id=column-2&amp;p_p_col_count=1">«Go Back</a></div>

Ta thấy rằng form submit mit của alloy ui rất khác javax.portlet.action=updateBook, nếu ta click save bây giờ sẽ bị lỗi

01:31:40,140 ERROR [http-bio-8088-exec-122][render_portlet_jsp:154] java.lang.NoSuchMethodException: com.liferay.util.bridges.mvc.MVCPortlet.updateBook(javax.portlet.ActionRequest, javax.portlet.ActionResponse)

Vì bạn chưa implement action này,

Bước tiếp theo: implement control class:

Tạo class vn.neo.training.library.portlet.BookPortlet extends MVCPortlet trong WEB-INF/src:

package vn.neo.training.library.portlet;

import com.liferay.util.bridges.mvc.MVCPortlet;

public class BookPortlet extends MVCPortlet {

}

Open portlet.xml thay đổi

<portlet-class>vn.neo.training.library.portlet.BookPortlet</portlet-class>

Định nghĩa updateBook action:

public void updateBook(ActionRequest actionRequest,ActionResponse actionResponse)

throws IOException, PortletException {ThemeDisplay themeDisplay = (ThemeDisplay)

actionRequest.getAttribute(WebKeys.THEME_DISPLAY);String bookTitle = ParamUtil.getString(actionRequest,

"bookTitle");String author = ParamUtil.getString(actionRequest,

"author");LMSBook book = new LMSBookImpl();

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

// set UI fieldsbook.setBookTitle(bookTitle);book.setAuthor(author);book.setCompanyId((int)themeDisplay.getCompanyId());book.setGroupId((int)themeDisplay.getScopeGroupId());// set audit field(s)book.setDateAdded(new Date());try{

/*phương thức của DATs hỗ trợ giao tiếp với service layer*/

LMSBookLocalServiceUtil.addLMSBook(book, (int)themeDisplay.getUserId());

SessionMessages.add(actionRequest,"add-success"); // key sẽ bắt được nếu thành công

}catch (Exception e) {SessionErrors.add(actionRequest, "add-errors");

// key sẽ bắt được nếu thất bại}

}

Chúng ta chú ý tới 2 class mới là SessionMessages và SessionErrors và 1 util Class là ParamUtil,ParamUtil sẽ là 1 kiểu request.getParameter() nhưng đã tự parse và catch exception(nếu parameter không tồn tại, nếu kiểu int thì ParamUtil sẽ trả về 0, và nếu kiểu String sẽ trả về ”” ) 2 class kia sẽ giúp bắt được sự kiện trong Portlet Code layer và gửi sự kiện nó nó Client Site thông qua các key ta đinh nghĩa như là: "add-success” , "add-errors" ở trên,

Chúng ta bắt các sự kiện của SessionMessages và SessionError bằng cách dùng <liferay-ui:success> và tag import cần thiết là

<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%> thêm vào init.jspThêm 2 thẻ sau vào trước thẻ form trong update.jsp ta sẽ bắt được sự kiện gửi từ Portlet Code

<liferay-ui:success key="add-success" message="added-successfully"/><liferay-ui:error key="add-errors" message="added-unsuccesfully" />

Và save

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

There are no actions associated with the resource vn.neo.training.library.model.LMSBook

Lúc này vì lý do addResources trong service layer nên chúng ta cần khai báo resource cho LMSBook model.

Công việc bày khá đơn giản

Tạo folder resource-actions và file portlet.xml trong WeB-InF/src

Portlet.xml có nội dung như sau:

resource.actions.configs=resource-actions/default.xml

Tạo file default.xml trong folder resource-actions với nội dung như sau:

<?xml version="1.0" encoding="UTF-8"?><resource-action-mapping>

<portlet-resource><portlet-name>book-portet</portlet-name><permissions>

<supports><action-key>VIEW</action-key><action-key>ADD_BOOK</action-key>

</supports><community-defaults>

<action-key>VIEW</action-key></community-defaults><site-member-defaults>

<action-key>VIEW</action-key></site-member-defaults><guest-defaults>

<action-key>VIEW</action-key></guest-defaults><guest-unsupported>

<action-key>ADD_BOOK</action-key>

</guest-unsupported></permissions>

</portlet-resource>

<model-resource><model-name>vn.neo.training.library.model.LMSBook</model-

name><portlet-ref>

<portlet-name>book-portlet</portlet-name></portlet-ref> <permissions> <supports> <action-key>VIEW</action-key> <action-key>PERMISSIONS</action-key> <action-key>DELETE</action-key>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<action-key>UPDATE</action-key> </supports> <site-member-defaults> <action-key>VIEW</action-key> </site-member-defaults> <guest-defaults> <action-key>VIEW</action-key> </guest-defaults> <guest-unsupported> <action-key>UPDATE</action-key> </guest-unsupported> </permissions>

</model-resource></resource-action-mapping>

Sau khi khai báo như trên các resesource actions sẽ được update vào table Resource

Và bạn phải login mới thêm được sách vì:

<guest-unsupported> <action-key>UPDATE</action-key>

</guest-unsupported>

Bây giờ bạn đã thêm được book

Nhưng sẽ không có thông báo nào được hiên ra cho bạn vì sau khi thêm xong ActionResponse tự redirect về view.jsp, (bởi vì chúng ta render từ view.jsp sang update.jsp, render gốc vẫn là view.jsp không phải update.jsp), view không có các catch key (liferay-ui:)như update.jsp và mặc dù chúng ta đã thoát khỏi form update nhưng submition vẫn xảy ra nếu bạn refresh lại trang, 2 records giống hệt nhau sẽ insert xuống database, điều này khá bất tiện, hơn nữa về nguyên tắc nếu thêm thành công rồi bạn sẽ redirect về view.jsp và thông báo thánh công, còn không thì vẫn redirect về update.jsp và điền lại các thông tin, nhưng vẫn giữ lại những thông tin đã nhập trước đó, và 1 cuốn sach thì phải có title bạn sẽ bắt cả lỗi này nữa,

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Như vậy ta sẽ xử lý như sau:

Dùng redirect

Sửa đổi view.jsp như sau:

<%@include file="/book/jsp/init.jsp" %><portlet:renderURL var="updateBookURL"><portlet:param name="jspPage" value="/book/jsp/update.jsp"/></portlet:renderURL><liferay-ui:success key="add-success" message="added-successfully"/>

<br/><a href="<%=updateBookURL %>">Add new Book &raquo;</a>

Thêm vào init.jsp<%@page import="vn.neo.training.library.model.impl.LMSBookImpl"%><%@page import="vn.neo.training.library.model.LMSBook"%>

Sửa đổi update.jsp như sau:

<%@include file="/book/jsp/init.jsp" %><portlet:renderURL var="redirectUrl">

<portlet:param name="jspPage" value="/book/jsp/view.jsp"/></portlet:renderURL><%PortletURL updateBookURL = renderResponse.createActionURL();updateBookURL.setParameter(ActionRequest.ACTION_NAME, "updateBook");LMSBook book = null;book = (LMSBook)request.getAttribute("book");if(book == null) book = new LMSBookImpl();%>

<liferay-ui:success key="add-success" message="added-successfully"/>

<liferay-ui:error key="add-errors" message="added-unsuccesfully" /><liferay-ui:error key="missing-title" message="missing-book-title" />

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<aui:form name="fm" method="POST" action="<%=updateBookURL.toString() %>"><aui:input name="bookTitle" value="<%=book.getBookTitle() %>"/><aui:input name="author" value="<%=book.getAuthor() %>"/><aui:input type="hidden" name="redirect"value="<%=redirectUrl%>" /><aui:button type="submit" value="Save"/></aui:form><a href="<portlet:renderURL/>">&laquo;Go Back</a>

Và sửa đổi BookPortlet.java như sau:

package vn.neo.training.library.portlet;

import java.io.IOException;import java.util.Date;

import javax.portlet.ActionRequest;import javax.portlet.ActionResponse;import javax.portlet.PortletException;

import vn.neo.training.library.model.LMSBook;import vn.neo.training.library.model.impl.LMSBookImpl;import vn.neo.training.library.service.LMSBookLocalServiceUtil;

import com.liferay.portal.kernel.servlet.SessionErrors;import com.liferay.portal.kernel.servlet.SessionMessages;import com.liferay.portal.kernel.util.ParamUtil;import com.liferay.portal.kernel.util.Validator;import com.liferay.portal.kernel.util.WebKeys;import com.liferay.portal.theme.ThemeDisplay;import com.liferay.util.bridges.mvc.MVCPortlet;

public class BookPortlet extends MVCPortlet {public void updateBook(ActionRequest actionRequest,ActionResponse

actionResponse) throws IOException, PortletException {

ThemeDisplay themeDisplay = (ThemeDisplay) actionRequest.getAttribute(WebKeys.THEME_DISPLAY);

String bookTitle = ParamUtil.getString(actionRequest, "bookTitle");

String author = ParamUtil.getString(actionRequest, "author");

String redirect = ParamUtil.getString(actionRequest,"redirect");

LMSBook book = new LMSBookImpl();

// set UI fieldsbook.setBookTitle(bookTitle.trim());book.setAuthor(author.trim());book.setCompanyId((int)themeDisplay.getCompanyId());book.setGroupId((int)themeDisplay.getScopeGroupId());// set audit field(s)book.setDateAdded(new Date());System.out.println("book: " + book);

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

if(!Validator.isNull(book.getBookTitle())){ // phương thức của liferay giúp check các gia trị khác nhau như null, email, .. khá giống Spring

try{/*phương thức của DATs hỗ trợ giao

tiếp với service layer*/

LMSBookLocalServiceUtil.addLMSBook(book, (int)themeDisplay.getUserId());

SessionMessages.add(actionRequest,"add-success"); // key sẽ bắt được nếu thành công

actionResponse.sendRedirect(redirect);}catch (Exception e) {

e.printStackTrace();actionRequest.setAttribute("book",

book);

actionResponse.setRenderParameter("jspPage","/book/jsp/update.jsp");SessionErrors.add(actionRequest,

"add-errors"); // key sẽ bắt được nếu thất bại}

} else{actionRequest.setAttribute("book", book);

actionResponse.setRenderParameter("jspPage","/book/jsp/update.jsp");SessionErrors.add(actionRequest, "missing-

title");}

}}

Bây giờ nếu add new book

Thì

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Và chúng ta refresh lại trang thì sẽ không còn popup submition lại nữaHay Thêm 1 book mà không có title

Các bạn tự hỏi tại sao tôi toàn viêt các thông báo kiểuAdded-successfullyMissing-book-title mà không viết nghĩa rõ rangTôi làm điều này vì tôi muốn Book-portlet sẽ hiển thị được nhiều ngôn ngữ

3. Đa ngôn ngữ:Open portlet.xml thêm

<resource-bundle>content.Language</resource-bundle>Ngay dưới tag supportTạo folder content trong Web-InF/src, tạo file Language.properties trong folder contentSửa đổi nội dung file Language.properties như sau:book-title: Book Titleauthor: Authoradded-successfully: New Book was added successfullymissing-book-title: Book title cannot be left blanksau đó nếu dung IDE các bạn phải chuột vào book-portlet và chọn

Liferaybuild languagenếu bạn muốn build bằng tay thì sửa đổi build.xml của book-portlet

như sau:/***********************************************/

<?xml version="1.0"?><!DOCTYPE project>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<project name="book-portlet" basedir="." default="deploy"><import file="../build-common-portlet.xml" /><target name="build-lang">

<antcall target="build-lang-cmd"><param name="lang.dir"

value="docroot/WEB-INF/src/content" /><param name="lang.file" value="Language" />

</antcall></target>

</project>

Sau run in command line câu lệnh( chú ý trược khi chạy bạn phải cd tới folder chứa build.xml)Ant build-lang Chuyển đến file Language_vi.properties sửa đổi như sau:book-title: Ti&#234;u &#273;&#7873; s&#225;ch author: T&#225;c gi&#7843;added-successfully: Th&#234;m m&#7899;i s&#225;ch th&#224;nh c&#244;ngmissing-book-title: Thi&#7871;u ti&#234;u &#273;&#7873; s&#225;ch

và các bạn sẽ thấy

Hay

Bây giờ chuyển qua locale vi_VNGotomy public page chọn lá ngôn ngư việt nam (trong language portlet, la cờ viet nam)Sau đó quay lại trang chứa book-portlet

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Sẽ là:

Ta nhận thấy còn 2 nút là Add new Book và go back chưa được localizeTiến hanh làm như sau:Sửa các thẻ <a> chứa Add new book và go back trong các file view.jsp và update,jsp thành:<br/><a href="<%=updateBookURL %>"><liferay-ui:message key="add-new-book"/>&raquo;</a>

<a href="<portlet:renderURL/>">&laquo;<liferay-ui:message key="go-back"/></a>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Thêm 2 key vào Language.properties add-new-book: Add New Bookgo-back: Go backvà Language_vi.propertiesadd-new-book: Th&#234;m m&#7899;igo-back: Tr&#7903; v&#7873; trang tr&#432;&#7899;c

Tương tụ ta có thể làm như vậy với cac file language khác, và portlet sẽ hiên thị được các ngôn ngữ khác nhau

Hay tiếp tục khám phá các khía cạnh của service layer:Custom-sql:Implement các function của LMSBookFinderImpl.java

Tạo folder custom-sql trong WEB-INF/srcTạo file default.xml với nội dung như sau<?xml version="1.0" encoding="UTF-8"?><custom-sql>

<sql id="vn.neo.training.library.service.persistence.LMSBookFinder.findAllAuthors">

<![CDATA[ SELECT DISTINCT author FROM book ORDER BY author ASC]]>

</sql>

<sql id="vn.neo.training.library.service.persistence.LMSBookFinder.findOrderBooks">

<![CDATA[ SELECT * FROM book ORDER BY dateAdded DESC]]>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

</sql>

<sql id="vn.neo.training.library.service.persistence.LMSBookFinder.findNumOfBooksAdded">

<![CDATA[ SELECT count(*) numOfBooks FROM book WHERE dateAdded BETWEEN TO_DATE(?,'dd/mm/yyyy') AND

TO_DATE(?,'dd/mm/yyyy')]]>

</sql></custom-sql>

Đây là file sẽ chứa các câu lênh sql , mỗi câu lênh sẽ có 1 id nhất định thường là [-entity-name-Finder-class].[-ten-finder-];

Các câu lênh sql này được gọi ra trong các method củaLMSBookFinderImpl.java

Đây là LMSBookFinderImpl.java hoành chỉnhpackage vn.neo.training.library.service.persistence;

import java.util.ArrayList;import java.util.Iterator;import java.util.List;

import vn.neo.training.library.model.LMSBook;import vn.neo.training.library.model.impl.LMSBookImpl;

import com.liferay.portal.kernel.dao.orm.ORMException;import com.liferay.portal.kernel.dao.orm.QueryPos;import com.liferay.portal.kernel.dao.orm.QueryUtil;import com.liferay.portal.kernel.dao.orm.SQLQuery;import com.liferay.portal.kernel.dao.orm.Session;import com.liferay.portal.kernel.dao.orm.Type;

import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;import com.liferay.util.dao.orm.CustomSQLUtil;

public class LMSBookFinderImpl extends BasePersistenceImpl<LMSBook> implements LMSBookFinder{

private final String FIND_NUM_OF_BOOK_ADDED = LMSBookFinder.class.getName() +".findNumOfBookAdded";

private final String NUM = "numOfBooks";private final String FIND_ALL_AUTHORS =

LMSBookFinder.class.getName() +".findAllAuthors";private final String FIND_ORDER_BOOKS =

LMSBookFinder.class.getName() + ".findOrderBooks";

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

public List<LMSBook> findOrderBooks(int start , int end){ Session session = null; try{

String sql = CustomSQLUtil.get(FIND_ORDER_BOOKS); session = openSession(); SQLQuery q = session.createSQLQuery(sql); q.addEntity("LMSBook",LMSBookImpl.class);

return (List<LMSBook>) QueryUtil. list (q, getDialect(), start, end);

}catch(ORMException e){ e.printStackTrace();

} return new ArrayList<LMSBook>();

} public List<String> findAllAuthors(){

Session session = null; List<String> authors = new ArrayList<String>(); try{

String sql = CustomSQLUtil.get(FIND_ALL_AUTHORS); System.out.println(sql); session = openSession(); SQLQuery q = session.createSQLQuery(sql); q.addScalar("author", Type.STRING); /* nếu bạn add nhiều hơn 1 scalar thì dùng * List<Object[]> objs = (List<Object[]>)

QueryUtil.list(q, getDialect(), QueryUtil.ALL_POS, QueryUtil.ALL_POS); for(Object[] obj : objs){ //populate vào result theo thứ tự tương ứng }

* */ List<String> objs = (List<String>) QueryUtil. list (q,

getDialect(), QueryUtil. ALL_POS , QueryUtil. ALL_POS ) ; for(String obj : objs){

authors.add(obj);

}

}catch(ORMException e){ e.printStackTrace();

} return authors;

} public int findNumOfBooksAdded(String date1, String date2){

Session session = null;

try{ String sql = CustomSQLUtil.get(FIND_NUM_OF_BOOK_ADDED); session = openSession(); SQLQuery q = session.createSQLQuery(sql); q.addScalar(NUM, Type.INTEGER);

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

QueryPos queryPos = QueryPos.getInstance(q); queryPos.add(date1); queryPos.add(date2);

Iterator<Long> itr = q.iterate();if(itr.hasNext()){

Long count = itr.next();if(count != null)

return count.intValue();}

}catch(ORMException e){ e.printStackTrace();

} return 0;

} public LMSBook findOldestBook(){

Session session = null; try{

String sql = "select * from (select * from book order by dateadded asc ) where rownum = 1";

session = openSession(); SQLQuery q = session.createSQLQuery(sql); q.addEntity("LMSBook",LMSBookImpl.class); List<LMSBook> oldbBooks = (List<LMSBook>)

QueryUtil. list (q, getDialect(), QueryUtil. ALL_POS ,QueryUtil. ALL_POS ) ; if(oldbBooks.size() > 0) return oldbBooks.get(0);

}catch(ORMException e){ e.printStackTrace();

} return null;

}}Ta thấy CustomSQlUtil là util class có thể lấy được câu lệnh sql trong file default.xml thông qua id của chúng,Việc implement các query trong class trên khá giống trong hibernate, mặc dù có phần đơn giản hơnVà class: LMSBookLocalServiceImpl.java/** * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

* details. */

package vn.neo.training.library.service.impl;

import java.util.ArrayList;import java.util.Date;import java.util.List;

import com.liferay.portal.kernel.exception.PortalException;import com.liferay.portal.kernel.exception.SystemException;import com.liferay.portal.model.ResourceConstants;

import vn.neo.training.library.model.LMSBook;import vn.neo.training.library.service.base.LMSBookLocalServiceBaseImpl;

/** * The implementation of the l m s book local service. * * <p> * All custom service methods should be put in this class. Whenever methods are added, rerun ServiceBuilder to copy their definitions into the {@link vn.neo.training.library.service.LMSBookLocalService} interface. * * <p> * This is a local service. Methods of this service will not have security checks based on the propagated JAAS credentials because this service can only be accessed from within the same VM. * </p> * * @author vanthuan * @see vn.neo.training.library.service.base.LMSBookLocalServiceBaseImpl * @see vn.neo.training.library.service.LMSBookLocalServiceUtil */public class LMSBookLocalServiceImpl extends LMSBookLocalServiceBaseImpl {

/* * NOTE FOR DEVELOPERS: * * Never reference this interface directly. Always use {@link

vn.neo.training.library.service.LMSBookLocalServiceUtil} to access the l m s book local service.

*/public LMSBook addLMSBook(LMSBook newBook, int userId) throws

PortalException, SystemException{LMSBook lmsBook =

lmsBookPersistence.create(counterLocalService.increment(LMSBook.class.getName()));

lmsBook.setAuthor(newBook.getAuthor());lmsBook.setBookTitle(newBook.getBookTitle());lmsBook.setCompanyId(newBook.getCompanyId());lmsBook.setDateAdded(newBook.getDateAdded());lmsBook.setGroupId(newBook.getGroupId());

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

/* thêm resource LMSBook mới cho organization, user group, */

resourceLocalService.addResources(newBook.getCompanyId(),newBook.getGroupId(),userId,LMSBook.class.getName(),lmsBook.getPrimaryKey(), false, true, true);

return lmsBookPersistence.update(lmsBook, false);}public void deleteLMSBook(int bookId)throws Exception{

try{LMSBook book =

lmsBookPersistence.fetchByPrimaryKey(bookId);deleteLMSBook(book);

}catch(Exception e){throw e;

}

}public LMSBook deleteLMSBook(LMSBook book) throws Exception{

try{

resourceLocalService.deleteResource(book.getCompanyId(),LMSBook.class.getName(),ResourceConstants.SCOPE_INDIVIDUAL, book.getPrimaryKey());

lmsBookPersistence.remove(book); }catch(Exception e){ throw e; }return book;

}public List<LMSBook> getBookByDateAdded(Date date) throws

SystemException{return lmsBookPersistence.findByDateAdded(date);

}public LMSBook getOldestBook(){

return lmsBookFinder.findOldestBook();}public List<String> getAllAuthors(){

return lmsBookFinder.findAllAuthors();}public int getNumOfBooks(String date1, String date2){

return lmsBookFinder.findNumOfBooksAdded(date1, date2);}public List<LMSBook> getBooksByAuthor(String author){

try {return lmsBookPersistence.findByAuthor(author);

} catch (SystemException e) {// TODO Auto-generated catch blockreturn new ArrayList<LMSBook>();

}}

}Mỗi phương thức trong LMSBookLocalServiceImpl sẽ sẵn có sau khi build service thông qua LMSBookLocalServiceUtil, như tôi đã nói trước đó

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

BookPortlet.java hoàn thiện:package vn.neo.training.library.portlet;

import java.io.IOException;import java.util.Date;

import javax.portlet.ActionRequest;import javax.portlet.ActionResponse;import javax.portlet.PortletException;import javax.portlet.PortletSession;

import vn.neo.training.library.model.LMSBook;import vn.neo.training.library.model.impl.LMSBookImpl;import vn.neo.training.library.service.LMSBookLocalServiceUtil;

import com.liferay.portal.kernel.servlet.SessionErrors;import com.liferay.portal.kernel.servlet.SessionMessages;import com.liferay.portal.kernel.util.ParamUtil;import com.liferay.portal.kernel.util.Validator;import com.liferay.portal.kernel.util.WebKeys;import com.liferay.portal.theme.ThemeDisplay;import com.liferay.util.bridges.mvc.MVCPortlet;

public class BookPortlet extends MVCPortlet {public void updateBook(ActionRequest actionRequest,ActionResponse

actionResponse) throws IOException, PortletException {

ThemeDisplay themeDisplay = (ThemeDisplay) actionRequest.getAttribute(WebKeys.THEME_DISPLAY);

String bookTitle = ParamUtil.getString(actionRequest, "bookTitle");

String author = ParamUtil.getString(actionRequest, "author");

String redirect = ParamUtil.getString(actionRequest,"redirect");

int bookId = ParamUtil.getInteger(actionRequest,"bookId");

LMSBook book = new LMSBookImpl();// set PK fieldbook.setBookId(bookId);// set UI fieldsbook.setBookTitle(bookTitle.trim());book.setAuthor(author.trim());book.setCompanyId((int)themeDisplay.getCompanyId());book.setGroupId((int)themeDisplay.getScopeGroupId());// set audit field(s)book.setDateAdded(new Date());System.out.println("book: " + book);if(!Validator.isNull(book.getBookTitle())){ // phương

thức của liferay giúp check các gia trị khác nhau như null, email, .. khá giống Spring

if(book.getBookId() == 0) {//Thêm mới

try{/*phương thức của

DATs hỗ trợ giao tiếp với service layer*/

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

LMSBookLocalServiceUtil.addLMSBook(book, (int)themeDisplay.getUserId());

SessionMessages.add(actionRequest,"add-success"); // key sẽ bắt được nếu thành công

actionResponse.sendRedirect(redirect);}catch (Exception e) {

e.printStackTrace();

actionRequest.setAttribute("book", book);

actionResponse.setRenderParameter("jspPage","/book/jsp/update.jsp");

SessionErrors.add(actionRequest, "add-errors"); // key sẽ bắt được nếu thất bại

}}else{

// cập nhậttry{

LMSBookLocalServiceUtil.updateLMSBook(book,false);

actionResponse.sendRedirect(redirect);

}catch(Exception e){

actionRequest.setAttribute("book", book);

actionResponse.setRenderParameter("jspPage","/book/jsp/update.jsp");

SessionErrors.add(actionRequest, "update-errors"); // key sẽ bắt được nếu thất bại

}}

} else{actionRequest.setAttribute("book", book);

actionResponse.setRenderParameter("jspPage","/book/jsp/update.jsp");SessionErrors.add(actionRequest, "missing-

title");}

}public void deleteBook(ActionRequest request, ActionResponse

response){int bookId

=ParamUtil.getInteger(request,"resourcePrimKey" );if(bookId> 0){

try {LMSBookLocalServiceUtil.deleteLMSBook( bookId);

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

}catch(Exception e){e.printStackTrace();

}}

}public void searchBook(ActionRequest request, ActionResponse

response){String author = ParamUtil.getString(request,"author");if(author.equals("not-choose")) author =null;PortletSession portletSession = request.getPortletSession();portletSession.setAttribute("books",

LMSBookLocalServiceUtil.getBooksByAuthor(author));request.setAttribute("author", author);

}}Các tên method của class trên cũng bầy tỏ phần nào ý nghĩa của chúngVàView.jsp

<%@include file="/book/jsp/init.jsp" %><portlet:renderURL var="updateBookURL"><portlet:param name="jspPage" value="/book/jsp/update.jsp"/></portlet:renderURL><liferay-ui:success key="add-success" message="added-successfully"/>

<br/><a href="<%=updateBookURL %>"><liferay-ui:message key="add-new-book"/>&raquo;</a><%

List<LMSBook> allBook = LMSBookLocalServiceUtil.getLMSBooks(-1, -1); // find all books

String authorKey = (String) request.getAttribute("author");String keyword = (String) ParamUtil.getString(request,"author");

if(authorKey != null) {

keyword = authorKey;

}if(keyword.trim().length() != 0) allBook = (List<LMSBook>)

portletSession.getAttribute("books");PortletURL iteratorUrl = renderResponse.createRenderURL();iteratorUrl.setParameter("jspPage", "/book/jsp/view.jsp");iteratorUrl.setParameter("author",keyword);%>

<portlet:actionURL name="searchBook" var="searchUrl"/><div style="float: right;" class="search"><form action="<%=searchUrl%>" method="post">

<select name="author"><option value="not-choose"><liferay-ui:message

key="not-choose"/></option>

<%

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

List<String> authors = LMSBookLocalServiceUtil.getAllAuthors();

for(String author: authors){%><option value="<%=author%>" <

%=author.equals(keyword)?"selected":"" %>><%=author %></option>

<%}%>

</select><input type="submit" value="<liferay-ui:message key="search"/>"/></form>

</div><liferay-ui:search-container emptyResultsMessage="there-are-no-books" delta="10" iteratorURL="<%=iteratorUrl %>">

<liferay-ui:search-container-results> <% results = ListUtil.subList(allBook,

searchContainer.getStart(),searchContainer.getEnd()); pageContext.setAttribute("results",results); pageContext.setAttribute("total", allBook.size()); %></liferay-ui:search-container-results><liferay-ui:search-container-row

className="vn.neo.training.library.model.LMSBook" keyProperty="bookId" modelVar="book">

<liferay-ui:search-container-column-text name="book-title" property="bookTitle"/>

<liferay-ui:search-container-column-text name="author" property="author"/>

<liferay-ui:search-container-column-jsp name="action" path="/book/jsp/book-actions.jsp"></liferay-ui:search-container-column-jsp>

</liferay-ui:search-container-row><liferay-ui:search-iterator/>

</liferay-ui:search-container>

Trong view.jsp tôi có sự dụng searchcontainer( đây là 1 công cụ khá hữu ích trong liferay) 1 jsp phụ thuộc là book-actions.jsp có nội dung như sau:

<%@include file="/book/jsp/init.jsp" %><% ResultRow resultRow = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);

LMSBook book = (LMSBook) resultRow.getObject();String primKey = String.valueOf(book.getPrimaryKey()); String name = LMSBook.class.getName();

int groupId = book.getGroupId();

%>

<table>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

vanthuan ngo, 09/29/13,

<tr><td><c:if test="<%=permissionChecker.hasPermission(groupId, name, primKey,ActionKeys.UPDATE) %>"> <portlet:renderURL var="editBookUrl"> <portlet:param name="jspPage" value="/book/jsp/update.jsp"/> <portlet:param name="resourcePrimKey" value="<%=primKey %>"/> </portlet:renderURL> <liferay-ui:icon image="edit" message="edit" url="<%=editBookUrl.toString() %>"/></c:if></td><td><c:if test="<%=permissionChecker.hasPermission(groupId, name,primKey,ActionKeys.DELETE)%>"> <portlet:renderURL var="redirectUrl">

<portlet:param name="jspPage" value="/book/jsp/view.jsp"/>

</portlet:renderURL> <portlet:actionURL name="deleteBook" var="deleteBookUrl"> <portlet:param name="resourcePrimKey" value="<%= primKey %>"/> <portlet:param name="redirect" value="<%=redirectUrl %>"/> </portlet:actionURL> <liferay-ui:icon-delete message="delete" url="<%= deleteBookUrl.toString()%>"/> </c:if></td>

</tr></table>

Các bạn thấy sự xuất hiện của permissionChecker ,thực ra đây là một instance của util class trong theme taglib. Nó giúp check xem người dùng có được quyền để sử dụng các chức năng như edit và delete hay không?, các tiêu chuẩn của permission chúng ta đã định nghĩa khi viết resource mapping và add resources trong service layer

Và update.jsp:

<%@include file="/book/jsp/init.jsp" %><portlet:renderURL var="redirectUrl">

<portlet:param name="jspPage" value="/book/jsp/view.jsp"/></portlet:renderURL><%PortletURL updateBookURL = renderResponse.createActionURL();updateBookURL.setParameter(ActionRequest.ACTION_NAME, "updateBook");LMSBook book = null;int bookId = ParamUtil.getInteger(request, "resourcePrimKey");

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

book = (LMSBook)request.getAttribute("book");if(bookId != 0) book = LMSBookLocalServiceUtil.getLMSBook(bookId);if(book == null) book = new LMSBookImpl();%>

<liferay-ui:success key="add-success" message="added-successfully"/>

<liferay-ui:error key="add-errors" message="added-unsuccesfully" /><liferay-ui:error key="missing-title" message="missing-book-title" />

<aui:form name="fm" method="POST" action="<%=updateBookURL.toString() %>"><aui:input name="bookTitle" value="<%=book.getBookTitle() %>"/><aui:input name="bookId" type="hidden" value="<%=book.getBookId() %>"/><aui:input name="author" value="<%=book.getAuthor() %>"/><aui:input type="hidden" name="redirect"value="<%=redirectUrl%>" /><aui:button type="submit" value="Save"/></aui:form><a href="<portlet:renderURL/>">&laquo;<liferay-ui:message key="go-back"/></a>

Update.jsp là nơi vừa để thêm mới book cũng như sửa đổi bookVà cuối cung init.jsp<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%>

<%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet"%><%@ taglib uri="http://liferay.com/tld/util" prefix="liferay-util"%><%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme"%>

<%@ taglib uri="http://liferay.com/tld/security" prefix="liferay-security"%><%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %><%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet"%><%@page import="javax.portlet.PortletURL"%><%@page import="javax.portlet.ActionRequest"%><%@page import="vn.neo.training.library.model.impl.LMSBookImpl"%><%@page import="vn.neo.training.library.model.LMSBook"%><%@page import="com.liferay.portal.kernel.util.ParamUtil"%><%@page import="com.liferay.portal.kernel.util.ListUtil"%><%@page import="java.util.List"%><%@page import="vn.neo.training.library.service.LMSBookLocalServiceUtil"%><%@page import="com.liferay.portal.security.permission.ActionKeys"%><%@page import="com.liferay.portal.kernel.util.WebKeys"%><%@page import="com.liferay.portal.kernel.dao.search.ResultRow"%><portlet:defineObjects /> <!-- dùng để định nghĩa các biến dùng được ngay như session, request,... --><liferay-theme:defineObjects/> <!-- dùng để định nghĩa các biến dùng được ngay như theme, layout, permissionChecker -->

Thêm 1 chút css:

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Tạo file book/css/book.css có nội dung như sau:.search input[type="submit"] { text-align: center;font-size: 12px;text-decoration: none;

text-shadow: 1px 1px 0px #fff;display: block;text-align: center;float:right;padding: 6px 12px;height: 28px;cursor: pointer;min-width: 96px;width: auto;background: #ddedf3 url(../images/button-bg.jpg) repeat-x 0 0 scroll;box-shadow: 0 0 0 5px #eef5f7;-moz-box-shadow: 0 0 0 5px #eef5f7;-webkit-box-shadow: 0 0 0 5px #eef5f7;border: 1px solid #a4c5d0;font-weight: bold;color: #6b97a8;}.search select{

float: left;height: 28px;width: 150px;border: 1px solid #a4c5d0;

-webkit-box-shadow: 0 0 0 5px #eef5f7;-moz-box-shadow: 0 0 0 5px #eef5f7;

}Để file css này hoạt động được cần map nó tới portlet bằng cách:Open liferay-portlet.xml sửa đổi như sau:<?xml version="1.0"?><!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.1.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_1_0.dtd">

<liferay-portlet-app><portlet>

<portlet-name>book-portlet</portlet-name><icon>/icon.png</icon><instanceable>false</instanceable><header-portlet-css>/book/css/book.css</header-portlet-css>

<footer-portlet-javascript>/js/main.js</footer-portlet-javascript>

<css-class-wrapper>book-portlet</css-class-wrapper></portlet><role-mapper>

<role-name>administrator</role-name><role-link>Administrator</role-link>

</role-mapper>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<role-mapper><role-name>guest</role-name><role-link>Guest</role-link>

</role-mapper><role-mapper>

<role-name>power-user</role-name><role-link>Power User</role-link>

</role-mapper><role-mapper>

<role-name>user</role-name><role-link>User</role-link>

</role-mapper></liferay-portlet-app>

Bạn có thể thêm đúng thứ tự <header-portlet-css> hay <footer-portlet-javascript> trong liferay-portlet.xml mapping với các file css và js khác như đối với book.css, tất cả chúng sẽ hoạt động trong book-portlet.Chú ý: khi bạn submit 1 form thì không chỉ có 1 thông bào bạn gửi bằng SessionMessages hay SessionErrors được gửi tới trang client mà còn có các message mặc địnhc ủa liferay để xoa bỏ chúng khỏi ựng dụng của bạn bạn có thể thêm 1 biến init-param như sau vào portlet.xml<init-param> <name>add-process-action-success-action</name> <value>false</value> </init-param> Như vậy tới bây giờ bạn đã có thể viết được portlet khá hoàn chỉnh thực hiện được những nhiệm vụ cơ bản thêm sửa xóa, tìm kiếm, …

FriendlyUrl:Các đường link trong book portlet khá dài dong và tối nghĩa, liferay cung cấp 1 cách làm url đó thân thiện hơn bằng cách sau:Open liferay-portlet.xmlThêm đoạn mã sau ngay dưới icon tag<friendly-url-mapper-class>

com.liferay.portal.kernel.portlet.DefaultFriendlyURLMapper</friendly-url-mapper-class><friendly-url-mapping>

book_library</friendly-url-mapping><friendly-url-routes>

vn/neo/training/library/portlet/book-friendly-url-routes.xml

</friendly-url-routes>Tạo file vn.neo.training.library.portlet.book-friendly-url-routes.xml với thồng tin như sau:<?xml version="1.0"?><!DOCTYPE routes PUBLIC "-//Liferay//DTD Friendly URL Routes 6.1.0//EN" "http://www.liferay.com/dtd/liferay-friendly-url-routes_6_1_0.dtd">

<routes>

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

<route> <pattern>/{resourcePrimKey:\d+}/edit</pattern> <implicit-parameter name="p_p_lifecycle">0</implicit-parameter> <implicit-parameter name="jspPage">/book/jsp/update.jsp</implicit-parameter> </route>

<route> <pattern>/add</pattern> <implicit-parameter name="p_p_lifecycle">0</implicit-parameter> <implicit-parameter name="jspPage">/book/jsp/update.jsp</implicit-parameter> </route><route> <pattern>/back</pattern> <implicit-parameter name="p_p_lifecycle">0</implicit-parameter> <implicit-parameter name="p_p_state">normal</implicit-parameter> </route>

<route> <pattern>/view</pattern> <implicit-parameter name="p_p_lifecycle">0</implicit-parameter> <implicit-parameter name="p_p_state">maximized</implicit-parameter> <implicit-parameter name="jspPage">/book/jsp/view.jsp</implicit-parameter> </route> <route> <pattern>/re/view</pattern> <implicit-parameter name="p_p_lifecycle">0</implicit-parameter> <implicit-parameter name="p_p_state">normal</implicit-parameter> <implicit-parameter name="jspPage">/book/jsp/view.jsp</implicit-parameter> </route>

</routes>

Một chú ý khi viết các route thì nên viết route chứa nhiều thông tin hơn trước, route ít thông tin sau.Trong đoạn :/{resourcePrimKey:\d+}/edit thì resourcePrimKey là renderParameter và \d+ ám chỉ nó là giá trị số:Các tham số cận chú ý: p_p_lifecycle = 0 là render url, 1 là action Url

Save lại refresh lại trang Web và bạn sẽ nhận thấy sự thay đổi

Các vấn đề khác liên quan sử dung alloy ui, mong rằng các bạn tự tìm hiểu được thông qua nguồn tại liệu sau: https://github.com/liferay/alloy-ui/tree/masterBạn nên dùng alloy ui nhiều nhất của thể và nên hạn chế dùng các thư viện khác vì bản thân liferay hỗ trợ alloy ui rất tốt, và việc sử dụng các thư viện khác trong liferay khá dễ bị xung đột.

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2

Chương 4: theme layout

Mọi source code trong tài liệu này được viết dựa trên sdk 6.1.1.1 và deploy trên liferay 6.1.1.1 ga2