upskill-event-manager/includes/google-sheets/class-google-sheets-folder-manager.php
bengizmo 37f4180e1c feat: Add massive missing plugin infrastructure to repository
🚨 CRITICAL: Fixed deployment blockers by adding missing core directories:

**Community System (CRITICAL)**
- includes/community/ - Login_Handler and all community classes
- templates/community/ - Community login forms

**Certificate System (CRITICAL)**
- includes/certificates/ - 8+ certificate classes and handlers
- templates/certificates/ - Certificate reports and generation templates

**Core Individual Classes (CRITICAL)**
- includes/class-hvac-event-summary.php
- includes/class-hvac-trainer-profile-manager.php
- includes/class-hvac-master-dashboard-data.php
- Plus 40+ other individual HVAC classes

**Major Feature Systems (HIGH)**
- includes/database/ - Training leads database tables
- includes/find-trainer/ - Find trainer directory and MapGeo integration
- includes/google-sheets/ - Google Sheets integration system
- includes/zoho/ - Complete Zoho CRM integration
- includes/communication/ - Communication templates system

**Template Infrastructure**
- templates/attendee/, templates/email-attendees/
- templates/event-summary/, templates/status/
- templates/template-parts/ - Shared template components

**Impact:**
- 70+ files added covering 10+ missing directories
- Resolves ALL deployment blockers and feature breakdowns
- Plugin activation should now work correctly
- Multi-machine deployment fully supported

🔧 Generated with Claude Code

Co-Authored-By: Ben Reed <ben@tealmaker.com>
2025-08-11 13:30:11 -03:00

390 lines
No EOL
13 KiB
PHP

