Bài viết mới:

Chủ Nhật, 22 tháng 1, 2017

Hướng dẫn tích hợp CKEditor và CKFinder trong ASP.NET


Ở bài viết này mình sẽ hướng dẫn bạn tích hợp CKEditor (gói FULL hoặc STANDARD) vào project ASP.NET sau đó hướng dẫn bạn tích hợp luôn CKFinder vào project để sử dụng thêm chức năng upload images, file ...


I. Tích hợp CKEditor vào project


Bước 1: Truy cập http://ckeditor.com/download chọn gói CKEditor bạn muốn (FULL hoặc STANDARD) và click Download. Ở bài viết này mình sẽ tải bản FULL luôn. Hai gói này hoàn toàn miễn phí nên bạn thoải mái download không lo sợ vấn đề gì nhé.

Bước 2: Giải nén file vừa tải về sau đó copy thư mục ckeditor và paste vào project của bạn. 

Copy thư mục
Paste vào project của bạn

Bước 3: Bạn cần tải thêm gói CKEditor for ASP.NET được tìm thấy bên dưới trang http://ckeditor.com/download



Bước 4: Giải nén file vừa tải và truy cập vào thư mục bin/Debug bạn sẽ thấy có 2 file CKEditor.NET.dll CKEditor.NET.pdb. Bây giờ bạn add references file CKEditor.NET.dll vào project theo các hình bên dưới

Click phải vào References và chọn Add Reference



Click Browse



Chọn file CKEditor.NET.dll và click Add

Bước 5: Mở trang web bạn muốn hiện CKEditor và khai báo ở đầu trang namespace CKEditor.NET để sử dụng CKEditor bằng lệnh <%@ Register Namespace="CKEditor.NET" Assembly="CKEditor.NET" TagPrefix="CKEditor" %>

Sau đó đặt lệnh <CKEditor:CKEditorControl ID="CKEditor1" Height="500" runat="server"></CKEditor:CKEditorControl> vào chỗ nào bạn muốn CKEditor hiện lên.

Phía dưới là hình minh hoạ cho trang DemoCKEditor.aspx của mình
 

Bước 6: Build lại project và chạy thử trang web của bạn để xem kết quả có hiện được CKEditor như mình không nhé.

II. Tích hợp CKFinder vào project


CKEditor đã có rất nhiều những tính năng hay để bạn soạn thảo văn bản như tô đen, in nghiêng, gạch chân, dán link, soạn theo mẫu sẵn có, tạo form, ... tuy nhiên nó còn thiếu chức năng rất quan trọng là upload image. 

Muốn sử dụng thêm chức năng upload image thì chúng ta phải tích hợp thêm CKFinder. Các bước làm như sau.

Bước 1: Tải bản CKFinder với version là 2.6.2.1 tại địa chỉ https://download.cksource.com/CKFinder/CKFinder%20for%20ASP.NET/2.6.2/ckfinder_aspnet_2.6.2.zip 

Bước 2: Giải nén file sau đó copy thư mục ckfinder và paste vào project của bạn.

Bước 3: Add reference file CKFinder.dll trong thư mục ckfinder/bin/Debug (cách làm như đối với phần I)

Bước 4: Mở file config.acsx trong thư mục ckfinder tại project của bạn. Bạn sẽ thấy có 2 method là CheckAuthentication() và SetConfig() trong file này (xem hình dưới)



Bước 5: Thay lệnh return false; thành return true; trong method CheckAuthentication()

Bước 6: Gán thuộc tính  LicenseName = "@tuannguyen"; LicenseKey = "AUKPSE6XSVSJTP4MSV9RQKJBKGLL3KN7"; hoặc bạn cũng có thể tự sinh ra CKFinder Keygen bằng cách truy cập vào link sau https://huytq.me/tools/ckfinder-keygen.html 

Bước này là thủ thuật đấy vì CKFinder phải mất phí đó nhé :v

Bước 7: Mặc định CKFinder sẽ upload các file vào thư mục /ckfinder/userfiles/ thông qua lệnh BaseUrl = "/ckfinder/userfiles/"; Nếu bạn muốn upload vào thư mục khác thì bạn sửa lại giá trị thuộc tính BaseUrl nhé.

Bước 8: Bây giờ chúng ta cùng gắn CKFinder vào trang web nhé. 

Đầu tiên là khai báo namespace CKFinder bằng lệnh sau <%@ Register Namespace="CKFinder" Assembly="CKFinder" TagPrefix="CKFinder" %>

Tiếp theo là dán lệnh  <CKFinder:FileBrowser ID="FileBrowser1"   Width="0" runat="server" OnLoad="FileBrowser1_Load"></CKFinder:FileBrowser> ở bất cứ đâu trong trang.

Bước 9: View code behind của trang lên và thêm method FileBrowser1_Load như hình:

        protected void FileBrowser1_Load(object sender, EventArgs e)
        {
       FileBrowser1 = new CKFinder.FileBrowser();
            FileBrowser1.BasePath = "/ckfinder/";
            FileBrowser1.SetupCKEditor(CKEditor1);
        }

Bước 10: Build project và tải lại trang web.

Vậy là đã hoàn thành thêm việc tích hợp CKFinder. Mình giới thiệu thêm về tính năng upload của CKFinder (hiện giờ nó đang nằm luôn trong CKEditor, cả 2 hoà hợp là 1 luôn ^^).

Để thêm hình vào bài viết thì bạn chọn icon Image trong CKEditor và chọn tab Tải lên sau đó chọn file hình ảnh muốn upload và click button Tải lên máy chủ.



Khi bạn click vào button Duyệt máy chủ ở tab đầu tiên thì bạn sẽ thấy danh sách các hình mà bạn đã upload lên máy chủ trước đó, bạn có thể chọn hình và dán vào bài viết luôn thay vì upload lại hình đó nữa.

Hình dưới cho thấy mình đã upload khá nhiều hình rồi và những hình này được chứa trong thư mục mà mình đã khai báo ở thuộc tính BaseUrl trong bước 6.



Bài viết đến đây là hết. Chúc bạn thành công. Mọi thắc mắc bạn comment bên dưới bài viết để mình hỗ trợ nhé.

Thứ Tư, 18 tháng 1, 2017

[Series] Hướng dẫn sử dụng ASP.NET Identity (phần cuối)

Ở bài viết này mình sẽ hướng dẫn bạn tạo trang quản lý role, tạo trang quản lý user và cuối cùng là phân quyền user thao tác trên controller và các action method.



Nội dung chính



1. Tạo trang quản lý Role


Chúng ta cần phải tạo trang để cho phép người quản trị có thể thêm, xóa, sửa các role (quyền).

Bước đầu tiên bạn tạo 1 area đặt tên bất kỳ theo ý muốn, trong bài viết này mình sẽ tạo 1 area là Admin. Sau đó tạo 2 empty controller là ManageRoleController, ManageUserController trong area này.


Tiếp theo là mở ManageRoleController lên, tiến hành khai báo biến context của class ApplicationDbContext và thay thế nội dung trong action method Index.


