1<?php 2 3/** 4 * DokuWiki Plugin struct (Helper Component) 5 * 6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7 * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 8 */ 9 10use dokuwiki\plugin\struct\meta\AccessDataValidator; 11use dokuwiki\plugin\struct\meta\AccessTable; 12use dokuwiki\plugin\struct\meta\Assignments; 13use dokuwiki\plugin\struct\meta\Schema; 14use dokuwiki\plugin\struct\meta\StructException; 15 16/** 17 * The public interface for the struct plugin 18 * 19 * 3rd party developers should always interact with struct data through this 20 * helper plugin only. If additional interface functionality is needed, 21 * it should be added here. 22 * 23 * All functions will throw StructExceptions when something goes wrong. 24 * 25 * Remember to check permissions yourself! 26 */ 27class helper_plugin_struct extends DokuWiki_Plugin 28{ 29 /** 30 * Class names of renderers which should NOT render struct data. 31 * All descendants are also blacklisted. 32 */ 33 public const BLACKLIST_RENDERER = [ 34 'Doku_Renderer_metadata', 35 '\renderer_plugin_qc' 36 ]; 37 38 /** 39 * Get the structured data of a given page 40 * 41 * @param string $page The page to get data for 42 * @param string|null $schema The schema to use null for all 43 * @param int $time A timestamp if you want historic data 44 * @return array ('schema' => ( 'fieldlabel' => 'value', ...)) 45 * @throws StructException 46 */ 47 public function getData($page, $schema = null, $time = 0) 48 { 49 $page = cleanID($page); 50 if (!$time) { 51 $time = time(); 52 } 53 54 if (is_null($schema)) { 55 $assignments = Assignments::getInstance(); 56 $schemas = $assignments->getPageAssignments($page, false); 57 } else { 58 $schemas = array($schema); 59 } 60 61 $result = array(); 62 foreach ($schemas as $schema) { 63 $schemaData = AccessTable::getPageAccess($schema, $page, $time); 64 $result[$schema] = $schemaData->getDataArray(); 65 } 66 67 return $result; 68 } 69 70 /** 71 * Saves data for a given page (creates a new revision) 72 * 73 * If this call succeeds you can assume your data has either been saved or it was 74 * not necessary to save it because the data already existed in the wanted form or 75 * the given schemas are no longer assigned to that page. 76 * 77 * Important: You have to check write permissions for the given page before calling 78 * this function yourself! 79 * 80 * this duplicates a bit of code from entry.php - we could also fake post data and let 81 * entry handle it, but that would be rather unclean and might be problematic when multiple 82 * calls are done within the same request. 83 * 84 * @param string $page 85 * @param array $data ('schema' => ( 'fieldlabel' => 'value', ...)) 86 * @param string $summary 87 * @param string $summary 88 * @throws StructException 89 * @todo should this try to lock the page? 90 * 91 * 92 */ 93 public function saveData($page, $data, $summary = '', $minor = false) 94 { 95 $page = cleanID($page); 96 $summary = trim($summary); 97 if (!$summary) $summary = $this->getLang('summary'); 98 99 if (!page_exists($page)) throw new StructException("Page does not exist. You can not attach struct data"); 100 101 // validate and see if anything changes 102 $valid = AccessDataValidator::validateDataForPage($data, $page, $errors); 103 if ($valid === false) { 104 throw new StructException("Validation failed:\n%s", join("\n", $errors)); 105 } 106 if (!$valid) return; // empty array when no changes were detected 107 108 $newrevision = self::createPageRevision($page, $summary, $minor); 109 110 // save the provided data 111 $assignments = Assignments::getInstance(); 112 foreach ($valid as $v) { 113 $v->saveData($newrevision); 114 // make sure this schema is assigned 115 $assignments->assignPageSchema($page, $v->getAccessTable()->getSchema()->getTable()); 116 } 117 } 118 119 /** 120 * Save lookup data row 121 * 122 * @param AccessTable $access the table into which to save the data 123 * @param array $data data to be saved in the form of [columnName => 'data'] 124 */ 125 public function saveLookupData(AccessTable $access, $data) 126 { 127 if (!$access->getSchema()->isEditable()) { 128 throw new StructException('lookup save error: no permission for schema'); 129 } 130 $validator = $access->getValidator($data); 131 if (!$validator->validate()) { 132 throw new StructException("Validation failed:\n%s", implode("\n", $validator->getErrors())); 133 } 134 if (!$validator->saveData()) { 135 throw new StructException('No data saved'); 136 } 137 } 138 139 /** 140 * Creates a new page revision with the same page content as before 141 * 142 * @param string $page 143 * @param string $summary 144 * @param bool $minor 145 * @return int the new revision 146 */ 147 public static function createPageRevision($page, $summary = '', $minor = false) 148 { 149 $summary = trim($summary); 150 // force a new page revision @see action_plugin_struct_entry::handle_pagesave_before() 151 $GLOBALS['struct_plugin_force_page_save'] = true; 152 saveWikiText($page, rawWiki($page), $summary, $minor); 153 unset($GLOBALS['struct_plugin_force_page_save']); 154 $file = wikiFN($page); 155 clearstatcache(false, $file); 156 return filemtime($file); 157 } 158 159 /** 160 * Get info about existing schemas 161 * 162 * @param string|null $schema the schema to query, null for all 163 * @return Schema[] 164 * @throws StructException 165 */ 166 public static function getSchema($schema = null) 167 { 168 if (is_null($schema)) { 169 $schemas = Schema::getAll(); 170 } else { 171 $schemas = array($schema); 172 } 173 174 $result = array(); 175 foreach ($schemas as $table) { 176 $result[$table] = new Schema($table); 177 } 178 return $result; 179 } 180 181 /** 182 * Returns all pages known to the struct plugin 183 * 184 * That means all pages that have or had once struct data saved 185 * 186 * @param string|null $schema limit the result to a given schema 187 * @return array (page => (schema => true), ...) 188 * @throws StructException 189 */ 190 public function getPages($schema = null) 191 { 192 $assignments = Assignments::getInstance(); 193 return $assignments->getPages($schema); 194 } 195 196 /** 197 * Returns decoded JSON value or throws exception 198 * when passed parameter is not JSON 199 * 200 * @param string $value 201 * @return mixed 202 * @throws StructException 203 */ 204 public static function decodeJson($value) 205 { 206 if (!empty($value) && $value[0] !== '[') throw new StructException('Lookup expects JSON'); 207 return json_decode($value); 208 } 209} 210