PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.24
MxChat – AI Chatbot & Content Generation for WordPress v3.2.24
3.2.25 3.2.24 3.2.23 3.2.22 3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 All 156 releases
← All changes | includes/class-mxchat-word-handler.php +38 -23 3.2.17 → 3.2.24 View file →
@@ -42,12 +42,12 @@
42 42 $original_filename = sanitize_text_field($file['name']);
43 43
44 44 // Update session owner if it changed (e.g. IP changed due to network switch)
45 45 $current_user_identifier = MxChat_User::mxchat_get_user_identifier();
46 - $session_owner = get_option("mxchat_session_owner_{$session_id}");
46 + $session_owner = MxChat_Session_Store::get($session_id, 'owner');
47 47
48 48 if (!$session_owner || $session_owner !== $current_user_identifier) {
49 - update_option("mxchat_session_owner_{$session_id}", $current_user_identifier, 'no');
49 + MxChat_Session_Store::set($session_id, 'owner', $current_user_identifier);
50 50 }
51 51
52 52 // Check file type
53 53 $allowed_types = array(
@@ -98,8 +98,33 @@
98 98 ]);
99 99 }
100 100
101 101 /**
102 + * Extract plain text from a .docx file. THE one .docx parser — the
103 + * visitor-facing toolbar upload (via mxchat_process_word_document) and the
104 + * admin knowledge importer (plan 0485e5) both call this; do not fork a
105 + * second copy. Returns the cleaned text, or false on unreadable/empty
106 + * files (not a Zip, no word/document.xml, no text content).
107 + */
108 + public static function extract_docx_text($file_path) {
109 + try {
110 + $zip = new ZipArchive();
111 + if ($zip->open($file_path) !== true) {
112 + return false;
113 + }
114 + $content = $zip->getFromName('word/document.xml');
115 + $zip->close();
116 + if ($content === false) {
117 + return false;
118 + }
119 + $text = self::clean_word_xml($content);
120 + return $text === '' ? false : $text;
121 + } catch (Exception $e) {
122 + return false;
123 + }
124 + }
125 +
126 + /**
102 127 * Process Word document and generate embeddings
103 128 */
104 129 private function mxchat_process_word_document($file_path) {
105 130 // Get the maximum number of pages allowed from admin settings
@@ -105,24 +130,13 @@
105 130 // Get the maximum number of pages allowed from admin settings
106 131 $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69; // Use same setting as PDF
107 132
108 133 try {
109 - $zip = new ZipArchive();
110 - if ($zip->open($file_path) !== true) {
134 + $text = self::extract_docx_text($file_path);
135 + if ($text === false) {
111 136 return false;
112 137 }
113 138
114 - // Extract main document content
115 - $content = $zip->getFromName('word/document.xml');
116 - $zip->close();
117 -
118 - if ($content === false) {
119 - return false;
120 - }
121 -
122 - // Clean up the content
123 - $text = $this->mxchat_clean_word_content($content);
124 -
125 139 // Count pages (roughly estimate based on paragraphs)
126 140 $paragraphs = explode("\n\n", $text);
127 141 $estimated_pages = ceil(count($paragraphs) / 3); // Assume ~3 paragraphs per page
128 142
@@ -159,25 +173,26 @@
159 173 return false;
160 174 }
161 175 }
162 176 /**
163 - * Clean Word XML content
177 + * Clean Word XML content (word/document.xml) to plain text.
178 + * Static so both callers of extract_docx_text share one implementation.
164 179 */
165 - private function mxchat_clean_word_content($content) {
180 + public static function clean_word_xml($content) {
166 181 // Remove XML namespaces
167 182 $content = preg_replace('/xmlns[^=]*="[^"]*"/i', '', $content);
168 -
183 +
169 184 // Convert Word XML elements to text
170 185 $content = str_replace('</w:p>', "\n", $content);
171 186 $content = str_replace('</w:tr>', "\n", $content);
172 -
187 +
173 188 // Strip remaining XML tags
174 189 $content = strip_tags($content);
175 -
190 +
176 191 // Clean up whitespace
177 192 $content = preg_replace('/\s+/', ' ', $content);
178 193 $content = preg_replace('/\n\s*\n/', "\n\n", $content);
179 -
194 +
180 195 return trim($content);
181 196 }
182 197
183 198 /**
@@ -227,11 +242,11 @@
227 242 // history endpoint (plan-mxchat-20260731-d42bec). Possession of the session
228 243 // id is the credential, so this records/refreshes the owner rather than
229 244 // refusing a mismatch; see the fuller note in handle_pdf_remove().
230 245 $current_user_identifier = MxChat_User::mxchat_get_user_identifier();
231 - $session_owner = get_option("mxchat_session_owner_{$session_id}");
246 + $session_owner = MxChat_Session_Store::get($session_id, 'owner');
232 247 if (!$session_owner || $session_owner !== $current_user_identifier) {
233 - update_option("mxchat_session_owner_{$session_id}", $current_user_identifier, 'no');
248 + MxChat_Session_Store::set($session_id, 'owner', $current_user_identifier);
234 249 }
235 250
236 251 $word_path = get_transient('mxchat_word_url_' . $session_id);
237 252