1<?php
2
3namespace dokuwiki\plugin\wordimport\docx;
4
5use splitbrain\PHPArchive\Zip;
6
7/**
8 * The main DOCX object
9 *
10 * This class is the main entry point for importing a DOCX file into DokuWiki. It handles extracting the
11 * document and offers access to its contents.
12 */
13class DocX
14{
15    /** @var string The temporary directory where the DOCX is extracted */
16    protected $tmpdir;
17    /** @var Relationships Relationship references*/
18    protected $relationships;
19    /** @var Numbering Numbering definitions for lists */
20    protected $numbering;
21    /** @var Styles Style definitions */
22    protected $styles;
23    /** @var Document The main document */
24    protected $document;
25    /** @var string|null The page id to which this docx is imported */
26    protected $pageId;
27    /** @var array The plugin configuration */
28    protected $config;
29
30    /**
31     * Create a new DOCX object
32     *
33     * @param string $docx path to the DOCX file
34     * @param array $config the plugin configuration
35     */
36    public function __construct(string $docx, array $config)
37    {
38        $zip = new Zip();
39        $zip->open($docx);
40
41        $this->tmpdir = io_mktmpdir();
42        $zip->extract($this->tmpdir);
43        $zip->close();
44
45        $this->config = $config;
46    }
47
48    /**
49     * Import the DOCX into DokuWiki
50     *
51     * @param string $pageid the page id to import the document into
52     * @throws \Exception
53     */
54    public function import($pageid)
55    {
56        if (auth_quickaclcheck(getNS($pageid) . ':*') < AUTH_DELETE) {
57            throw new \Exception('You do not have enough permission to import into this namespace');
58        }
59
60        $this->pageId = $pageid;
61        if (checklock($pageid)) throw new \Exception('page is currently locked');
62        lock($pageid);
63
64        $doc = $this->getDocument();
65        saveWikiText($pageid, (string)$doc, 'Imported from DOCX');
66
67        unlock($pageid);
68    }
69
70    /**
71     * Parse and access the document
72     *
73     * @return Document
74     */
75    public function getDocument(): Document
76    {
77        if (!$this->document) $this->document = new Document($this);
78        return $this->document;
79    }
80
81    /**
82     * Parse and access the list number definitions
83     *
84     * @return Numbering
85     * @internal
86     */
87    public function getNumbering(): Numbering
88    {
89        if (!$this->numbering) $this->numbering = new Numbering($this);
90        return $this->numbering;
91    }
92
93    /**
94     * Parse and access the relationships
95     *
96     * @return Relationships
97     * @internal
98     */
99    public function getRelationships(): Relationships
100    {
101        if (!$this->relationships) $this->relationships = new Relationships($this);
102        return $this->relationships;
103    }
104
105    /**
106     * Parse and access the style information
107     *
108     * @return Styles
109     * @internal
110     */
111    public function getStyles(): Styles
112    {
113        if (!$this->styles) $this->styles = new Styles($this);
114        return $this->styles;
115    }
116
117    /**
118     * The page id to which this docx is imported. Used for media imports
119     *
120     * Important: this will return null if this is not called within a import process
121     *
122     * @return string|null
123     * @internal
124     */
125    public function getPageId(): ?string
126    {
127        return $this->pageId;
128    }
129
130    /**
131     * Load a file from the extracted docx
132     *
133     * @param string $file document relative path to the file to load
134     * @return \SimpleXMLElement
135     * @throws \Exception when the file does not exist
136     * @internal
137     */
138    public function loadXMLFile($file)
139    {
140        $file = $this->getFilePath($file);
141        return simplexml_load_file($file);
142    }
143
144    /**
145     * Get the full path to a file within the doc
146     *
147     * @param string $relative document relative path
148     * @return string
149     * @throws \Exception when the file does not exist
150     * @internal
151     */
152    public function getFilePath($relative): string
153    {
154        $file = $this->tmpdir . '/' . $relative;
155
156        if (!file_exists($file)) {
157            throw new \Exception('File not found: ' . $file);
158        }
159
160        return $file;
161    }
162
163    /**
164     * Get a configuration value
165     *
166     * @param string $key
167     * @param mixed $default default value if the key is not set
168     * @return mixed
169     * @internal
170     */
171    public function getConf($key, $default = null)
172    {
173        return $this->config[$key] ?? $default;
174    }
175
176    /**
177     * Cleanup the temporary directory
178     */
179    public function __destruct()
180    {
181        io_rmdir($this->tmpdir, true);
182        if ($this->pageId) unlock($this->pageId);
183    }
184}
185