diff --git a/Design/adminPage_ideas.md b/Design/adminPage_ideas.md index b874fcd4..1ed1df10 100644 --- a/Design/adminPage_ideas.md +++ b/Design/adminPage_ideas.md @@ -20,7 +20,7 @@ Customize Chỉnh sửa giao diện trực quan theo thời gian thực (Header, Widgets Quản lý các khối nội dung ở Sidebar hoặc Footer. Menus Thiết kế menu điều hướng chính ở đầu trang và chân trang. Theme File Editor Chỉnh sửa trực tiếp file code của giao diện (chỉ dùng cho lập trình viên). -7. Plugins (Gói mở rộng) Installed Plugins Quản lý, kích hoạt, vô hiệu hóa hoặc xóa các Plugin đang có trên hệ thống. +(done)7. Plugins (Gói mở rộng) Installed Plugins Quản lý, kích hoạt, vô hiệu hóa hoặc xóa các Plugin đang có trên hệ thống. Add New Tìm kiếm và cài đặt Plugin mới từ thư viện WordPress.org hoặc upload file .zip. Plugin File Editor Chỉnh sửa mã nguồn của các Plugin trực tiếp từ admin. 8. Users (Thành viên) All Users Quản lý danh sách toàn bộ tài khoản đăng ký trên hệ thống. @@ -37,6 +37,8 @@ Discussion Cấu hình bình luận (yêu cầu phê duyệt, chặn từ ngữ Media Cài đặt kích thước mặc định cho ảnh khi upload (Thumbnail, Medium, Large). Permalinks Định dạng cấu trúc đường dẫn tĩnh (URL) tối ưu cho SEO (ví dụ: /post-name/). Privacy Thiết lập trang chính sách bảo mật cho website. + + PHẦN 2: CÁC HÀM PHP & HOOKS ĐỂ PHÁT TRIỂN TRANG ADMIN (DEVELOPER API) Nếu bạn đang lập trình tùy biến trang admin (hoặc xây dựng CMS tương tự), WordPress cung cấp bộ API để tạo và kiểm soát trang admin sau: diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/CacheConfiguration.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/CacheConfiguration.java index 5aadb9b9..5e173787 100644 --- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/CacheConfiguration.java +++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/config/CacheConfiguration.java @@ -44,6 +44,10 @@ public class CacheConfiguration { createCache(cm, com.sisvietnamvn.web.domain.User.class.getName()); createCache(cm, com.sisvietnamvn.web.domain.Authority.class.getName()); createCache(cm, com.sisvietnamvn.web.domain.User.class.getName() + ".authorities"); + createCache(cm, com.sisvietnamvn.web.domain.Media.class.getName()); + createCache(cm, com.sisvietnamvn.web.domain.Plugin.class.getName()); + createCache(cm, com.sisvietnamvn.web.domain.Setting.class.getName()); + createCache(cm, "settings"); // jhipster-needle-ehcache-add-entry }; } diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManageSettingController.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManageSettingController.java new file mode 100644 index 00000000..f3da7e80 --- /dev/null +++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManageSettingController.java @@ -0,0 +1,142 @@ +package com.sisvietnamvn.web.controller.manage; + +import com.sisvietnamvn.web.service.SettingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.util.Map; + +@Controller +@RequestMapping("/manage/settings") +public class ManageSettingController { + + private static final Logger LOG = LoggerFactory.getLogger(ManageSettingController.class); + + private final SettingService settingService; + + public ManageSettingController(SettingService settingService) { + this.settingService = settingService; + } + + private void addSettingsToModel(Model model) { + model.addAttribute("settings", settingService.getAllAsMap()); + } + + private String saveSettings(Map allParams, RedirectAttributes redirectAttributes, String viewName) { + // Remove CSRF token from params before saving + allParams.remove("_csrf"); + + try { + settingService.setValues(allParams); + redirectAttributes.addFlashAttribute("successMessage", "Settings saved successfully."); + } catch (Exception e) { + LOG.error("Error saving settings", e); + redirectAttributes.addFlashAttribute("errorMessage", "Failed to save settings: " + e.getMessage()); + } + return "redirect:/manage/settings/" + viewName; + } + + // --- General Settings --- + + @GetMapping("/general") + public String showGeneralSettings(Model model) { + addSettingsToModel(model); + return "manage/settings/general"; + } + + @PostMapping("/general/save") + public String saveGeneralSettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + return saveSettings(allParams, redirectAttributes, "general"); + } + + // --- Writing Settings --- + + @GetMapping("/writing") + public String showWritingSettings(Model model) { + addSettingsToModel(model); + return "manage/settings/writing"; + } + + @PostMapping("/writing/save") + public String saveWritingSettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + return saveSettings(allParams, redirectAttributes, "writing"); + } + + // --- Reading Settings --- + + @GetMapping("/reading") + public String showReadingSettings(Model model) { + addSettingsToModel(model); + return "manage/settings/reading"; + } + + @PostMapping("/reading/save") + public String saveReadingSettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + // Handle un-checked checkboxes which don't send a value + if (!allParams.containsKey("search_engine_visibility")) { + allParams.put("search_engine_visibility", "0"); + } + return saveSettings(allParams, redirectAttributes, "reading"); + } + + // --- Discussion Settings --- + + @GetMapping("/discussion") + public String showDiscussionSettings(Model model) { + addSettingsToModel(model); + return "manage/settings/discussion"; + } + + @PostMapping("/discussion/save") + public String saveDiscussionSettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + // Handle checkboxes + if (!allParams.containsKey("allow_comments")) allParams.put("allow_comments", "0"); + if (!allParams.containsKey("comment_author_must_fill")) allParams.put("comment_author_must_fill", "0"); + if (!allParams.containsKey("comment_manual_approval")) allParams.put("comment_manual_approval", "0"); + + return saveSettings(allParams, redirectAttributes, "discussion"); + } + + // --- Media Settings --- + + @GetMapping("/media") + public String showMediaSettings(Model model) { + addSettingsToModel(model); + return "manage/settings/media"; + } + + @PostMapping("/media/save") + public String saveMediaSettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + return saveSettings(allParams, redirectAttributes, "media"); + } + + // --- Permalinks Settings --- + + @GetMapping("/permalinks") + public String showPermalinksSettings(Model model) { + addSettingsToModel(model); + return "manage/settings/permalinks"; + } + + @PostMapping("/permalinks/save") + public String savePermalinksSettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + return saveSettings(allParams, redirectAttributes, "permalinks"); + } + + // --- Privacy Settings --- + + @GetMapping("/privacy") + public String showPrivacySettings(Model model) { + addSettingsToModel(model); + return "manage/settings/privacy"; + } + + @PostMapping("/privacy/save") + public String savePrivacySettings(@RequestParam Map allParams, RedirectAttributes redirectAttributes) { + return saveSettings(allParams, redirectAttributes, "privacy"); + } +} diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Setting.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Setting.java new file mode 100644 index 00000000..3e50fc0f --- /dev/null +++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Setting.java @@ -0,0 +1,104 @@ +package com.sisvietnamvn.web.domain; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import java.io.Serializable; + +/** + * A Setting. + * This entity stores key-value configuration pairs for the application. + */ +@Entity +@Table(name = "sis_setting") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Setting extends AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator", sequenceName = "sis_setting_seq", allocationSize = 1) + private Long id; + + @NotNull + @Size(max = 100) + @Column(name = "setting_key", length = 100, nullable = false, unique = true) + private String settingKey; + + @Size(max = 2000) + @Column(name = "setting_value", length = 2000) + private String settingValue; + + // jhipster-needle-entity-add-field - JHipster will add fields here + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Setting id(Long id) { + this.setId(id); + return this; + } + + public String getSettingKey() { + return settingKey; + } + + public void setSettingKey(String settingKey) { + this.settingKey = settingKey; + } + + public Setting settingKey(String settingKey) { + this.setSettingKey(settingKey); + return this; + } + + public String getSettingValue() { + return settingValue; + } + + public void setSettingValue(String settingValue) { + this.settingValue = settingValue; + } + + public Setting settingValue(String settingValue) { + this.setSettingValue(settingValue); + return this; + } + + // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Setting)) { + return false; + } + return id != null && id.equals(((Setting) o).id); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + // prettier-ignore + @Override + public String toString() { + return "Setting{" + + "id=" + getId() + + ", settingKey='" + getSettingKey() + "'" + + ", settingValue='" + getSettingValue() + "'" + + "}"; + } +} diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/repository/SettingRepository.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/repository/SettingRepository.java new file mode 100644 index 00000000..f7a9ea15 --- /dev/null +++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/repository/SettingRepository.java @@ -0,0 +1,15 @@ +package com.sisvietnamvn.web.repository; + +import com.sisvietnamvn.web.domain.Setting; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/** + * Spring Data JPA repository for the Setting entity. + */ +@Repository +public interface SettingRepository extends JpaRepository { + Optional findBySettingKey(String settingKey); +} diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/SettingService.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/SettingService.java new file mode 100644 index 00000000..f26c9c32 --- /dev/null +++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/SettingService.java @@ -0,0 +1,79 @@ +package com.sisvietnamvn.web.service; + +import com.sisvietnamvn.web.domain.Setting; +import com.sisvietnamvn.web.repository.SettingRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@Transactional +public class SettingService { + + private static final Logger LOG = LoggerFactory.getLogger(SettingService.class); + + private final SettingRepository settingRepository; + + public SettingService(SettingRepository settingRepository) { + this.settingRepository = settingRepository; + } + + /** + * Get a setting value by key. + */ + @Transactional(readOnly = true) + @Cacheable(value = "settings", key = "#key") + public String getValue(String key, String defaultValue) { + return settingRepository.findBySettingKey(key) + .map(Setting::getSettingValue) + .orElse(defaultValue); + } + + /** + * Set a setting value by key. + */ + @CacheEvict(value = "settings", allEntries = true) + public void setValue(String key, String value) { + Optional settingOpt = settingRepository.findBySettingKey(key); + if (settingOpt.isPresent()) { + Setting setting = settingOpt.get(); + setting.setSettingValue(value); + settingRepository.save(setting); + LOG.debug("Updated setting '{}' to '{}'", key, value); + } else { + Setting setting = new Setting(); + setting.setSettingKey(key); + setting.setSettingValue(value); + settingRepository.save(setting); + LOG.debug("Created new setting '{}' with value '{}'", key, value); + } + } + + /** + * Set multiple settings at once. + */ + @CacheEvict(value = "settings", allEntries = true) + public void setValues(Map settings) { + settings.forEach(this::setValue); + } + + /** + * Get all settings as a map for easy rendering in UI templates. + */ + @Transactional(readOnly = true) + public Map getAllAsMap() { + List allSettings = settingRepository.findAll(); + return allSettings.stream() + .collect(Collectors.toMap(Setting::getSettingKey, setting -> + setting.getSettingValue() == null ? "" : setting.getSettingValue() + )); + } +} diff --git a/sisvietnamvn_main/src/main/resources/config/liquibase/changelog/20260702153000_add_setting_entity.xml b/sisvietnamvn_main/src/main/resources/config/liquibase/changelog/20260702153000_add_setting_entity.xml new file mode 100644 index 00000000..82db6d48 --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/config/liquibase/changelog/20260702153000_add_setting_entity.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml b/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml index d5b22122..635ecc51 100644 --- a/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml +++ b/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml @@ -28,6 +28,7 @@ + diff --git a/sisvietnamvn_main/src/main/resources/templates/fragments/manage-layout.html b/sisvietnamvn_main/src/main/resources/templates/fragments/manage-layout.html index aea20ea9..36919757 100644 --- a/sisvietnamvn_main/src/main/resources/templates/fragments/manage-layout.html +++ b/sisvietnamvn_main/src/main/resources/templates/fragments/manage-layout.html @@ -170,6 +170,27 @@ + + + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/discussion.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/discussion.html new file mode 100644 index 00000000..6088703f --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/discussion.html @@ -0,0 +1,77 @@ + + + + Discussion Settings + + +
+

Discussion Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+ +
+
+ + +
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+ + When a comment contains any of these words in its content, author name, URL, email, IP address, or browser's user agent string, it will be held in the moderation queue. One word or IP address per line. +
+
+ +
+
+
+ +
+
+
+
+
+
+ + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/general.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/general.html new file mode 100644 index 00000000..51678754 --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/general.html @@ -0,0 +1,96 @@ + + + + General Settings + + +
+

General Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+ +
+ +
+
+ +
+ +
+ + In a few words, explain what this site is about. +
+
+ +
+ +
+ + This address is used for admin purposes. +
+
+ +
+ +
+ +
+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ +
+
+
+
+
+
+ + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/media.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/media.html new file mode 100644 index 00000000..746ef4a7 --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/media.html @@ -0,0 +1,72 @@ + + + + Media Settings + + +
+

Media Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +
Image sizes
+

The sizes listed below determine the maximum dimensions in pixels to use when adding an image to the Media Library.

+ +
+ +
+ + + + +
+
+ +
+ +
+ + + + +
+
+ +
+ +
+ + + + +
+
+ +
+
+
+ +
+
+
+
+
+
+ + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/permalinks.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/permalinks.html new file mode 100644 index 00000000..6afab3b8 --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/permalinks.html @@ -0,0 +1,68 @@ + + + + Permalink Settings + + +
+

Permalink Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +
Common Settings
+

Select the permalink structure for your website. (Note: Since this is a Spring Boot application, changes here define the SEO structure preference but may require developer adjustments in the routing controller to take full effect).

+ +
+
Permalink structure
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ +
+
+
+
+
+
+ + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/privacy.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/privacy.html new file mode 100644 index 00000000..9ad7bfcc --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/privacy.html @@ -0,0 +1,50 @@ + + + + Privacy Settings + + +
+

Privacy Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +

As a website owner, you may need to follow national or international privacy laws. For example, you may need to create and display a privacy policy.

+

Select a Privacy Policy page to be shown on your login and registration pages.

+
+ +
+ +
+ +
+
+ +
+
+
+
+
+
+ + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/reading.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/reading.html new file mode 100644 index 00000000..e8d74046 --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/reading.html @@ -0,0 +1,101 @@ + + + + Reading Settings + + +
+

Reading Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+ +
+
+ + +
+
+ + +
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+ +
+
posts
+
+ +
+ +
+ +
+
items
+
+ +
+ +
+
+ + +
+ It is up to search engines to honor this request. +
+
+ +
+
+
+ +
+
+
+
+
+
+ + diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/settings/writing.html b/sisvietnamvn_main/src/main/resources/templates/manage/settings/writing.html new file mode 100644 index 00000000..9f7e27a2 --- /dev/null +++ b/sisvietnamvn_main/src/main/resources/templates/manage/settings/writing.html @@ -0,0 +1,90 @@ + + + + Writing Settings + + +
+

Writing Settings

+ +
+ + +
+
+ + +
+ +
+
+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
Post via email
+

To post to your site by email, you must set up a secret email account with POP3 access. Any mail received at this address will be posted, so it's a good idea to keep this address very secret.

+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+
+
+
+
+
+ +