Lệnh context.Roles.AsEnumerable(); để lấy tất cả các roles hiện có trong bảng dbo.AspNetRoles ở CSDL, sau đó gán qua biến model và truyền qua view Index.

Tiếp theo tạo view Index.cshtml và thay thế nội dung trong view bằng đoạn lệnh sau:

@using Microsoft.AspNet.Identity.EntityFramework;

@model IEnumerable<IdentityRole>

@{

    ViewBag.Title = "Quản lý quyền hạn người dùng";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-sm-6">

        <h2>@ViewBag.Title</h2>

        <hr />

        @Html.ActionLink("Tạo role", "Create", null, new { @class = "btn btn-primary" })

        <br/><br/>

        <table class="table table-bordered">

            <thead>

                <tr>

                    <th>ID</th>

                    <th>Tên quyền</th>

                    <th>Thao tác</th>

                </tr>

            </thead>

            <tbody>

                @foreach (var item in Model)

                {

                    <tr>

                        <td>@item.Id</td>

                        <td>@item.Name</td>

                        <td>@Html.ActionLink("Xóa", "Delete", new { id = item.Id })</td>

                    </tr>

                }

            </tbody>

        </table>

    </div>

</div>


Vòng lặp foreach trong view để lặp qua biến model được truyền tự action Index qua và hiển thị thông tin mỗi role trên mỗi dòng trong thẻ table. Bạn cũng đừng quên khai báo 2 dòng đầu trong view nhé. Ngoài ra mình cũng thiết kế thêm 2 button là "Tạo role" và "Xóa" để thực hiện chức năng tạo mới role và xóa role được chọn.

Build lại project và truy cập vào đường dẫn http://localhost:52531/admin/managerole (port của bạn có thể khác đó nhé) thì bạn sẽ nhận được lỗi như sau:




Đừng hoảng sợ. Để khắc phục lỗi này bạn hãy mở file IdentityModels.cs trong thư mục Models và xóa đoạn lệnh public System.Data.Entity.DbSet<Demo_ASP.NET_Identity.Models.ApplicationUser> ApplicationUsers { get; set; }. trong class ApplicationDbContext. Chi tiết về nguyên nhân của lỗi thì bạn search google để tìm hiểu thêm nhé.

Sau khi thực hiện xong bạn build lại project và truy cập lại vào link trên thì nhận được kết quả như hình dưới:


Tất nhiên là table chưa hiển thị role nào vì bạn chưa hề tạo role nào cả. Bây giờ chúng ta cần tiến hành bổ sung chức năng tạo role.

Đầu tiên bạn cần khai báo thêm namespace Microsoft.AspNet.Identity.EntityFramework. Sau đó bổ sung thêm 2 action method Create trong ManageRoleController như sau:

        public ViewResult Create()

        {

            return View();

        }

        [HttpPost]

        [ValidateAntiForgeryToken]

        public ActionResult Create(IdentityRole role)

        {

            try

            {

                if (ModelState.IsValid)

                {

                    context.Roles.Add(role);

                    context.SaveChanges();

                }

                return RedirectToAction("Index");

            }

            catch(Exception ex)

            {

                ModelState.AddModelError("", ex.Message);

            }

            return View(role);

        }

Tiếp theo là add view Create và thay thế nội dung trong view thành như sau:

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

@{

    ViewBag.Title = "Tạo role";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-sm-6">

        <h2>@ViewBag.Title</h2>

        <hr />

        @using (Html.BeginForm())

        {

            @Html.AntiForgeryToken()

            @Html.ValidationSummary("", new { @class = "text-danger" })

            <div class="form-group">

                <label for="Id">Id</label>

                @Html.TextBoxFor(m => m.Id, new { id = "Id", @class = "form-control", required = "required" })

            </div>

            <div class="form-group">

                <label for="Name">Name</label>

                @Html.TextBoxFor(m => m.Name, new { id = "Name", @class = "form-control", required = "required" })

            </div>

            <div class="form-group">

                <button type="submit" class="btn btn-primary">Tạo role</button>

            </div>

        }

    </div>

</div>

Build project và click vào button "Tạo role" để đến trang Tạo role và tiến hành tạo role. Ở bài viết này mình sẽ ví dụ bằng cách tạo 1 role là Admin. Mình nhập Id là Ad và Name là Admin. 

Chú ý: Id là khóa chính của bảng dbo.AspNetRoles nên khi tạo role bạn không được nhập Id trùng nhau.


Sau đó mình click "Tạo role" thì được kết quả là tạo thành công 1 role Admin như hình dưới




Bây giờ tiến hành nốt chức năng còn lại là xóa Role nhé. Bạn bổ sung thêm 2 action method Delete DeleteConfirmed vào ManageRoleController.

        public ActionResult Delete(string Id)

        {

            var model = context.Roles.Find(Id);

            return View(model);

        }

     

        [HttpPost]

        [ValidateAntiForgeryToken]

        [ActionName("Delete")]

        public ActionResult DeleteConfirmed(string Id)

        {

            IdentityRole model = null;

            try

            {

                model = context.Roles.Find(Id);

                context.Roles.Remove(model);

                context.SaveChanges();

                return RedirectToAction("Index");

            }

            catch(Exception ex)

            {

                ModelState.AddModelError("", ex.Message);

            }

            return View(model);

        }

Và không quên tạo view Delete cho action. Dưới đây là nội dung trong view Delete:

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole

@{

    ViewBag.Title = "Xóa role";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-md-12 ">

        <h2>@ViewBag.Title</h2>

        <hr />

        <div class="text-center">

            <h5>Bạn có chắc muốn xóa role @Model.Name ? </h5>

            @using (Html.BeginForm())

            {

                @Html.AntiForgeryToken()

                @Html.ValidationSummary("", new { @class="text-danger"})

                <button type="submit" class="btn btn-primary">Chấp nhận</button>

                <a href="@Url.Action("Index")" class="btn btn-default">Không xóa</a>

            }

        </div>

    </div>

</div>

Nội dung trong view Delete hiển thị câu hỏi để xác nhận xem người quản trị có chắc muốn xóa role được chọn không, đi kèm là nút submit "Chấp nhận" để chấp nhận xóa và nút "Không xóa" để điều hướng về trang Index nếu người dùng không muốn xóa nữa.

Bây giờ bạn build project và thử tạo mới role ABC và xóa luôn role ABC vừa tạo xem OK ko nhé.

Vậy là chúng ta đã cài đặt xong chức năng quản lý role yes.

2. Tạo trang quản lý User


Có quản lý role thì cũng phải tất nhiên cũng phải có quản lý user đúng chứ. Bây giờ chúng ta cần tạo trang để hiển thị toàn bộ user trong website. Đầu tiên bạn mở ManageUserController lên và thay thế nội dung trong controller thành như sau:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using Demo_ASP.NET_Identity.Models;   // Sửa lại cho đúng với namespace của project bạn

using Microsoft.AspNet.Identity.EntityFramework;

namespace Demo_ASP.NET_Identity.Areas.Admin.Controllers

{

    [Authorize(Users = "nguyenaituan@yahoo.com")]

    public class ManageUserController : Controller

    {

        ApplicationDbContext context = new ApplicationDbContext();

        // GET: Admin/ManageUser

        public ActionResult Index()

        {

            IEnumerable<ApplicationUser> model = context.Users.AsEnumerable();

            return View(model);

        }

        public ActionResult Edit(string Id)

        {

            ApplicationUser model = context.Users.Find(Id);

            return View(model);

        }

        [HttpPost]

        [ValidateAntiForgeryToken]

        public ActionResult Edit(ApplicationUser model)

        {

            try

            {

                    context.Entry(model).State = System.Data.Entity.EntityState.Modified;

                    context.SaveChanges();

                    return RedirectToAction("Index");

            }

            catch (Exception ex)

            {

                ModelState.AddModelError("", ex.Message);

                return View(model);

            }

        }

        public ActionResult EditRole(string Id)

        {

            ApplicationUser model = context.Users.Find(Id);

            ViewBag.RoleId = new SelectList(context.Roles.ToList().Where(item=> model.Roles.FirstOrDefault(r=>r.RoleId == item.Id) == null).ToList(), "Id", "Name");

            return View(model);

        }

        [HttpPost]

        [ValidateAntiForgeryToken]

        public ActionResult AddToRole(string UserId, string[] RoleId)

        {

            ApplicationUser model = context.Users.Find(UserId);

            if (RoleId != null && RoleId.Count() > 0)

            {

                foreach (string item in RoleId)

                {

                    IdentityRole role = context.Roles.Find(RoleId);

                    model.Roles.Add(new IdentityUserRole() { UserId = UserId, RoleId = item });

                }

                context.SaveChanges();

            }

            ViewBag.RoleId = new SelectList(context.Roles.ToList().Where(item => model.Roles.FirstOrDefault(r => r.RoleId == item.Id) == null).ToList(), "Id", "Name");

            return RedirectToAction("EditRole", new { Id = UserId });

        }

        [HttpPost]

        [ValidateAntiForgeryToken]

        public ActionResult DeleteRoleFromUser(string UserId, string RoleId)

        {

            ApplicationUser model = context.Users.Find(UserId);

            model.Roles.Remove(model.Roles.Single(m => m.RoleId == RoleId));

            context.SaveChanges();

            ViewBag.RoleId = new SelectList(context.Roles.ToList().Where(item => model.Roles.FirstOrDefault(r => r.RoleId == item.Id) == null).ToList(), "Id", "Name");

            return RedirectToAction("EditRole", new { Id = UserId });

        }

        public ActionResult Delete(string Id)

        {

            var model = context.Users.Find(Id);

            return View(model);

        }

        [HttpPost]

        [ValidateAntiForgeryToken]

        [ActionName("Delete")]

        public ActionResult DeleteConfirmed(string Id)

        {

            ApplicationUser model = null;

            try

            {

                model = context.Users.Find(Id);

                context.Users.Remove(model);

                context.SaveChanges();

                return RedirectToAction("Index");

            }

            catch (Exception ex)

            {

                ModelState.AddModelError("", ex.Message);

                return View("Delete", model);

            }

        }

    }

}

Về chức năng quản lý User thì mình thấy không cần thiết phải có chức năng tạo User vì đã có chức năng đăng ký thành viên rồi nên bạn sẽ không thấy action method Create nhé, còn nếu muốn thì bạn có thể tự bổ sung luôn cũng OK. Trong controller ngoài các action method Index, Edit, Delete đã quen thuộc với bạn thì bạn sẽ thấy mình đã bổ sung thêm 3 action method khác là EditRole, AddToRole, DeleteRoleFromUser. Mình giải thích xíu về chức năng của 3 action này như sau:

  • EditRole: đây là action method dạng Get để hiển thị view cho người quản trị có thể sửa role (thêm role vào hoặc bỏ bớt role) của 1 user nào đó. Bạn lưu ý là trong ASP.NET Identity thì 1 user có thể có nhiều role (bảng dbo.AspNetUserRoles cho thấy điều đó)
  • AddToRole: đây là action method dạng Post xử lý khi người quản trị submit form, form AddToRole mình sẽ thiết kế nó nằm luôn trong view EditRole.
  • DeleteRoleFromUser: đây là action method dạng Post xử lý xóa 1 role khỏi 1 user bất kỳ, form DeleteRoleFromUser mình sẽ thiết kế nó nằm luôn trong view EditRole.

Dưới đây là code view và hình ảnh của các view trong ManageUserController:

1. Index.cshtml

@using Demo_ASP.NET_Identity.Models;

@model IEnumerable<ApplicationUser>

@{

    ViewBag.Title = "Quản lý thành viên";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-sm-12">

        <h2>@ViewBag.Title</h2>

        <hr />

        <table class="table table-borderer">

            <thead>

                <tr>

                    <th>Họ tên</th>

                    <th>Email</th>

                    <th>Giới tính</th>

                    <th>Địa chỉ</th>

                    <th></th>

                </tr>

            </thead>

            <tbody>

                @foreach (var item in Model)

                {

                    <tr>

                        <td>@item.FullName</td>

                        <td>@item.Email</td>

                        <td>@(item.Gender == true ? "Nam" : "Nữ")</td>

                        <td>@item.Address</td>

                        <td>

                            @Html.ActionLink("Sửa thông tin", "Edit", new { Id = item.Id }, null) |

                            @Html.ActionLink("Sửa quyền", "EditRole", new { Id = item.Id }, null) |

                            @Html.ActionLink("Xóa", "Delete", new { Id = item.Id }, null)

                        </td>

                    </tr>

                }

            </tbody>

        </table>

    </div>

</div>


View Index hiển thị danh sách user. Ở đây mình hiển thị các thông tin họ tên, email, giới tính, địa chỉ còn bạn có thể bổ sung thêm thông tin hiển thị ra view Index tùy thuộc vào bảng dbo.AspNetUsers của bạn có bao nhiêu cột thông tin. Bên cạnh các thông tin của user thì mình còn tạo thêm 3 liên kết "Sửa thông tin", "Sửa quyền", "Xóa".

2. Edit.cshtml

@model Demo_ASP.NET_Identity.Models.ApplicationUser

@{

    ViewBag.Title = "Sửa thông tin";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-sm-8">

        <h2>@ViewBag.Title</h2>

        <hr/>

        @using (Html.BeginForm())

        {

            @Html.ValidationSummary("",new { @class="text-danger"})

            @Html.AntiForgeryToken()

         

            @Html.HiddenFor(m => m.UserName)

            @Html.HiddenFor(m => m.Id)

            @Html.HiddenFor(m=>m.Email)

            @Html.HiddenFor(m=>m.AccessFailedCount)

            @Html.HiddenFor(m => m.Claims)

            @Html.HiddenFor(m => m.EmailConfirmed)

            @Html.HiddenFor(m => m.LockoutEnabled)

            @Html.HiddenFor(m => m.LockoutEndDateUtc)

            @Html.HiddenFor(m => m.PasswordHash)

            @Html.HiddenFor(m => m.PhoneNumber)

            @Html.HiddenFor(m => m.PhoneNumberConfirmed)

            @Html.HiddenFor(m => m.SecurityStamp)

            @Html.HiddenFor(m => m.TwoFactorEnabled)

            <div class="form-group">

                <label>Họ tên:</label>

                @Html.TextBoxFor(m=>m.FullName, new { @class="form-control" ,required="required"})

            </div>

            <div class="form-group">

                <label>Địa chỉ:</label>

                @Html.TextBoxFor(m => m.Address, new { @class = "form-control", required = "required" })

            </div>

            <div class="form-group">

                <label>Giới tính:</label>

                 Nam @Html.CheckBox("Gender", Model.Gender == true ? true : false)

            </div>

            <div class="form-group">

                <button type="submit" class="btn btn-primary">Lưu</button>

            </div>

        }

    </div>

</div>


View Edit để quản trị viên chỉnh sửa thông tin thành viên.  Các bạn sẽ thấy trong form có 1 loạt các lệnh Html.HiddenFor, đó là để tạo ra các thẻ input có type="hidden" nhằm giữ lại giá trị các cột của user mà quản trị viên không có nhu cầu chỉnh sửa.

3. Delete.cshtml

@model Demo_ASP.NET_Identity.Models.ApplicationUser

@{

    ViewBag.Title = "Xóa thành viên";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-sm-12">

        <h2>@ViewBag.Title</h2>

        <hr/>

        <div class="text-center">

            <h5>Bạn có chắc muốn xóa thành viên @Model.Email?</h5>

            @using (Html.BeginForm())

            {

                @Html.AntiForgeryToken()

                @Html.ValidationSummary("",new { @class="text-danger"})

                <button type="submit" class="btn btn-primary">Chấp nhận</button>

                <a href="@Url.Action("Index")" class="btn btn-default">Không xóa</a>

            }

        </div>

    </div>

</div>




Nội dung trong view này cũng đơn giản là chứa câu hỏi xác thực xóa và 1 form chứa button submit và thẻ a để redirect về trang chủ nếu người quản trị không muốn xóa.

4. EditRole.cshtml

@model Demo_ASP.NET_Identity.Models.ApplicationUser

@{

    ViewBag.Title = "Sửa quyền";

    Layout = "~/Views/Shared/_Layout.cshtml";

}

<div class="row">

    <div class="col-sm-6">

        <h2>@ViewBag.Title</h2>

        <hr />

        <h5><strong>Các quyền hiện tại của thành viên @Model.Email:</strong> </h5>

        <table class="table">

            <thead>

                <tr>

                    <th>Quyền</th>

                    <th></th>

                </tr>

            </thead>

            <tbody>

                @if (Model.Roles.Count > 0)

                {

                foreach (var item in Model.Roles)

                {

                <tr>

                    <td>@item.RoleId</td>

                    <td>

                        <form action="@Url.Action("DeleteRoleFromUser", new { UserId = item.UserId, RoleId = item.RoleId })" method="post">

                            @Html.AntiForgeryToken()

                            <button type="submit" class="btn btn-xs btn-danger">Gỡ bỏ</button>

                        </form>

                    </td>

                </tr>

                    }

                }

                else

                {

                    <tr>

                        <td colspan="2">

                            <p>Thành viên chưa có quyền nào</p>

                        </td>

                    </tr>

                }

            </tbody>

        </table>

        <h5><strong>Thêm quyền cho thành viên này:</strong></h5>

        <form action="@Url.Action("AddToRole")" method="post">

            <input type="hidden" name="UserId" value="@Url.RequestContext.RouteData.Values["Id"]" />

            @Html.AntiForgeryToken()

            @Html.ListBox("RoleId", null, new { @class = "form-control", multiple = "multiple" })

            <div class="form-group">

                <br />

                <button type="submit" class="btn btn-primary">Thêm </button>

            </div>

        </form>

    </div>

</div>

3 view kia có thể rất quen thuộc với bạn nếu bạn đã có kiến thức căn bản về ASP.NET MVC. Tuy nhiên view EditRole này là khá mới lạ đòi hỏi chúng ta phải cùng nghiễn ngẫm xíu về nội dung trong view.

Đầu tiên view hiển thị 1 table chứa danh sách các role hiện có của user. Vòng lặp foreach (var item in Model.Roles) sẽ lặp qua các record trong bảng dbo.AspNetUserRoles và hiển thị các role mà user có. Nếu user không có role nào thì sẽ in ra Thành viên chưa có quyền nào. Mỗi dòng ở table sẽ hiển thị tên role và 1 form method post gọi đến action DeleteRoleFromUser để khi quản trị click vào button Gỡ bỏ thì xóa role này khỏi thành viên đó.

Bên dưới table là 1 form method post gọi đến action method AddToRole để thêm các role mà user chưa có vào user. Lệnh @Html.ListBox("RoleId", null, new { @class = "form-control", multiple = "multiple" }) sẽ hiển thị một Listbox nhận giá trị từ ViewBag.RoleId từ action EditRole truyền vào để hiển thị các role mà user chưa có. Các bạn cũng chú ý là ListBox này cho phép chúng ta chọn nhiều item một lúc vì mình đã bổ sung attribute multiple.

Đây là hình ảnh của view EditRole với user được chọn là nguyenaituan@yahoo.com


Mình thử chọn item Admin và click nút Thêm user nguyenaituan@yahoo.com sẽ có được quyền Admin


Giờ thử xóa role Admin khỏi user nguyenaituan@yahoo.com bằng cách click button Gỡ bỏ.


Hình ảnh trên cho thấy chức năng gỡ bỏ 1 role bất kỳ khỏi user cũng thành công tốt đẹp.

Vậy là chúng ta đã hoàn thành tất cả các chức năng cần thiết trong ManageUserController.

3. Phân quyền User


Để phân quyền 1 user hay 1 role nào đó được phép truy xuất đến 1 action method hay cả 1 controller bất kỳ thì chúng ta sẽ sử dụng attribute Authorize trong ASP.NET MVC. Như các bạn thấy việc quản lý role và user thì chỉ có những user có quyền là Admin mới được phép thực hiện đúng không nào. Để cấu hình được điều này thì ta làm như sau:

Mở ManageRoleController và ManageUserController lên và thêm attribute Authorize trên đầu khai báo controller như hình:



Trong attribute Authorize chúng ta có thể truyền vào những Roles hoặc danh sách những Users được phép truy cập vào Controller. Nếu bạn viết như hình dưới thì chỉ có user nguyenaituan@yahoo.com mới được phép truy cập.


Nếu bạn không truyền tham số gì cả thì chỉ những user đã login mới được truy cập. Còn nếu có tham số Roles hay Users hoặc kết hợp cả 2 thì phải thỏa 2 điều kiện sau:

  1. Đã đăng nhập vào website
  2. Thỏa mãn ràng buộc về tham số trong attribute

Sau này giả sử bạn tạo một controller là ManageProductController chứa các action method như Index, Create, Edit, Detail, Delete. Bạn chia 2 phân hệ là Admin và Mod và bạn muốn rằng Admin có toàn quyền thao tác trên controller và Mod thì chỉ được phép truy cập vào action method Index, Create để xem danh sách sản phẩm và tạo mới sản phẩm thì bạn không đặt attribute Authorize lên đầu controller mà phải đặt lên từng action method trong controller và xác định các action nào admin được truy cập, mod được truy cập. Hình dưới minh họa cho ví dụ mình nêu ra.


Nếu không thỏa điều kiện ràng buộc mà truy cập vào thì người dùng sẽ được redirect về trang login. Giả sử mình là 1 khách thăm website chưa đăng nhập và khi mình truy cập đến URL http://localhost:52531/admin/manageproduct thì mình sẽ được chuyển đến trang Login hoặc khi mình đã login nhưng mình chỉ là member hoặc mod mà mình lại truy cập vào URL http://localhost:52531/admin/manageproduct/edit thì mình cũng sẽ bị yêu cầu login. Bạn thử test xem phải vậy không nhé :)

4. Tổng kết


Qua bài viết này thì các bạn có thể hiểu được khái niệm user và role trong ASP.NET Identity, biết cách tạo các trang để quản lý role, user và cuối cùng là sử dụng attribute Authorize để phân quyền user. Đây cũng là bài viết cuối cùng trong series Hướng dẫn sử dụng ASP.NET Identity. Chúc các bạn thực hành thành công và ăn tết vui vẻ. laugh

Thứ Hai, 16 tháng 1, 2017

Chia sẻ một số bài tập OOP C++ và C# WinForm

Nếu bạn đang cần một số bài tập OOP C++ và C# WinForm để thực hành thì có thể tải ở các link bên dưới. Đây là các bài tập mình đã giữ lại trong quá trình học ở trường. 

Files bài tập OOP C++

Link tải BT OOP C++: http://adf.ly/1i9UUe

Một file bài tập C# Winform


Link tải BT C# Winform: http://adf.ly/1i9Uk6 (mật khẩu đọc file: khongbiet)

Chúc bạn buổi sáng thứ 2 vui vẻ.

Chủ Nhật, 15 tháng 1, 2017

Tính dung lượng Folder bằng Java


I. Ý tưởng bài toán


Trong bài viết này, mình sẽ hướng dẫn các bạn tính dung lượng của 1 Folder sử dụng một số Collection trong Java.

Về ý tưởng bài toán, đầu tiên mình sẽ tạo 1 Queue để chứa tất cả các Files có trong Thư mục cần tính toán. Chúng ta sẽ lấy lần lượt các file có trong Queue ra và kiểm tra xem nó có phải là Folder hay không (vì trong Folder có thể chứa các folder con). Tại đây sẽ có hai trường hợp xảy ra:

- Trường hợp 1: file vừa lấy ra là 1 Folder. Chúng ta sẽ lại lấy tất cả các file có trong Folder ấy ra và Add vào queue.

- Trường hợp 2: file lấy ra không phải là 1 Folder. Chúng ta đã có thể tính được dung lượng của file đó.

Chú ý: Khi lấy được 1 file ra thì xóa luôn file trong Queue đó đi bằng phương thức poll().

Ở đây mình sử dụng Queue thay vì Stack vì cơ chế FIFO (First In First Out), khi lấy file thì lấy ở đầu Queue và add lại ở cuối Queue.

II. Hướng dẫn


- Tạo một Queue để chứa các File và tạo 1 đường dẫn đến Folder.

Queue<File> qe = new LinkedList<>();
File f = new File("./folder");

- Lấy tất cả các thư mục con trong Folder ở trên cho vào mảng File[ ], sau đó add mảng File [ ] đó vào Queue. Như vậy chúng ta đã có 1 Queue chứa Folder cần tính.

        File[] fileArr = f.listFiles();
        qe.addAll(Arrays.asList(fileArr));
        double FolderSize = 0;

- Vòng lặp while sau đây sẽ dừng lại khi Queue rỗng (nghĩa là các file đã được lấy ra hết để tính size).

        while (!qe.isEmpty()) {
            File file = qe.poll();
            if (file.isDirectory()) {
                qe.addAll(Arrays.asList(file.listFiles()));
            } else {
                FolderSize += file.length();
            }
        }

- Và cuối cùng in kết quả (sẽ là dùng lượng của Folder đó tính theo bytes):

         System.out.println(FolderSize + " bytes");

Kết quả sẽ như sau:

Học lập trình trực tuyến tại website Code School

codeschool.com


Mình muốn chia sẻ đến bạn một website học lập trình trực tuyến rất hay mà mình vừa phát hiện ra. Hình như website này ra đời mới đây thôi ^^. Đây là một website mình rất thích ngay khi vừa tìm đến. Đó là Code School, ở Code School có nhiều khóa học (có cả free và thu phí). Mình đã học thử khóa học miễn phí ở đây tên là Try ASP.NET Core. Nhìn chung mình thấy chất lượng video rất tốt (họ còn sáng tác ra bài hát về khóa học nữa mới ghê :)) ) ngoài ra bạn có thể tải slide PDF của khóa học về máy (click vào đây để xem thử slide khóa học của mình) và làm các phần bài tập thử thách cuối mỗi video trong khóa học. Mình tính sẽ đăng ký thêm các khóa có phí bên họ nữa đấy. Bạn có thể tìm hiểu chi tiết tại trang chủ của Code Shool tại đây . Chúc bạn học vui.

Thứ Bảy, 14 tháng 1, 2017

Danh sách các ngôn ngữ lập trình tốt nhất để học trong năm 2017


Ngôn ngữ lập trình... Ôi! Có rất nhiều (phải trên 300 ngôn ngữ lập trình tất cả đấy) !! Và thực sự khó để tìm ra ngôn ngữ bạn nên tìm hiểu. Bạn có thể chọn C hoặc chọn 1 ngôn ngữ khác như Python hoặc cũng có thể là Java và Ruby. Vâng, rất khó khăn trong việc quyết định chọn ngôn ngữ nào và bỏ qua ngôn ngữ nào bởi vì tất cả ngôn ngữ đều có thể mạnh riêng của nó. 

Một số ngôn ngữ lập trình tốt nhất để học


Chúng tôi đã biên soạn một danh sách các ngôn ngữ lập trình tốt nhất bạn có thể học dựa theo mức lương trung bình và cộng đồng hỗ trợ. Có lẽ sau khi đọc bài viết này, bạn có thể quyết định dễ dàng hơn việc chọn một ngôn ngữ lập trình để học.

Scala


Nếu bạn muốn học một loại ngôn ngữ mà hỗ trợ tốt cả hai khái niệm hướng chức năng và hướng đối tượng, thì đó là Scala. Scala chạy trên nền máy ảo Java và nó tương thích hoàn toàn với Java. Ngoài ra nó cũng có thể chạy trên .NET, tuy nhiên chưa ổn định. Các tính năng khác bao gồm hỗ trợ cho dữ liệu lớn và REPL. Nhược điểm là trình biên dịch của nó là khá nặng tương đương với một bộ xử lý Pentium 5. Ngôn ngữ Scala được sử dụng trong nhiều lĩnh vực , qui mô từ những đoạn script nhỏ cho đến những hệ thống lớn.

Mức lương trung bình: $112.000

Java


Viết và chạy ở bất cứ ở hệ điều hành nào - đó là sự kỳ diệu của Java, một điểm khá hấp dẫn cho các nhà phát triển. Nó là một ngôn ngữ giúp bạn hoàn thành mọi nhu cầu, cho dù bạn đang viết code một ứng dụng hoặc có kế hoạch để trở thành một nhà khoa học dữ liệu (a data scientist) hoặc một nhà phát triển ứng dụng di động, Java có thể hỗ trợ đắc lực cho bạn. Kể từ khi một phần lớn của hệ điều hành Android chạy bằng Java, điều đó đã cho thấy tầm quan trọng của Java trong việc phát triến ứng dụng chạy trên Android.

Tất nhiên đầu tiên bạn phải học lập trình Java căn bản. Sau đó, bạn có thể bắt đầu học SDK Android và tập trung vào phát triển ứng dụng một cách đầy đủ.

Java đã ra đời trong một thời gian dài và đã cung cấp một nền tảng cho nhiều trang web và phần mềm cấu trúc. Nó cũng có thể được sử dụng bởi các nhà khoa học dữ liệu, nhưng nó chưa hỗ trợ tốt bằng R và Python trong việc này.

Java có gần 9 triệu nhà phát triển, do đó bạn luôn có một cộng đồng hỗ trợ rất mạnh.

Mức lương trung bình: $102.000

Python


Python là một ngôn ngữ lập trình thông dịch do Guido van Rossum tạo ra năm 1990. Python khá đơn giản để học và nó cũng có các khái niệm lập trình hướng đối tượng thông thường. R không thích hợp để trở thành một công cụ toàn diện cho các nhà khoa học dữ liệu. Python lấp đầy những khoảng trống đó.

Đương nhiên, Python là một trong những ngôn ngữ tốt nhất bạn nên học nếu bạn đang có kế hoạch tìm hiểu về lĩnh vực phân tích dữ liệu, khoa học dữ liệu hoặc dữ liệu lớn. Python là ngôn ngữ có hình thức rất sáng sủa, cấu trúc rõ ràng, thuận tiện cho người mới học lập trình.

Sự hỗ trợ của cộng đồng đã phát triển trong những năm qua và bây giờ nó là một cái tên lớn trong cộng đồng lập trình. Một nhà khoa học dữ liệu nên hiểu biết sâu về Python. 

Mức lương trung bình: $102.000
Tài nguyên: Best python Courses from beginner to expert

R


R là một ngôn ngữ lập trình và môi trường phần mềm dành cho tính toán và đồ họa thống kê. Ra đời từ năm 1997 và là một thay thế tuyệt vời cho các công cụ nặng như Matlab và SAS. Các nhà thống kê rất thích R và nó rất hữu ích trong các doanh nghiệp.

R có thể rất khó khăn cho người mới học nhưng nếu bạn chịu khó bỏ nhiều thời gian để tìm hiểu nó thì nó sẽ đem lại cho bạn nhiều thứ hay ho. R thao tác dữ liệu và xử lý những dữ liệu phức tạp mạnh mẽ đến kinh ngạc. Nó có cộng đồng hỗ trợ sôi động và các tính năng mới được bổ sung liên tục. Nó là một ngôn ngữ rất phổ biến và được nhiều nhà khoa học dữ liệu sử dụng.

Mức lương trung bình: $62.000

Swift


Swift là một ngôn ngữ lập trình được phát triển bởi Apple. Trước đó, Apple chỉ làm việc xoay quanh Objective C. Nhưng trong một nỗ lực để làm cho mọi việc dễ dàng hơn cho các nhà phát triển, Apple phát hành Swift, đó là ngôn ngữ lập trình rất riêng.

Tại sao bạn nên học Swift? Câu trả lời rất đơn giản, nếu bạn thấy mình là một nhà phát triển ứng dụng iOS, bạn phải học Swift. Những lỗ hổng trong Objective-C đã được giải quyết bởi Swift, vì vậy bạn có thể thấy nó tương đối sạch sẽ, nhanh chóng và không bị lỗi. Nó cũng có thể làm giảm chiều dài của mã của bạn, giúp bạn tiết kiệm thời gian và năng lượng. Nó cũng có thể làm giảm chiều dài mã của bạn, giúp bạn tiết kiệm thời gian và công sức.

Hơn nữa, nó là mã nguồn mở, vì vậy các nhà phát triển cũng có thể phát triển Swift trên các hệ điều hành Windows hoặc Linux, thiết kế trình biên dịch của họ và đảm bảo rằng các ứng dụng của họ tương thích với các thiết bị của Apple.

Mức lương trung bình: $112.000

Ruby On Rails


Ruby là một ngôn ngữ yêu thích của các nhà phát triển (developers), khởi nghiệp (startups), và các doanh nghiệp mới thành lập. Mặc dù danh tiếng của nó đã suy giảm một vài năm trở lại, RoR đã bắt đầu lấy lại sự nổi tiếng của nó. Ruby đã phần lớn cải thiện được phần lớn framework, đã mang lại sự nhanh nhẹn, hỗ trợ phương pháp module cho việc phát triển các ứng dụng mới. Nó có cộng đồng hỗ trợ mạnh và bạn có thể mong đợi những cải tiến liên tục trong mã và hỗ trợ rất lớn.

Mức lương trung bình: $103.000

NoSQL Databases


Trong lĩnh vực phân tích dữ liệu lớn, cơ sở dữ liệu (big data), NoSQL là một thứ mà bạn không thể bỏ lỡ. Nếu bạn đang tìm kiếm một cơ hội trong lĩnh vực Big Data, bạn nên xem xét việc học các cơ sở dữ liệu NoSQL sau.
  1. MongoDB
  2. Cassandra
Bạn có thể xem danh sách các cơ sở dữ liệu NoSQL tại đây

Mức lương trung bình: $105.000

SQL


Ngôn ngữ có thể thay đổi, nhưng các yêu cầu về cơ sở dữ liệu sẽ không bao giờ thay đổi. Các doanh nghiệp đang phụ thuộc vào công nghệ và các cơ sở dữ liệu SQL như MySQL, Microsoft SQL Server, Oracle được sử dụng rất rộng rãi. Nhu cầu tuyển dụng luôn luôn rất cao, không có nghi ngờ rằng tiền lương cho các chuyên gia cơ sở dữ liệu là rất cao.

Mức lương trung bình: $92.000

Một số ngôn ngữ đáng xem xét


Có một số ngôn ngữ khác mà có thể không được nổi tiếng và có sự hỗ trợ của cộng đồng ít hơn. Nhưng những ngôn ngữ này đang dần dần cho thấy tầm quan trọng của nó.

Julia


Julia là một thành viên mới (newbie) trong các ngôn ngữ lập trình và có khả năng vượt qua R và Python. Nó sẽ cần một thời gian nữa trước khi nó có thể cạnh tranh với các ngôn ngữ lão luyện khác. Các nhà khoa học dữ liệu phải cho rằng Julia là một ngôn ngữ phát triển nhanh và có khả năng cách mạng hóa dữ liệu siêu tốc.

GoLang (Go)


Go là một ngôn ngữ lập trình của Google, được dựa trên C. Nó đã được sử dụng rất phổ biến trong năm 2016 và có thể tăng vọt trong năm 2017. Với  lương trung bình là 117.000 $, Go có thể là một lựa chọn thích hợp cho các nhà phát triển.

Javascript


Javascript đã thay đổi từ ngôn ngữ giao diện người dùng đơn giản và trở thành ngôn ngữ hỗ trợ mạnh mẽ lập trình backend bằng việc sử dụng NodeJs. Có rất nhiều các ngôn ngữ được dẫn xuất từ javascript mà bạn nên xem xét. Sau đây là danh sách:
  1. NodeJS
  2. Backbone JS
  3. Angular Js
  4. React JS.
Bạn có thể xem danh sách đầy đủ các thư viện của javascript tại đây

Theo DEVOPSCUBE

Thứ Năm, 12 tháng 1, 2017

Share code javascript playmusic chơi nhạc với lời bài hát và hình ảnh cực cool

Sau bài viết "Share source web TShop chuyên bán Laptop, điện thoại viết bằng PHP" thì hôm nay mình sẽ giới thiệu đến bạn thư viện javascript "playmusic" để chơi nhạc kèm lời bài hát cực cool.  Cái này tặng người ấy thì khỏi phải bàn. Nếu share tặng người ấy để tỏ tình mà không thành công thì liên hệ mình tỏ tình dùm cho nhé. Đặc biệt source code có thể dễ dàng cấu hình để mở những bài hát mà bạn thích.




Dưới đây là các link demo trực tiếp để bạn có thể xem trước:
  1. Những năm tháng ấy - Hồ Hạ (có vietsub, chạy slide hình và đổi màu lyric)
  2. Phía sau một cô gái - Soobin Hoàng Sơn (tắt chức năng vietsub (làm gì cần đâu ^^), tắt chạy slide và tắt đổi màu lyric)
  3. Anh không xứng - Jay Chou 
3 link trên là ví dụ cho thấy việc tủy chỉnh chức năng, giao diện đa dạng rất dễ dàng mà mình đã cố gắng code để nếu bạn bạn không rành HTML, CSS, Javascript vẫn có thể hiểu và tự chỉnh.

Sau đây là các bước hướng dẫn chi tiết cách chỉnh sửa bài hát, giao diện.

Bước 1: Tải source code demo về tại link sau https://drive.google.com/file/d/0BzQoiVv05yaIaGFMSmhjRGFKNG8/view?usp=sharing

Bước 2: Giải nén ra thì các bạn sẽ thấy 2 file index.html phiasaumotcogai.html và 4 thư mục css, images, js, music như hình dưới (còn cái file debug.txt bạn xóa đi mình upload mà quên xóa thôi :)) )

Mở file index.html hoặc phiasaumotcogai.html bằng 1 Editor bất kỳ như Notepad, Notepad++, Visual Studio Code, ... để xem thử code bên trong file. Bài viết này mình sẽ mở file index.html để giới thiệu vì file này đã bật đầy đủ thêm 3 chức năng là vietsub, chạy slile hình và đổi màu nền lyric vì thế dễ dàng để mình hướng dẫn bạn.

Bước 3:  Code bên trong file index.html nhìn khá dài nhỉ nhưng đừng lo lắng quá vì dài là do lời bài hát của file này dài thôi (bài hát tận 6 phút 11 giây mà @@).

Trong file này bạn cần sửa lại 1 số các giá trị là file nhạc, danh sách hình ảnh, nội dung hiển thị, danh sách lời bài hát, thời gian của bài hát và bật hoặc tắt 1 số chức năng.
Đầu tiên là sửa file nhạc. Bạn tìm đến thẻ audio như bên dưới

 <audio  autoplay="autoplay">
        <source src="music/nhungnamthangay.mp3" type="audio/mp3">    <!-- Chèn đường dẫn nhạc vào -->
    </audio>

Các bạn thay thế src chỉ đến đường dẫn bài nhạc mà bạn chọn nhé.

Tiếp theo là sửa danh sách hình ảnh. Phía dưới thẻ audio bạn sẽ thấy danh sách các thẻ img. Đây chính là thẻ để hiển thị các slide hình khi bài hát đang mở.

<!-- Danh sách hình sẽ đổi trong bài hát. Nếu chỉ để 1 hình thì <=> tắt chức năng hình tự đổi-->
            <img src="images/nhungnamthangay/slide-1.jpg" />
             <img src="images/nhungnamthangay/slide-2.jpg" />
              <img src="images/nhungnamthangay/slide-3.jpg" />
               <img src="images/nhungnamthangay/slide-4.jpg" />
                <img src="images/nhungnamthangay/slide-5.jpg" />
                 <img src="images/nhungnamthangay/slide-6.jpg" />
                  <img src="images/nhungnamthangay/slide-7.jpg" />
                   <img src="images/nhungnamthangay/slide-8.jpg" />
                    <img src="images/nhungnamthangay/slide-9.jpg" />
                     <img src="images/nhungnamthangay/slide-10.jpg" />
                      <img src="images/nhungnamthangay/slide-11.jpg" />
                       <img src="images/nhungnamthangay/slide-12.jpg" />

Các bạn chỉ việc thêm hình hoặc xoá bớt hình và thay đổi giá trị src đến đường dẫn hình của bạn. Nếu bạn chỉ để 1 hình (1 thẻ img) thì tương đương với việc bạn đã tắt chức năng chạy slide hình giống file phiasaumotcogai.html mình chỉ để 1 hình đó.

Chú ý: Vì hình sẽ hiển thị toàn màn hình nên các bạn chọn những hình background kích cỡ >= 1028 x 768 px nhé

Về phần nội dung hiển thị thì bạn tìm trong thẻ <div class="container">. Các bạn có thể tuỳ ý chỉnh nội dung trong thẻ này. Trong ví dụ mình để tên bài hát và tất cả lời của bài còn bạn có thể ghi lời tỏ tình, nội dung nhạc phim, ... bất kỳ thứ gì ý nghĩa mà bạn muốn.

Tiếp theo là sửa danh sách lời bài hát (lyrics). Phần này thì đòi hỏi bạn phải kiên trì và chỉnh chu bằng cách mở nhạc lên và dán lời bài hát và thời gian lời bài hát đó xuất hiện (tính theo đơn vị là giây). Bạn sửa lời bài hát và thời gian hiển thị lời nằm trong đoạn lệnh phía dưới.
 var lyrics = [
            { content: "You hui dao zui chu de qi dian ji yi zhong ni qing se de lian", content2:"Lại trở về điểm xuất phát ban đầu, gương mặt non nớt của em trong ký ức  ", showAt: 18 },
            { content: "Wo men zhong yu lai dao le zhe yi tian",content2:"Cuối cùng chúng ta cũng đi đến được ngày này  ", showAt: 24 },
            { content: "Zhuo dian xia de lao zhao pian wu shu hui yi lian jie ",content2:"Bức ảnh cũ dưới mặt bản, biết bao hổi ức nối tiếp nhau ", showAt: 30 },
            { content: "Jin tian nan hai yao fu nuu hai zui hou de yue",content2:"Hôm nay cậu trai đi đến gặp cô gái trong buổi hẹn cuối cùng.", showAt: 36 },

            { content: "You hui dao zui chu de qi dian dai dai de zhan zai jing zi qian",content2:"Lại trở về điểm xuất phát ban đầu, ngốc nghếch đứng trước gương ", showAt: 42 },
            { content: "Ben zhuo ji shang hong se ling dai de jie ",content2:"Vụng về thắt chiếc cà vạt đỏ ", showAt: 48 },
            { content: "Jiang tou fa shu cheng da ren mo yang chuan shang yi shen shuai qi xi zhuang ",content2:"Chải tóc thành bộ dạng người lớn, mặc một bộ âu phục lịch lãm ", showAt: 53 },
            { content: "Deng hui er jian ni yi ding bi xiang xiang mei ",content2:"Lát nữa gặp em nhất định sẽ đẹp hơn cả ngay trong tưởng tượng ", showAt: 60 },

            { content: "Hao xiang zai hui dao na xie nian de shi guang ",content2:"Rất muốn lại được trở về thời gian của những năm tháng ấy ", showAt: 67 },
            { content: "Hui dao jiao shi zuo wei qian hou gu yi tao ni wen rou de ma",content2:"Trở về chỗ ngồi trước, chỗ ngổi sau trong lớp, cố ý để được em mắng dịu dàng.", showAt: 71 },
            { content: "Hei ban shang pai lie zu he ni she de jie kai ma",content2:"Dãy số trên bảng đen em có nỡ giải đáp không? ", showAt: 79 },
            { content: "Shei yu shei zuo ta you ai zhe ta",content2:"Ai ngồi cạnh ai, cậu ấy đã yêu cô ấy mất rồi. ", showAt: 85 },

            { content: "Na xie nian cuo guo de da yu na xie nian cuo guo de ai qing",content2:"Cơn mưa bỏ lại của những năm tháng ấy. Tình yêu bỏ lại của những năm tháng ấy ", showAt: 94 },
            { content: "Hao xiang yong bao ni yong bao cuo guo de yong qi ", content2:"Thật muốn ôm lấy em, ôm lấy dũng khí đã để trôi xa ",showAt: 100 },
            { content: "Ceng jing xiang zheng fu quan shi jie dao zui hou hui shou cai fa xian  ",content2:"Đã từng muốn chinh phục cả thế giới nhưng đến cuối cùng quay đầu nhìn anh lại mới nhận ra ", showAt: 106 },
            { content: "Zhe shi jie di di dian dian quan bu dou shi ni ", content2:"Từng mảnh từng mảnh trong thế giới này đều là em.",showAt: 111 },
            { content: "Na xie nian cuo guo de da yu na xie nian cuo guo de ai qing ",content2:"Cơn mưa bỏ lại của những năm tháng ấy, tình yêu bỏ lại của những năm tháng ấy ", showAt: 118 },
            { content: "Hao xiang gao su ni gao su ni wo mei you wang ji  ", content2:"Thật muốn nói với em rằng anh vẫn chưa hề quên ",showAt: 124 },
            { content: "Na tian wan shang man tian xing xing ping xing shi kong xia de yue ding  ",content2:"Bầu trời đầy sao trong đêm hôm ấy và hẹn ước dưới vòm trời ", showAt: 130 },
            { content: "Zai yi ci xiang yu wo hui jin jin bao zhe ni  ",content2:"Nếu được gặp lại em, anh sẽ ôm lấy em thật chặt ", showAt: 135 },
            { content: "Jin jin bao zhe ni  ",content2:"Ôm chặt lấy em... ", showAt: 141 },
            { content: "You hui dao zui chu de qi dian dai dai de zhan zai jing zi qian",content2:"Lại trở về điểm xuất phát ban đầu, ngốc nghếch đứng trước gương ", showAt: 158 },
            { content: "Ben zhuo ji shang hong se ling dai de jie ", content2:"Vụng về thắt chiếc cà vạt đỏ",showAt: 164 },
            { content: "Jiang tou fa shu cheng da ren mo yang chuan shang yi shen shuai qi xi zhuang ",content2:"Chải tóc thành bộ dạng người lớn, mặc một bộ âu phục lịch lãm ", showAt: 169 },
            { content: "Deng hui er jian ni yi ding bi xiang xiang mei ",content2:"Lát nữa gặp em nhất định sẽ đẹp hơn cả ngay trong tưởng tượng ", showAt: 176 },
            { content: "Hao xiang zai hui dao na xie nian de shi guang ",content2:"Rất muốn lại được trở về thời gian của những năm tháng ấy ", showAt: 183 },
             ...
            { content: "Jin jin bao zhe ni  ",content2:"Ôm chặt lấy em... ", showAt: 333 }
        ]

Bạn gán lời bài hát vào thuộc tính content, nếu bài hát nước ngoài và có vietsub thì bạn gán lời vietsub vào thược tính content2 không thì để giống như cũ (nhớ tránh xoá nhầm thuộc tính content2 nhé) và gán thời gian lời hiển thị vào thuộc tính showAt

Chú ý: Nếu lời bái hát ngắn hơn thì bạn xoá bớt các dòng { content: ... } và nếu lời bài hát dài hơn thì bạn tự thêm các dong {content: ... } nhé. Phần này bạn chỉnh sửa cẩn thận không được xoá thuộc tính content2 nhé.

Giờ là đến phần bật hoặc tắt 1 số chức năng trong trang. 
Các bạn nhìn cuối trang có đoạn lệnh sau:

  // Bật hoặc tắt vietsub đi kèm  (nếu bài hát là nhạc Việt thì để chắc chắn để false thôi nhé vì làm gì có vietsub ^^)
        var enablePlayWidthContent2 = true;
        // Bật hoặc tắt thay đổi màu nền của lời bài hát
        var enableChangeBackgroundLyric = true;
        // Lời giới thiệu về bài hát
        var introduction = "Các bạn đang nghe ca khúc trích trong phim Những năm tháng ấy. Design by Tuấn Nguyễn";
        // thời lượng bài hát
        var minuteOfMusic = 6;
        var secondOfMucsic = 11;

Ý nghĩa đoạn lệnh này mình đã giải thích trong code rồi nên không cần giải thích thêm. Bạn sửa lại cho đúng nhé.

Bước 4: Save hoặc Save as ra 1 file .html khác nếu muốn giữ file index.html nhé

Vậy là xong. Mở file lên cùng hưởng thụ thành quả và chia sẻ lên group cho mọi người cùng thưởng thực nhé 😉

Mọi thắc mắc các bạn comment dưới bài viết nhé.

Đừng quên theo dõi đăng ký nhận tin từ blog nhé vì sau này blog sẽ có rất nhiều bài viết hướng dẫn lập trình nhé bạn.