/** * Save the sortfield for a given Page * * @param Page * @return bool * */ public function save(Page $page) { if (!$page->id) { return; } if (!$page->isChanged('sortfield')) { return; } $sortfield = $this->fuel('db')->escape_string($this->encode($page->sortfield)); if ($sortfield == 'sort' || !$sortfield) { return $this->delete($page); } $sql = "INSERT INTO pages_sortfields (pages_id, sortfield) " . "VALUES({$page->id}, '{$sortfield}') " . "ON DUPLICATE KEY UPDATE sortfield=VALUES(sortfield)"; return $this->fuel('db')->query($sql) != false; }
/** * Save the sortfield for a given Page * * @param Page * @return bool * */ public function save(Page $page) { if (!$page->id) { return false; } if (!$page->isChanged('sortfield')) { return true; } $page_id = (int) $page->id; $database = $this->wire('database'); $sortfield = $this->encode($page->sortfield); if ($sortfield == 'sort' || !$sortfield) { return $this->delete($page); } $sql = "INSERT INTO pages_sortfields (pages_id, sortfield) " . "VALUES(:page_id, :sortfield) " . "ON DUPLICATE KEY UPDATE sortfield=VALUES(sortfield)"; $query = $database->prepare($sql); $query->bindValue(":page_id", $page_id, PDO::PARAM_INT); $query->bindValue(":sortfield", $sortfield, PDO::PARAM_STR); $result = $query->execute(); return $result; }
function testIsChanged() { $page = $this->objFromFixture('Page', 'home'); $page->Title = 'Home-Changed'; $page->ShowInMenus = true; // type change only, database stores "1" $this->assertTrue($page->isChanged('Title', 1)); $this->assertTrue($page->isChanged('Title', 2)); $this->assertTrue($page->isChanged('ShowInMenus', 1)); $this->assertFalse($page->isChanged('ShowInMenus', 2)); $this->assertFalse($page->isChanged('Content', 1)); $this->assertFalse($page->isChanged('Content', 2)); $newPage = new Page(); $newPage->Title = "New Page Title"; $this->assertTrue($newPage->isChanged('Title', 1)); $this->assertTrue($newPage->isChanged('Title', 2)); $this->assertFalse($newPage->isChanged('Content', 1)); $this->assertFalse($newPage->isChanged('Content', 2)); $newPage->write(); $this->assertFalse($newPage->isChanged('Title', 1)); $this->assertFalse($newPage->isChanged('Title', 2)); $this->assertFalse($newPage->isChanged('Content', 1)); $this->assertFalse($newPage->isChanged('Content', 2)); $page = $this->objFromFixture('Page', 'home'); $page->Title = null; $this->assertTrue($page->isChanged('Title', 1)); $this->assertTrue($page->isChanged('Title', 2)); /* Test when there's not field provided */ $page = $this->objFromFixture('Page', 'home'); $page->Title = "New Page Title"; $this->assertTrue($page->isChanged()); $page->write(); $this->assertFalse($page->isChanged()); }
/** * Save the given field from page * * Possible template method: If overridden, children should NOT call this parent method. * * @param Page $page Page object to save. * @param Field $field Field to retrieve from the page. * @return bool True on success, false on DB save failure. * */ public function ___savePageField(Page $page, Field $field) { if (!$page->id) { throw new WireException("Unable to save to '{$field->table}' for page that doesn't exist in pages table"); } if (!$field->id) { throw new WireException("Unable to save to '{$field->table}' for field that doesn't exist in fields table"); } // if this field hasn't changed since it was loaded, don't bother executing the save if (!$page->isChanged($field->name)) { return true; } $value = $page->get($field->name); // if the value is the same as the default, then remove the field from the database because it's redundant if ($value === $this->getDefaultValue($page, $field)) { return $this->deletePageField($page, $field); } $value = $this->sleepValue($page, $field, $value); if (is_array($value)) { $sql1 = "INSERT INTO `{$field->table}` (pages_id"; $sql2 = "VALUES('{$page->id}'"; $sql3 = "ON DUPLICATE KEY UPDATE "; foreach ($value as $k => $v) { $sql1 .= ",{$k}"; $sql2 .= ",'" . $this->db->escape_string($v) . "'"; $sql3 .= "{$k}=VALUES({$k}), "; } $sql = "{$sql1}) {$sql2}) " . rtrim($sql3, ', '); } else { $value = $this->db->escape_string($value); $sql = "INSERT INTO `{$field->table}` (pages_id, data) " . "VALUES('{$page->id}', '{$value}') " . "ON DUPLICATE KEY UPDATE data=VALUES(data)"; } $result = $this->db->query($sql); return $result; }
/** * Per the Fieldtype interface, Save the given Field from the given Page to the database * * Because the number of values may have changed, this method plays it safe and deletes all the old values * and reinserts them as new. * * @param Page $page * @param Field $field * @return bool * @throws WireException on failure * */ public function ___savePageField(Page $page, Field $field) { if (!$page->id || !$field->id) { return false; } $database = $this->wire('database'); $values = $page->get($field->name); $schema = array(); if (is_object($values)) { if (!$values->isChanged() && !$page->isChanged($field->name)) { return true; } } else { if (!$page->isChanged($field->name)) { return true; } } $values = $this->sleepValue($page, $field, $values); $table = $database->escapeTable($field->table); $page_id = (int) $page->id; // since we don't manage IDs of existing values for multi fields, we delete the existing data and insert all of it again $query = $database->prepare("DELETE FROM `{$table}` WHERE pages_id=:page_id"); // QA $query->bindValue(":page_id", $page_id, PDO::PARAM_INT); $query->execute(); if (count($values)) { // get first value to find key definition $value = reset($values); // if the first value is not an associative (key indexed) array, then force it to be with 'data' as the key. // this is to allow for this method to be able to save fields that have more than just a 'data' field, // even though most instances will probably just use only the data field if (is_array($value)) { $keys = array_keys($value); foreach ($keys as $k => $v) { $keys[$k] = $database->escapeTableCol($v); } } else { $keys = array('data'); } $sql = "INSERT INTO `{$table}` (pages_id, sort, `" . implode('`, `', $keys) . "`) VALUES"; $sort = 0; // cycle through the values to generate the query foreach ($values as $value) { $sql .= "({$page_id}, {$sort}, "; // if the value is not an associative array, then force it to be one if (!is_array($value)) { $value = array('data' => $value); } // cycle through the keys, which represent DB fields (i.e. data, description, etc.) and generate the insert query foreach ($keys as $key) { $v = isset($value[$key]) ? $value[$key] : null; if (is_null($v)) { if (empty($schema)) { $schema = $this->getDatabaseSchema($field); } $sql .= isset($schema[$key]) && stripos($schema[$key], ' DEFAULT NULL') ? "NULL, " : "'', "; } else { $sql .= "'" . $database->escapeStr("{$v}") . "', "; } } $sql = rtrim($sql, ", ") . "), "; $sort++; } $sql = rtrim($sql, ", "); $query = $database->prepare($sql); try { $result = $query->execute(); } catch (Exception $e) { $msg = $e->getMessage(); if ($this->wire('config')->debug && $this->wire('config')->advanced) { $msg .= "\n{$sql}"; } throw new WireException($msg); } return $result; } return true; }
/** * Save the given field from page * * Possible template method: If overridden, children should NOT call this parent method. * * @param Page $page Page object to save. * @param Field $field Field to retrieve from the page. * @return bool True on success, false on DB save failure. * @throws WireException * */ public function ___savePageField(Page $page, Field $field) { if (!$page->id) { throw new WireException("Unable to save to '{$field->table}' for page that doesn't exist in pages table"); } if (!$field->id) { throw new WireException("Unable to save to '{$field->table}' for field that doesn't exist in fields table"); } // if this field hasn't changed since it was loaded, don't bother executing the save if (!$page->isChanged($field->name)) { return true; } $database = $this->wire('database'); $value = $page->get($field->name); // if the value is the same as the default, then remove the field from the database because it's redundant if ($value === $this->getBlankValue($page, $field)) { return $this->deletePageField($page, $field); } $value = $this->sleepValue($page, $field, $value); $page_id = (int) $page->id; $table = $database->escapeTable($field->table); $schema = array(); if (is_array($value)) { $sql1 = "INSERT INTO `{$table}` (pages_id"; $sql2 = "VALUES('{$page_id}'"; $sql3 = "ON DUPLICATE KEY UPDATE "; foreach ($value as $k => $v) { $k = $database->escapeCol($k); $sql1 .= ",`{$k}`"; if (is_null($v)) { // check if schema explicitly allows NULL if (empty($schema)) { $schema = $this->getDatabaseSchema($field); } $sql2 .= isset($schema[$k]) && stripos($schema[$k], ' DEFAULT NULL') ? ",NULL" : ",''"; } else { $v = $database->escapeStr($v); $sql2 .= ",'{$v}'"; } $sql3 .= "`{$k}`=VALUES(`{$k}`), "; } $sql = "{$sql1}) {$sql2}) " . rtrim($sql3, ', '); } else { if (is_null($value)) { // check if schema explicitly allows NULL $schema = $this->getDatabaseSchema($field); $value = isset($schema[$k]) && stripos($schema[$k], ' DEFAULT NULL') ? "NULL" : "''"; } else { $value = "'" . $database->escapeStr($value) . "'"; } $sql = "INSERT INTO `{$table}` (pages_id, data) " . "VALUES('{$page_id}', {$value}) " . "ON DUPLICATE KEY UPDATE data=VALUES(data)"; } $query = $database->prepare($sql); $result = $query->execute(); return $result; }
/** * Per the Fieldtype interface, Save the given Field from the given Page to the database * * Because the number of values may have changed, this method plays it safe and deletes all the old values * and reinserts them as new. * * @param Page $page * @param Field $field * @return bool * */ public function ___savePageField(Page $page, Field $field) { if (!$page->id || !$field->id) { return false; } $values = $page->get($field->name); // if(!$values) return false; /* if($this->config->debug) { if($page->isChanged($field->name)) { $this->message("Page {$page->id} reports that '{$field->name}' has changed"); } if($values->isChanged()) { $this->message("Values changed for field: {$field->name}"); foreach($values->getChanges() as $key => $change) $this->message("{$field->name}: $key: $change"); } } */ if (is_object($values)) { if (!$values->isChanged() && !$page->isChanged($field->name)) { return true; } } else { if (!$page->isChanged($field->name)) { return true; } } $values = $this->sleepValue($page, $field, $values); // since we don't manage IDs of existing values for multi fields, we delete the existing data and insert all of it again $this->db->query("DELETE FROM `{$field->table}` WHERE pages_id={$page->id}"); if (count($values)) { // get first value to find key definition $value = reset($values); // if the first value is not an associative (key indexed) array, then force it to be with 'data' as the key. // this is to allow for this method to be able to save fields that have more than just a 'data' field, // even though most instances will probably just use only the data field if (is_array($value)) { $keys = array_keys($value); } else { $keys = array('data'); } $sql = "INSERT INTO `{$field->table}` (pages_id, sort, " . implode(', ', $keys) . ") VALUES"; $sort = 0; // cycle through the values to generate the query foreach ($values as $value) { $sql .= "({$page->id}, {$sort}, "; // if the value is not an associative array, then force it to be one if (!is_array($value)) { $value = array('data' => $value); } // cycle through the keys, which represent DB fields (i.e. data, description, etc.) and generate the insert query foreach ($keys as $key) { $v = $value[$key]; $sql .= "'" . $this->db->escape_string("{$v}") . "', "; } $sql = rtrim($sql, ", ") . "), "; $sort++; } $sql = rtrim($sql, ", "); $result = $this->db->query($sql); return $result; } return true; }
/** * Save individual Page fields and supporting actions * * triggers hooks: saved, added, moved, renamed, templateChanged * * @param Page $page * @param bool $isNew * @param array $options * @return bool * */ protected function savePageFinish(Page $page, $isNew, array $options) { $changes = $page->getChanges(); $changesValues = $page->getChanges(true); // update children counts for current/previous parent if ($isNew) { $page->parent->numChildren++; } else { if ($page->parentPrevious && $page->parentPrevious->id != $page->parent->id) { $page->parentPrevious->numChildren--; $page->parent->numChildren++; } } // if page hasn't changed, don't continue further if (!$page->isChanged() && !$isNew) { $this->debugLog('save', '[not-changed]', true); $this->saved($page, array()); return true; } // if page has a files path (or might have previously), trigger filesManager's save if (PagefilesManager::hasPath($page)) { $page->filesManager->save(); } // disable outputFormatting and save state $of = $page->of(); $page->of(false); // when a page is statusCorrupted, it records what fields are corrupted in _statusCorruptedFields array $corruptedFields = $page->hasStatus(Page::statusCorrupted) ? $page->_statusCorruptedFields : array(); // save each individual Fieldtype data in the fields_* tables foreach ($page->fieldgroup as $field) { if (isset($corruptedFields[$field->name])) { continue; } // don't even attempt save of corrupted field if (!$field->type) { continue; } try { $field->type->savePageField($page, $field); } catch (Exception $e) { $error = sprintf($this->_('Error saving field "%s"'), $field->name) . ' - ' . $e->getMessage(); $this->trackException($e, true, $error); } } // return outputFormatting state $page->of($of); if (empty($page->template->sortfield)) { $this->sortfields->save($page); } if ($options['resetTrackChanges']) { $page->resetTrackChanges(); } // determine whether we'll trigger the added() hook if ($isNew) { $page->setIsNew(false); $triggerAddedPage = $page; } else { $triggerAddedPage = null; } // check for template changes if ($page->templatePrevious && $page->templatePrevious->id != $page->template->id) { // the template was changed, so we may have data in the DB that is no longer applicable // find unused data and delete it foreach ($page->templatePrevious->fieldgroup as $field) { if ($page->template->fieldgroup->has($field)) { continue; } $field->type->deletePageField($page, $field); $this->message("Deleted field '{$field}' on page {$page->url}", Notice::debug); } } if ($options['uncacheAll']) { $this->uncacheAll($page); } // determine whether the pages_access table needs to be updated so that pages->find() // operations can be access controlled. if ($isNew || $page->parentPrevious || $page->templatePrevious) { new PagesAccess($page); } // lastly determine whether the pages_parents table needs to be updated for the find() cache // and call upon $this->saveParents where appropriate. if ($page->parentPrevious && $page->numChildren > 0) { // page is moved and it has children $this->saveParents($page->id, $page->numChildren); if ($page->parent->numChildren == 1) { $this->saveParents($page->parent_id, $page->parent->numChildren); } } else { if ($page->parentPrevious && $page->parent->numChildren == 1 || $isNew && $page->parent->numChildren == 1 || $page->forceSaveParents) { // page is moved and is the first child of it's new parent // OR page is NEW and is the first child of it's parent // OR $page->forceSaveParents is set (debug/debug, can be removed later) $this->saveParents($page->parent_id, $page->parent->numChildren); } } if ($page->parentPrevious && $page->parentPrevious->numChildren == 0) { // $page was moved and it's previous parent is now left with no children, this ensures the old entries get deleted $this->saveParents($page->parentPrevious->id, 0); } // trigger hooks $this->saved($page, $changes, $changesValues); if ($triggerAddedPage) { $this->added($triggerAddedPage); } if ($page->namePrevious && $page->namePrevious != $page->name) { $this->renamed($page); } if ($page->parentPrevious) { $this->moved($page); } if ($page->templatePrevious) { $this->templateChanged($page); } if (in_array('status', $changes)) { $this->statusChanged($page); } $this->debugLog('save', $page, true); return true; }