wp-quickstart-installer/step03.py
google-labs-jules[bot] 118771021e Implement specific setups feature
- Added a new step (Step02_5) for users to select a WordPress setup type (e.g., Elementor, Blog, All Items).
- Modified Step03 to dynamically display plugins and themes based on the chosen setup.
- Updated APP_CONFIG (defined in step03.py) to store setup configurations, including plugin/theme details (id, name, icon, path).
- Changed selections.json to store a list of dictionaries for selected items, providing richer data (including paths) for the installation process.
- Updated Step05 to correctly parse the new selections.json structure and use item names for downloading.
- Adjusted navigation in SetupApp.py, step02.py, and step03.py to accommodate the new flow.
2025-06-22 17:31:27 +00:00

235 lines
12 KiB
Python

import tkinter as tk
from tkinter import ttk
import subprocess
from PIL import Image, ImageTk
import os
import json
import sys
# APP_CONFIG: Central configuration for setups, plugins, and themes
# This would ideally be in a separate app_data.py or managed by the controller
APP_CONFIG = {
"setups": {
"elementor_focus": {
"name": "Elementor Website",
"description": "Ideal for building rich, visual websites with Elementor page builder.",
"plugins": [
{"id": "elementor", "name": "Elementor", "icon": "img/elementor.jpg", "path": "plugins/elementor.zip"},
{"id": "contact_form_7", "name": "Contact Form 7", "icon": "img/contact_form_7.jpg", "path": "plugins/contact-form-7.zip"},
{"id": "yoast_seo", "name": "Yoast SEO", "icon": "img/yoast_seo.jpg", "path": "plugins/wordpress-seo.zip"},
{"id": "litespeed_cache", "name": "LiteSpeed Cache", "icon": "img/litespeed_cache.jpg", "path": "plugins/litespeed-cache.zip"},
],
"themes": [
{"id": "hello_elementor", "name": "Hello Elementor", "icon": "img/hello.jpg", "path": "themes/hello-elementor.zip"},
{"id": "astra", "name": "Astra", "icon": "img/astra.jpg", "path": "themes/astra.zip"},
]
},
"blogging_setup": {
"name": "Just a Blog Setup",
"description": "Perfect for starting a blog with essential SEO and writing tools.",
"plugins": [
{"id": "yoast_seo", "name": "Yoast SEO", "icon": "img/yoast_seo.jpg", "path": "plugins/wordpress-seo.zip"},
{"id": "yoast_duplicate_post", "name": "Yoast Duplicate Post", "icon": "img/yoast_duplicate_post.jpg", "path": "plugins/yoast-duplicate-post.zip"},
{"id": "autoptimize", "name": "Autoptimize", "icon": "img/autoptimize.jpg", "path": "plugins/autoptimize.zip"},
],
"themes": [
{"id": "generatepress", "name": "GeneratePress", "icon": "img/generatepress.jpg", "path": "themes/generatepress.zip"},
{"id": "kadence", "name": "Kadence", "icon": "img/kadence.jpg", "path": "themes/kadence.zip"},
]
},
"all_items": {
"name": "All Items (Manual Selection)",
"description": "Choose from all available plugins and themes.",
"plugins": [
{"id": "elementor", "name": "Elementor", "icon": "img/elementor.jpg", "path": "plugins/elementor.zip"},
{"id": "contact_form_7", "name": "Contact Form 7", "icon": "img/contact_form_7.jpg", "path": "plugins/contact-form-7.zip"},
{"id": "yoast_seo", "name": "Yoast SEO", "icon": "img/yoast_seo.jpg", "path": "plugins/wordpress-seo.zip"},
{"id": "woocommerce", "name": "WooCommerce", "icon": "img/woocommerce.jpg", "path": "plugins/woocommerce.zip"},
{"id": "litespeed_cache", "name": "LiteSpeed Cache", "icon": "img/litespeed_cache.jpg", "path": "plugins/litespeed-cache.zip"},
{"id": "really_simple_ssl", "name": "Really Simple SSL", "icon": "img/really_simple_ssl.jpg", "path": "plugins/really-simple-ssl.zip"},
{"id": "yoast_duplicate_post", "name": "Yoast Duplicate Post", "icon": "img/yoast_duplicate_post.jpg", "path": "plugins/yoast-duplicate-post.zip"},
{"id": "wp_mail_smtp", "name": "WP Mail SMTP", "icon": "img/wp_mail_smtp.jpg", "path": "plugins/wp-mail-smtp.zip"},
{"id": "autoptimize", "name": "Autoptimize", "icon": "img/autoptimize.jpg", "path": "plugins/autoptimize.zip"},
{"id": "duplicator", "name": "Duplicator", "icon": "img/duplicator.jpg", "path": "plugins/duplicator.zip"},
{"id": "wp_fastest_cache", "name": "WP Fastest Cache", "icon": "img/wp_fastest_cache.jpg", "path": "plugins/wp-fastest-cache.zip"}
],
"themes": [
{"id": "hello_elementor", "name": "Hello Elementor", "icon": "img/hello.jpg", "path": "themes/hello-elementor.zip"},
{"id": "astra", "name": "Astra", "icon": "img/astra.jpg", "path": "themes/astra.zip"},
{"id": "kadence", "name": "Kadence", "icon": "img/kadence.jpg", "path": "themes/kadence.zip"},
{"id": "generatepress", "name": "GeneratePress", "icon": "img/generatepress.jpg", "path": "themes/generatepress.zip"},
{"id": "storefront", "name": "Storefront", "icon": "img/storefront.jpg", "path": "themes/storefront.zip"},
{"id": "hello_biz", "name": "Hello Biz", "icon": "img/hello_biz.jpg", "path": "themes/hello-biz.zip"}
]
}
}
}
class Step03(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.controller = controller
self.configure(bg="#f4f4f4")
self.canvas = tk.Canvas(self, bg="#f4f4f4")
self.scrollbar = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.scrollable_frame = tk.Frame(self.canvas, bg="#f4f4f4")
self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
self.canvas.bind_all("<MouseWheel>", lambda e: self.canvas.yview_scroll(-1*(e.delta//120), "units"))
self.title_label = tk.Label(self.scrollable_frame, text="Select Plugins and Themes", font=("Arial", 18, "bold"), bg="#f4f4f4")
self.title_label.pack(pady=(20, 10))
# Placeholders for plugin and theme frames, will be populated by populate_items
self.plugins_group_frame = tk.Frame(self.scrollable_frame, bg="#f4f4f4")
self.plugins_group_frame.pack(pady=10, fill="x")
self.themes_group_frame = tk.Frame(self.scrollable_frame, bg="#f4f4f4")
self.themes_group_frame.pack(pady=10, fill="x")
self.plugin_vars = {}
self.theme_vars = {}
# Button frame
self.button_frame = tk.Frame(self.scrollable_frame, bg="#f4f4f4") # Changed to self.button_frame to allow re-packing
self.button_frame.pack(pady=20)
prev_button = ttk.Button(self.button_frame, text="< Prev", command=self.prev_window)
prev_button.grid(row=0, column=0, padx=10)
next_button = ttk.Button(self.button_frame, text="> Next", command=self.next_window)
next_button.grid(row=0, column=1, padx=10)
# Add an on_show_frame method to be called by the controller
self.bind("<<ShowFrame>>", self.on_show_frame)
def resource_path(self, relative_path):
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
full_path = os.path.join(base_path, relative_path)
# print(f"Resolving path: {full_path}") # Debug print, can be noisy
if not os.path.exists(full_path):
print(f"Warning: Resource path not found: {full_path}")
return full_path
def create_checkbox_with_icon(self, parent, item_id, item_name, var, row, column, icon_path_str):
frame = tk.Frame(parent, bg="#f4f4f4")
frame.grid(row=row, column=column, padx=10, pady=5, sticky=tk.W)
try:
icon_path = self.resource_path(icon_path_str)
if os.path.exists(icon_path):
img = Image.open(icon_path).resize((30, 30))
icon = ImageTk.PhotoImage(img)
img_label = tk.Label(frame, image=icon, bg="#f4f4f4")
img_label.image = icon
img_label.pack(side=tk.LEFT, padx=5)
else:
# Fallback if image not found
tk.Label(frame, text="[img]", bg="#f4f4f4").pack(side=tk.LEFT, padx=5)
except Exception as e:
print(f"Error loading image {icon_path_str}: {e}")
tk.Label(frame, text="[img err]", bg="#f4f4f4").pack(side=tk.LEFT, padx=5)
checkbox = tk.Checkbutton(frame, text=item_name, variable=var, bg="#f4f4f4")
checkbox.pack(side=tk.LEFT)
# Simplified tooltip for now, can be expanded later if needed
# For more complex tooltips, a dedicated tooltip class might be better
# info_img_path = self.resource_path("img/question.png")
# if os.path.exists(info_img_path):
# info_img = Image.open(info_img_path).resize((15, 15))
# info_icon = ImageTk.PhotoImage(info_img)
# info_label = tk.Label(frame, image=info_icon, bg="#f4f4f4", cursor="question_arrow")
# info_label.image = info_icon
# info_label.pack(side=tk.LEFT, padx=5)
# Bind tooltip events here if re-adding full tooltip functionality
def on_show_frame(self, event=None): # Modified to be event handler if bound
# Clear previous items
for widget in self.plugins_group_frame.winfo_children():
widget.destroy()
for widget in self.themes_group_frame.winfo_children():
widget.destroy()
self.plugin_vars.clear()
self.theme_vars.clear()
selected_setup_key = self.controller.shared_data.get("selected_setup_key", "all_items")
setup_data = APP_CONFIG["setups"].get(selected_setup_key)
if not setup_data:
print(f"Error: Setup key '{selected_setup_key}' not found in APP_CONFIG.")
# Display an error message to the user in the UI
error_label = tk.Label(self.scrollable_frame, text=f"Error: Setup '{selected_setup_key}' not found.", fg="red", bg="#f4f4f4")
error_label.pack(pady=10) # Make sure this is packed before buttons
self.button_frame.pack_forget() # Hide buttons if error
return
# Ensure button frame is visible if it was hidden due to previous error
if not self.button_frame.winfo_ismapped():
self.button_frame.pack(pady=20) # Re-pack it
# Remove any previous error message
for widget in self.scrollable_frame.winfo_children():
if isinstance(widget, tk.Label) and widget.cget("fg") == "red":
widget.destroy()
# Plugins
if setup_data["plugins"]:
subheadline_plugins = tk.Label(self.plugins_group_frame, text="Plugins", font=("Arial", 14, "bold"), bg="#f4f4f4")
subheadline_plugins.pack(pady=10)
plugins_frame_inner = tk.Frame(self.plugins_group_frame, bg="#f4f4f4")
plugins_frame_inner.pack(pady=10)
for idx, plugin in enumerate(setup_data["plugins"]):
var = tk.IntVar()
self.plugin_vars[plugin["id"]] = {"var": var, "data": plugin}
self.create_checkbox_with_icon(plugins_frame_inner, plugin["id"], plugin["name"], var, idx // 3, idx % 3, plugin["icon"])
# Themes
if setup_data["themes"]:
subheadline_themes = tk.Label(self.themes_group_frame, text="Themes", font=("Arial", 14, "bold"), bg="#f4f4f4")
subheadline_themes.pack(pady=10)
themes_frame_inner = tk.Frame(self.themes_group_frame, bg="#f4f4f4")
themes_frame_inner.pack(pady=10)
for idx, theme in enumerate(setup_data["themes"]):
var = tk.IntVar()
self.theme_vars[theme["id"]] = {"var": var, "data": theme}
self.create_checkbox_with_icon(themes_frame_inner, theme["id"], theme["name"], var, idx // 2, idx % 2, theme["icon"])
self.canvas.yview_moveto(0) # Scroll to top
def prev_window(self):
self.controller.show_frame("Step02_5")
def next_window(self):
selected_plugins_data = []
for item_id, item_info in self.plugin_vars.items():
if item_info["var"].get() == 1:
selected_plugins_data.append(item_info["data"]) # Store the whole plugin dict
selected_themes_data = []
for item_id, item_info in self.theme_vars.items():
if item_info["var"].get() == 1:
selected_themes_data.append(item_info["data"]) # Store the whole theme dict
selections = {
"plugins": selected_plugins_data, # Now list of dicts
"themes": selected_themes_data # Now list of dicts
}
with open("selections.json", "w") as file:
json.dump(selections, file, indent=4) # Added indent for readability
self.controller.show_frame("Step04")