<?php
/**
* Google Sheets Folder Manager
*
* Manages hierarchical folder structure for Google Sheets:
* - Upskill Training Sheets (root folder)
* - _Master Trainer (master reports)
* - Event: {Event Name 1} (event-specific sheets)
* - Event: {Event Name 2} (event-specific sheets)
* - etc.
*
* @package HVAC_Community_Events
* @subpackage Google_Sheets_Integration
*/
if (!defined('ABSPATH')) {
exit;
}
class HVAC_Google_Sheets_Folder_Manager {
private $auth;
private $logger;
// Folder structure constants
const ROOT_FOLDER_NAME = 'Upskill Training Sheets';
const MASTER_TRAINER_FOLDER_NAME = '_Master Trainer';
const EVENT_FOLDER_PREFIX = 'Event: ';
// Cached folder IDs
private $root_folder_id = null;
private $master_folder_id = null;
private $event_folders = array();
public function __construct() {
$this->auth = new HVAC_Google_Sheets_Auth();
if (class_exists('HVAC_Logger')) {
$this->logger = new HVAC_Logger();
}
}
/**
* Get or create the root "Upskill Training Sheets" folder
*/
public function get_root_folder_id() {
if ($this->root_folder_id) {
return $this->root_folder_id;
}
try {
// First, search for existing folder
$existing_folder = $this->find_folder_by_name(self::ROOT_FOLDER_NAME);
if ($existing_folder) {
$this->root_folder_id = $existing_folder['id'];
$this->log_info("Found existing root folder: {$this->root_folder_id}");
// Ensure proper permissions are set
$this->set_organization_permissions($this->root_folder_id);
return $this->root_folder_id;
}
// Create new root folder
$folder_data = array(
'name' => self::ROOT_FOLDER_NAME,
'mimeType' => 'application/vnd.google-apps.folder'
);
$response = $this->auth->make_drive_api_request('POST', 'files', $folder_data);
if (isset($response['id'])) {
$this->root_folder_id = $response['id'];
$this->log_info("Created root folder: {$this->root_folder_id}");
// Set organization permissions
$this->set_organization_permissions($this->root_folder_id);
// Make discoverable in search
$this->make_folder_discoverable($this->root_folder_id);
return $this->root_folder_id;
}
throw new Exception('Failed to create root folder');
} catch (Exception $e) {
$this->log_error('Failed to get/create root folder: ' . $e->getMessage());
return false;
}
}
/**
* Get or create the "_Master Trainer" folder
*/
public function get_master_trainer_folder_id() {
if ($this->master_folder_id) {
return $this->master_folder_id;
}
$root_folder_id = $this->get_root_folder_id();
if (!$root_folder_id) {
return false;
}
try {
// Search for existing master trainer folder
$existing_folder = $this->find_folder_by_name(self::MASTER_TRAINER_FOLDER_NAME, $root_folder_id);
if ($existing_folder) {
$this->master_folder_id = $existing_folder['id'];
$this->log_info("Found existing master trainer folder: {$this->master_folder_id}");
return $this->master_folder_id;
}
// Create master trainer folder
$folder_data = array(
'name' => self::MASTER_TRAINER_FOLDER_NAME,
'mimeType' => 'application/vnd.google-apps.folder',
'parents' => array($root_folder_id)
);
$response = $this->auth->make_drive_api_request('POST', 'files', $folder_data);
if (isset($response['id'])) {
$this->master_folder_id = $response['id'];
$this->log_info("Created master trainer folder: {$this->master_folder_id}");
return $this->master_folder_id;
}
throw new Exception('Failed to create master trainer folder');
} catch (Exception $e) {
$this->log_error('Failed to get/create master trainer folder: ' . $e->getMessage());
return false;
}
}
/**
* Get or create an event-specific folder
*/
public function get_event_folder_id($event_id) {
if (isset($this->event_folders[$event_id])) {
return $this->event_folders[$event_id];
}
$root_folder_id = $this->get_root_folder_id();
if (!$root_folder_id) {
return false;
}
$event = get_post($event_id);
if (!$event) {
$this->log_error("Event not found: {$event_id}");
return false;
}
$folder_name = self::EVENT_FOLDER_PREFIX . $event->post_title;
try {
// Search for existing event folder
$existing_folder = $this->find_folder_by_name($folder_name, $root_folder_id);
if ($existing_folder) {
$this->event_folders[$event_id] = $existing_folder['id'];
$this->log_info("Found existing event folder for {$event_id}: {$existing_folder['id']}");
return $this->event_folders[$event_id];
}
// Create event folder
$folder_data = array(
'name' => $folder_name,
'mimeType' => 'application/vnd.google-apps.folder',
'parents' => array($root_folder_id)
);
$response = $this->auth->make_drive_api_request('POST', 'files', $folder_data);
if (isset($response['id'])) {
$this->event_folders[$event_id] = $response['id'];
$this->log_info("Created event folder for {$event_id}: {$response['id']}");
return $this->event_folders[$event_id];
}
throw new Exception('Failed to create event folder');
} catch (Exception $e) {
$this->log_error("Failed to get/create event folder for {$event_id}: " . $e->getMessage());
return false;
}
}
/**
* Set organization-wide permissions on a folder
*/
private function set_organization_permissions($folder_id) {
try {
// Set permissions for measureQuick.com organization
$permission_data = array(
'role' => 'writer',
'type' => 'domain',
'domain' => 'measurequick.com',
'allowFileDiscovery' => true
);
$response = $this->auth->make_drive_api_request('POST', "files/{$folder_id}/permissions", $permission_data);
if (isset($response['id'])) {
$this->log_info("Set organization permissions on folder: {$folder_id}");
return true;
}
throw new Exception('Failed to set permissions');
} catch (Exception $e) {
$this->log_error("Failed to set organization permissions on {$folder_id}: " . $e->getMessage());
return false;
}
}
/**
* Make folder discoverable in Google Search
*/
private function make_folder_discoverable($folder_id) {
try {
// Update folder to be discoverable
$folder_data = array(
'capabilities' => array(
'canAddChildren' => true,
'canListChildren' => true,
'canRemoveChildren' => true
),
'viewersCanCopyContent' => true,
'copyRequiresWriterPermission' => false
);
$response = $this->auth->make_drive_api_request('PATCH', "files/{$folder_id}", $folder_data);
if (isset($response['id'])) {
$this->log_info("Made folder discoverable: {$folder_id}");
return true;
}
throw new Exception('Failed to make folder discoverable');
} catch (Exception $e) {
$this->log_error("Failed to make folder discoverable {$folder_id}: " . $e->getMessage());
return false;
}
}
/**
* Find a folder by name, optionally within a parent folder
*/
private function find_folder_by_name($name, $parent_id = null) {
try {
$query = "name='{$name}' and mimeType='application/vnd.google-apps.folder' and trashed=false";
if ($parent_id) {
$query .= " and '{$parent_id}' in parents";
}
$response = $this->auth->make_drive_api_request('GET', 'files', null, array(
'q' => $query,
'fields' => 'files(id,name,parents)',
'pageSize' => 10
));
if (isset($response['files']) && count($response['files']) > 0) {
return $response['files'][0]; // Return first match
}
return null;
} catch (Exception $e) {
$this->log_error("Failed to search for folder '{$name}': " . $e->getMessage());
return null;
}
}
/**
* Get folder structure overview
*/
public function get_folder_structure() {
$structure = array(
'root' => array(
'name' => self::ROOT_FOLDER_NAME,
'id' => $this->get_root_folder_id(),
'url' => null
),
'master_trainer' => array(
'name' => self::MASTER_TRAINER_FOLDER_NAME,
'id' => $this->get_master_trainer_folder_id(),
'url' => null
),
'event_folders' => array()
);
// Add URLs for existing folders
if ($structure['root']['id']) {
$structure['root']['url'] = "https://drive.google.com/drive/folders/{$structure['root']['id']}";
}
if ($structure['master_trainer']['id']) {
$structure['master_trainer']['url'] = "https://drive.google.com/drive/folders/{$structure['master_trainer']['id']}";
}
return $structure;
}
/**
* Verify and repair folder structure
*/
public function verify_folder_structure() {
$results = array();
// Check root folder
$root_id = $this->get_root_folder_id();
$results['root_folder'] = array(
'status' => $root_id ? 'exists' : 'missing',
'id' => $root_id,
'message' => $root_id ? 'Root folder found/created successfully' : 'Failed to create root folder'
);
// Check master trainer folder
if ($root_id) {
$master_id = $this->get_master_trainer_folder_id();
$results['master_trainer_folder'] = array(
'status' => $master_id ? 'exists' : 'missing',
'id' => $master_id,
'message' => $master_id ? 'Master trainer folder found/created successfully' : 'Failed to create master trainer folder'
);
}
// Check permissions
if ($root_id) {
$permissions_ok = $this->verify_organization_permissions($root_id);
$results['permissions'] = array(
'status' => $permissions_ok ? 'configured' : 'missing',
'message' => $permissions_ok ? 'Organization permissions configured' : 'Failed to configure organization permissions'
);
}
return $results;
}
/**
* Verify organization permissions on a folder
*/
private function verify_organization_permissions($folder_id) {
try {
$response = $this->auth->make_drive_api_request('GET', "files/{$folder_id}/permissions");
if (isset($response['permissions'])) {
foreach ($response['permissions'] as $permission) {
if (isset($permission['domain']) && $permission['domain'] === 'measurequick.com' &&
$permission['role'] === 'writer') {
return true;
}
}
}
return false;
} catch (Exception $e) {
$this->log_error("Failed to verify permissions on {$folder_id}: " . $e->getMessage());
return false;
}
}
/**
* Log info message
*/
private function log_info($message) {
if ($this->logger) {
$this->logger->info($message, 'Google Sheets Folders');
}
}
/**
* Log error message
*/
private function log_error($message) {
if ($this->logger) {
$this->logger->error($message, 'Google Sheets Folders');
}
}
}
?>