function handle($params) { if (!defined('DISABLE_reCAPTCHA')) { define('DISABLE_reCAPTCHA', 1); } import('Dataface/QuickForm.php'); Dataface_QuickForm::$TRACK_SUBMIT = false; $app = Dataface_Application::getInstance(); $query = $app->getQuery(); $errors = null; try { if (!@$_POST['-table']) { throw new Exception("No table specified"); } $table = $_POST['-table']; $rec = new Dataface_Record($table, array()); $tableObj = $rec->_table; $fields = array(); if (!$rec->checkPermission('new')) { throw new Exception("Failed to insert record. Permission denied"); } foreach ($_POST as $k => $v) { if ($k[0] == '-') { continue; } $fields[] = $k; $rec->setValue($k, $v); if (!$rec->checkPermission('new', array('field' => $k))) { throw new Exception(sprintf("Failed to insert record because you do not have permission to insert data into the %s column", $k)); } } $form = df_create_new_record_form($table, $fields); $form->_flagSubmitted = true; $res = $form->validate(); if (!$res) { $errors = $form->_errors; throw new Exception('Validation error', REST_INSERT_VALIDATION_ERROR); } $res = $rec->save(null, true); if (PEAR::isError($res)) { throw new Exception("Failed to insert record due to a server error: " . $res->getMessage(), 500); } $out = array(); $vals = $rec->strvals(); foreach ($vals as $k => $v) { if ($rec->checkPermission('view')) { $out[$k] = $v; } } $this->out(array('code' => 200, 'message' => 'Record successfully inserted', 'record' => $out)); exit; } catch (Exception $ex) { $this->out(array('code' => $ex->getCode(), 'message' => $ex->getMessage(), 'errors' => $errors)); exit; } }
/** * Builds an SQL query to copy the given record. This honours permissions * and will only copy columns for which 'view' access is available in the * source record and 'edit' access is available in the destination record. * * Individual column failures (due to permissions) are recorded in the * $warnings variable of this class. It will be an array of Dataface_Error * objects. * * @param Dataface_Record $record The record being copied. * @param array $valls Values that should be placed in the copied version. * @param boolean $force If true this will perform the copy despite individual * column warnings. * @returns string The SQL query to copy the record. */ function buildCopyQuery($record, $vals = array(), $force = true) { $dummy = new Dataface_Record($record->_table->tablename, $vals); if (!$record->checkPermission('view') || !$dummy->checkPermission('edit')) { return Dataface_Error::permissionDenied("Failed to copy record '" . $record->getTitle() . "' because of insufficient permissions."); } $copy_fields = array_keys($record->_table->fields()); // Go through each field and see if we have copy permission. // Copy permission is two-fold: 1- make sure the source is viewable // 2- make sure the destination is editable. $failed = false; foreach ($copy_fields as $key => $fieldname) { if (!$record->checkPermission('view', array('field' => $fieldname)) || !$dummy->checkPermission('edit', array('field' => $fieldname))) { $this->warnings[] = Dataface_Error::permissionDenied("The field '{$fieldname}' could not be copied for record '" . $record->getTitle() . "' because of insufficient permissions."); unset($copy_fields[$key]); $failed = true; } } // If we are not forcing completion, any failures will result in cancellation // of the copy. if (!$force and $failed) { return Dataface_Error::permissionDenied("Failed to copy the record '" . $record->getTitle() . "' due to insufficient permissions on one or more of the columns."); } // We don't copy auto increment fields. $auto_inc_field = $record->_table->getAutoIncrementField(); if ($auto_inc_field) { $key = array_search($auto_inc_field, $copy_fields); if ($key !== false) { unset($copy_fields[$key]); } } // Now we can build the query. $sql = array(); $sql[] = "insert into `" . $record->_table->tablename . "`"; $sql[] = "(`" . implode('`,`', $copy_fields) . "`)"; $copy_values = array(); foreach ($copy_fields as $key => $val) { if (isset($vals[$val])) { $copy_values[$key] = "'" . addslashes($dummy->getSerializedValue($val)) . "' as `{$val}`"; } else { $copy_values[$key] = "`" . $val . "`"; } } $sql[] = "select " . implode(', ', $copy_values) . " from `" . $record->_table->tablename . "`"; $qb = new Dataface_QueryBuilder($record->_table->tablename); $keys = array_keys($record->_table->keys()); $q = array(); foreach ($keys as $key_fieldname) { $q[$key_fieldname] = $record->strval($key_fieldname); } $where = $qb->_where($q); $where = $qb->_secure($where); $sql[] = $where; return implode(' ', $sql); }
function handle($params) { $app = Dataface_Application::getInstance(); $query = $app->getQuery(); try { if (!@$_POST['-table']) { throw new Exception("No table was specified"); } $vals = array(); foreach ($query as $k => $v) { if ($k and $k[0] != '-') { $vals[$k] = $v; } } $record = new Dataface_Record($_POST['-table'], array()); $record->setValues($vals); if (!$record->checkPermission('ajax_save')) { throw new Exception("Permission Denied", 502); } $res = $record->save(null, true); if (PEAR::isError($res)) { error_log($res->getMessage(), $res->getCode()); throw new Exception("Failed to save record due to a server error. See log for details."); } $this->out(array('code' => 200, 'message' => 'Successfully inserted record.', 'recordId' => $record->getId())); } catch (Exception $ex) { $this->out(array('code' => $ex->getCode(), 'message' => $ex->getMessage())); } }
/** * Deletes a record from the database. * @param Dataface_Record $record Dataface_Record object to be deleted. * @param boolean $secure Whether to check permissions. * @returns mixed true if successful, or PEAR_Error if failed. */ function delete(&$record, $secure = false) { if ($secure && !$record->checkPermission('delete')) { // Use security to check to see if we are allowed to delete this // record. return Dataface_Error::permissionDenied(df_translate('scripts.Dataface.IO.delete.PERMISSION_DENIED', 'Could not delete record "' . $record->getTitle() . '" from table "' . $record->_table->tablename . '" because you have insufficient permissions.', array('title' => $record->getTitle(), 'table' => $record->_table->tablename))); } $builder = new Dataface_QueryBuilder($this->_table->tablename); if ($this->fireTriggers) { $res = $this->fireBeforeDelete($record); if (PEAR::isError($res)) { return $res; } } // do the deleting $keys =& $record->_table->keys(); if (!$keys || count($keys) == 0) { trigger_error(df_translate('scripts.Dataface.IO.delete.ERROR_NO_PRIMARY_KEY', 'Could not delete record from table "' . $record->_table->tablename . '" because no primary key was defined.', array('tablename' => $record->_table->tablename))); exit; } $query = array(); foreach (array_keys($keys) as $key) { if (!$record->strval($key)) { return PEAR::raiseError(Dataface_LanguageTool::translate('Could not delete record because missing keys', 'Could not delete record ' . $record->getTitle() . ' because not all of the keys were included.', array('title' => $record->getTitle(), 'key' => $key)), DATAFACE_E_DELETE_FAILED); } $query[$key] = '=' . $record->strval($key); } $sql = $builder->delete($query); if (PEAR::isError($sql)) { return $sql; } //$res = mysql_query($sql); $res = $this->dbObj->query($sql, null, $this->lang); if (!$res || PEAR::isError($res)) { if (PEAR::isError($res)) { $msg = $res->getMessage(); } else { $msg = mysql_error(df_db()); } return PEAR::raiseError(Dataface_LanguageTool::translate('Failed to delete record. SQL error', 'Failed to delete record ' . $record->getTitle() . ' because of an sql error. ' . mysql_error(df_db()), array('title' => $record->getTitle(), 'sql' => $sql, 'mysql_error' => $msg)), DATAFACE_E_DELETE_FAILED); } $parentIO =& $this->getParentIO(); if (isset($parentIO)) { $parentRecord =& $record->getParentRecord(); if (isset($parentRecord)) { $res = $parentIO->delete($parentRecord, $secure); if (PEAR::isError($res)) { return $res; } } } if ($this->fireTriggers) { $res2 = $this->fireAfterDelete($record); if (PEAR::isError($res2)) { return $res2; } } self::touchTable($this->_table->tablename); return $res; }
function saveTransients(Dataface_Record $record, $keys = null, $tablename = null, $secure = false) { $app = Dataface_Application::getInstance(); // Now we take care of the transient relationship fields. // Transient relationship fields aren't actually stored in the record // itself, they are stored as related records. foreach ($record->_table->transientFields() as $tfield) { if (!isset($tfield['relationship'])) { continue; } if (!$record->valueChanged($tfield['name'])) { continue; } $trelationship =& $record->_table->getRelationship($tfield['relationship']); if (!$trelationship or PEAR::isError($trelationship)) { // We couldn't find the specified relationship. //$record->vetoSecurity = $oldVeto; return $trelationship; } $orderCol = $trelationship->getOrderColumn(); if (PEAR::isError($orderCol)) { $orderCol = null; } $tval = $record->getValue($tfield['name']); if ($tfield['widget']['type'] == 'grid') { $tval_existing = array(); $tval_new = array(); $tval_new_existing = array(); $torder = 0; foreach ($tval as $trow) { if (!is_array($trow)) { continue; } $trow['__order__'] = $torder++; if (isset($trow['__id__']) and preg_match('/^new:/', $trow['__id__'])) { $tval_new_existing[] = $trow; } else { if (isset($trow['__id__']) and $trow['__id__'] != 'new') { $tval_existing[$trow['__id__']] = $trow; } else { if (isset($trow['__id__']) and $trow['__id__'] == 'new') { $tval_new[] = $trow; } } } } // The transient field was loaded so we can go about saving the // changes/ $trecords =& $record->getRelatedRecordObjects($tfield['relationship'], 'all'); if (!is_array($trecords) or PEAR::isError($trecords)) { error_log('Failed to get related records for record ' . $record->getId() . ' in its relationship ' . $tfield['relationship']); unset($tval); unset($orderCol); unset($tval_new); unset($torder); unset($trelationship); unset($tval_existing); continue; } // Update the existing records in the relationship. // We use the __id__ parameter in each row for this. //echo "About to save related records"; foreach ($trecords as $trec) { $tid = $trec->getId(); if (isset($tval_existing[$tid])) { $tmp = new Dataface_RelatedRecord($trec->_record, $tfield['relationship'], $trec->getValues()); $tmp->setValues($tval_existing[$tid]); $changed = false; foreach ($tval_existing[$tid] as $k1 => $v1) { if ($tmp->isDirty($k1)) { $changed = true; break; } } if ($changed) { $trec->setValues($tval_existing[$tid]); if ($orderCol) { $trec->setValue($orderCol, $tval_existing[$tid]['__order__']); } //echo "Saving ";print_r($trec->vals()); $res_t = $trec->save($this->lang, $secure); if (PEAR::isError($res_t)) { return $res_t; error_log('Failed to save related record ' . $trec->getId() . ' while saving transient field ' . $tfield['name'] . ' in record ' . $record->getId() . '. The error returned was : ' . $res_t->getMessage()); } } else { if ($orderCol and $record->checkPermission('reorder_related_records', array('relationship' => $tfield['relationship']))) { $trec->setValue($orderCol, $tval_existing[$tid]['__order__']); $res_t = $trec->save($this->lang, false); // we don't need this to be secure if (PEAR::isError($res_t)) { return $res_t; error_log('Failed to save related record ' . $trec->getId() . ' while saving transient field ' . $tfield['name'] . ' in record ' . $record->getId() . '. The error returned was : ' . $res_t->getMessage()); } } } unset($tmp); } else { } unset($trec); unset($tid); unset($res_t); } // Now add new records (specified by __id__ field being 'new' foreach ($tval_new as $tval_to_add) { $temp_rrecord = new Dataface_RelatedRecord($record, $tfield['relationship'], array()); $temp_rrecord->setValues($tval_to_add); if ($orderCol) { $temp_rrecord->setValue($orderCol, $tval_to_add['__order__']); } $res_t = $this->addRelatedRecord($temp_rrecord, $secure); if (PEAR::isError($res_t)) { error_log('Failed to save related record ' . $temp_rrecord->getId() . ' while saving transient field ' . $tfield['name'] . ' in record ' . $record->getId() . '. The error returned was : ' . $res_t->getMessage()); } unset($temp_rrecord); unset($res_t); } // Now add new existing records (specified by __id__ field being 'new:<recordid>' foreach ($tval_new_existing as $tval_to_add) { $tid = preg_replace('/^new:/', '', $tval_to_add['__id__']); $temp_record = df_get_record_by_id($tid); if (PEAR::isError($temp_record)) { return $temp_record; } if (!$temp_record) { return PEAR::raiseError("Failed to load existing record with ID {$tid}."); } $temp_rrecord = new Dataface_RelatedRecord($record, $tfield['relationship'], $temp_record->vals()); $temp_rrecord->setValues($tval_to_add); if ($orderCol) { $temp_rrecord->setValue($orderCol, $tval_to_add['__order__']); } $res_t = $this->addExistingRelatedRecord($temp_rrecord, $secure); if (PEAR::isError($res_t)) { error_log('Failed to save related record ' . $temp_rrecord->getId() . ' while saving transient field ' . $tfield['name'] . ' in record ' . $record->getId() . '. The error returned was : ' . $res_t->getMessage()); } unset($temp_rrecord); unset($res_t); } // Now we delete the records that were deleted // we use the __deleted__ field. if (isset($tval['__deleted__']) and is_array($tval['__deleted__']) and $trelationship->supportsRemove()) { $tdelete_record = ($trelationship->isOneToMany() and !$trelationship->supportsAddExisting()); // If it supports add existing, then we shouldn't delete the entire record. Just remove it // from the relationship. foreach ($tval['__deleted__'] as $del_id) { if ($del_id == 'new') { continue; } $drec = Dataface_IO::getByID($del_id); if (PEAR::isError($drec) or !$drec) { unset($drec); continue; } $mres = $this->removeRelatedRecord($drec, $tdelete_record, $secure); if (PEAR::isError($mres)) { throw new Exception($mres->getMessage()); } unset($drec); } } unset($trecords); } else { if ($tfield['widget']['type'] == 'checkbox') { // Load existing records in the relationship $texisting =& $record->getRelatedRecordObjects($tfield['relationship'], 'all'); if (!is_array($texisting) or PEAR::isError($texisting)) { error_log('Failed to get related records for record ' . $record->getId() . ' in its relationship ' . $tfield['relationship']); unset($tval); unset($orderCol); unset($tval_new); unset($torder); unset($trelationship); unset($tval_existing); continue; } $texistingIds = array(); foreach ($texisting as $terec) { $texistingIds[] = $terec->getId(); } // Load currently checked records $tchecked = array(); $tcheckedRecords = array(); $tcheckedIds = array(); $tcheckedId2ValsMap = array(); foreach ($tval as $trkey => $trval) { // $trval is in the form key1=val1&size=key2=val2 parse_str($trval, $trquery); $trRecord = new Dataface_RelatedRecord($record, $tfield['relationship'], $trquery); $trRecords[] =& $trRecord; $tcheckedIds[] = $tid = $trRecord->getId(); $checkedId2ValsMap[$tid] = $trquery; unset($trRecord); unset($trquery); } // Now we have existing ids in $texistingIds // and checked ids in $tcheckedIds // See which records we need to have removed $tremoves = array_diff($texistingIds, $tcheckedIds); $tadds = array_diff($tcheckedIds, $texistingIds); foreach ($tremoves as $tid) { $trec = df_get_record_by_id($tid); $res = $this->removeRelatedRecord($trec, false, $secure); if (PEAR::isError($res)) { return $res; } unset($trec); } foreach ($tadds as $tid) { $trecvals = $checkedId2ValsMap[$tid]; $trec = new Dataface_RelatedRecord($record, $tfield['relationship'], $trecvals); $res = $this->addExistingRelatedRecord($trec, $secure); if (PEAR::isError($res)) { return $res; } unset($trec, $trecvals); } unset($tadds); unset($tremoves); unset($tcheckedIds, $tcheckedId2ValsMap); unset($tcheckedRecords); unset($tchecked); unset($texistingIds); unset($texisting); } } unset($tval); unset($trelationship); } }
function &buildWidget(&$record, &$field, &$form, $formFieldName, $new = false) { /* * * This field uses a table widget. * */ //$field['display'] = 'block'; $table =& $record->_table; $formTool =& Dataface_FormTool::getInstance(); $factory =& Dataface_FormTool::factory(); $widget =& $field['widget']; $el =& $factory->addElement('grid', $formFieldName, $widget['label']); if (!$record->checkPermission('delete related record', array('relationship' => $field['relationship']))) { $el->delete = false; } if (!$record->checkPermission('add new related record', array('relationship' => $field['relationship']))) { //echo "No add new ".$record->_table->tablename; $el->addNew = false; } if (isset($field['relationship'])) { $relationship =& $table->getRelationship($field['relationship']); if (!$relationship->supportsAddNew()) { $el->addNew = false; } if (!$relationship->supportsAddExisting()) { $el->addExisting = false; } else { $el->addExistingFilters = $relationship->getAddExistingFilters(); } $el->table = $relationship->getDomainTable(); if (isset($widget['columns'])) { $columns = array_map('trim', explode(',', $widget['columns'])); } else { $columns = $relationship->_schema['short_columns']; } $count = 0; $subfactory = new HTML_QuickForm(); foreach ($columns as $column) { $colTable =& $relationship->getTable($column); if (!$colTable) { echo "Could not find table for column {$column}"; } // We need to be a bit more refined on this one. We need to take // into account the context being that we are in a relationship. $dummyRecord = new Dataface_Record($colTable->tablename, $record->vals()); if (!$dummyRecord->checkPermission('view', array('field' => $column, 'recordmask' => array('view' => 1)))) { unset($colTable); unset($dummyRecord); continue; } $colFieldDef =& $colTable->getField($column); $columnElement =& $formTool->buildWidget($dummyRecord, $colFieldDef, $subfactory, $column, false); $defaultValue = $colTable->getDefaultValue($column); $columnElement->setValue($defaultValue); $el->addField($colFieldDef, $columnElement); $orderCol = $relationship->getOrderColumn(); if (PEAR::isError($orderCol)) { $el->reorder = false; } unset($columnElement); unset($colFieldDef); unset($dummyRecord); unset($colTable); unset($elementFilter); } } else { if (isset($widget['fields'])) { $widget_fields =& $widget['fields']; foreach ($widget_fields as $widget_field) { $widget_field =& Dataface_Table::getTableField($widget_field, $this->db); if (PEAR::isError($widget_field)) { return $widget_field; } $widget_widget = $formTool->buildWidget($record, $widget_field, $factory, $widget_field['name']); $defaultValue = $table->getDefaultValue($widget_field['name']); $widget_widget->setValue($defaultValue); $el->addField($widget_widget); } } else { if (isset($field['fields'])) { foreach (array_keys($field['fields']) as $field_key) { $widget_widget = $formTool->buildWidget($record, $field['fields'][$field_key], $factory, $field['fields'][$field_key]['name']); $defaultValue = $table->getDefaultValue($widget_field['name']); $widget_widget->setValue($defaultValue); $el->addField($widget_widget); unset($widget_widget); } } } } return $el; }
function testPermissions() { $formulasTable = Dataface_Table::loadTable('formulas'); $formulasDel = $formulasTable->getDelegate(); $formulasDel->testPermissions = true; $formula = new Dataface_Record('formulas', array('formula_name' => 'test formula')); // Test the standard permissions on tables and fields $this->assertTrue($formula->checkPermission('view'), 'View permission should be set by default.'); $this->assertTrue(!$formula->checkPermission('list'), 'List permission should be denied by default.'); $this->assertTrue($formula->checkPermission('new'), 'New permission should be permitted by default.'); $this->assertTrue(!$formula->checkPermission('new', array('field' => 'formula_name')), 'New permission should be denied on the formula_name field.'); $this->assertTrue($formula->checkPermission('view', array('field' => 'formula_name')), 'The view permission should be allowed on the formula_name field.'); $this->assertTrue(!$formula->checkPermission('view', array('field' => 'formula_id')), 'The view permission should be denied on the formula_id field.'); // Test the nobubble parameter on getPermissions $this->assertTrue(!$formula->checkPermission('delete', array('field' => 'formula_name', 'nobubble' => 1)), 'Since we are not bubbling up to record, we should not have permission for the delete permission as it is not enabled at field level explicitly - only at record level.'); $this->assertTrue($formula->checkPermission('delete', array('field' => 'formula_name')), 'Now that we are allowing bubbling, we should return true for delete on teh formula_name field.'); $this->assertTrue($formula->checkPermission('copy', array('field' => 'formula_name', 'nobubble' => 1)), 'Even though there is no bubbling, we should still return true for the copy permission on the formula_name field since it is defined in the __field__permssions() method.'); $this->assertTrue($formula->checkPermission('view', array('field' => 'amount', 'relationship' => 'ingredients')), 'view permission of the amount field in the ingredients relationship should be allowed because it is granted in the rel_ingredients__amount__permissions() method of the formulas delegate class.'); $this->assertTrue($formula->checkPermission('view', array('field' => 'amount', 'relationship' => 'ingredients', 'nobubble' => 1)), 'view permission for amount field in ingredients relationship should be allowed even with nobubble=1 because it is permistted in the rel_ingredients__amount__permissions().'); $this->assertTrue($formula->checkPermission('link', array('field' => 'amount', 'relationship' => 'ingredients')), 'link permission on amount field of the ingredients relationship should be allowed because it is granted in the rel_ingredients__permissions() method of the formulas delegate class.'); $this->assertTrue(!$formula->checkPermission('link', array('field' => 'amount', 'relationship' => 'ingredients', 'nobubble' => 1)), 'link permission on the amount field of the ingredients relationship should not be allowed when nobubble=1 because although it is granted in the rel_ingredients__permissions() method of the formulas delegate class - this method shouldnt be consulted if nobubble=1. It should just check the specific field permissions of the relationship and then break.'); $this->assertTrue($formula->checkPermission('link', array('relationship' => 'ingredients')), 'link permission should be allowed on the ingredients relationship because it is granted in the rel_ingredients__permissions() method of the formulas delegate class.'); $this->assertTrue($formula->checkPermission('link', array('relationship' => 'ingredients', 'nobubble' => 1)), 'link permission should be allowed in the ingredients relationship even with nobubble=1 because it is granted in the rel_ingredients__permissions() method of the formulas delegate class. nobubble should just prevent it from looking past the relationship permissions.'); // Test related record permissions $formulaIngredientsTable = Dataface_Table::loadTable('formula_ingredients'); $formulaIngredientsDel = $formulaIngredientsTable->getDelegate(); $formulaIngredientsDel->testPermissions = true; $relatedRecord = new Dataface_RelatedRecord($formula, 'ingredients', array('ingredient_id' => 1, 'concentration' => 3, 'amount' => 4)); // Test the standard related permission $this->assertTrue(!$relatedRecord->checkPermission('view', array('field' => 'concentration')), 'There shouldn\'t be permission to view the concentration field as it is denied in the getPermissions() method and is not overridden in any of the function methods.'); $this->assertTrue($relatedRecord->checkPermission('view', array('field' => 'ingredient_id')), 'There should be permission to view the ingredient_id field since it is overridden in the ingredient_id__permissions() method of the formula_ingredients delegate class.'); $this->assertTrue($relatedRecord->checkPermission('view', array('field' => 'amount')), 'There should be permission to view the amount field since the rel_ingredients__amount__permissions() method is defined in the parent table delegate class and grants the permission.. This should table precedence.'); $ingredientRecord = new Dataface_Record('formula_ingredients', array('ingredient_id' => 1, 'concentration' => 3, 'amount' => 4)); $this->assertTrue(!$ingredientRecord->checkPermission('view', array('field' => 'amount')), 'There should be no permission for view of the amount field directly because it hasnt been granted in the formula_ingredients delegate class.'); // Test the display now. $this->assertEquals('NO ACCESS', $relatedRecord->display('concentration'), 'Concentration should be no access via the related record because we havent granted access yet.'); $this->assertEquals('4', $relatedRecord->display('amount'), 'Amount should display the proper value because view has been granted via the relationship.'); $this->assertEquals('NO ACCESS', $ingredientRecord->display('amount'), 'Amount should display "NO ACCESS" when accessing the record directly, but instead received the actual value.'); $formulasDel->testPermissions = false; }