Example #1
0
 public function write()
 {
     $pdo = $this->database->getConnection();
     $tables = $pdo->select(sprintf("SELECT TABLE_NAME as `table`, COLUMN_NAME as pk FROM information_schema.columns WHERE table_schema = '%s' AND COLUMN_KEY = 'PRI'", $pdo->getDatabaseName()));
     foreach ($tables as $table) {
         $name = $table->table;
         if (!$this->resolver->getModel($name)) {
             $path = sprintf('%s/app/Model/%s.php', $this->bootLoader->getBaseDir(), ucfirst(Str::camel(Str::singular("{$name}"))));
             if (!file_exists($path)) {
                 $this->writer->write($path, ['class' => $this->utils->filename($path), 'table' => $table->table, 'pk' => $table->pk], 'Created new model');
             }
         }
     }
 }
 protected function setUp()
 {
     define('MODEL_DIR', '\\Test\\Model');
     //define('DEBUG_MODE', true);
     $this->injector = new Injector();
     $this->injector->share(Database::class);
     $this->database = $this->injector->make('Minute\\Database\\Database', [true]);
     try {
         $this->pdo = $this->database->getPdo();
     } catch (\PDOException $e) {
         $this->markTestSkipped('Database connection error: ' . $e->getMessage());
     }
     parent::setUp();
 }
Example #3
0
 public function modelToJsClasses($parent, string $foreignKey = '')
 {
     /** @var ModelEx $model */
     $self = $parent['self'];
     $template = '';
     if ($class = $this->resolver->getModel($self['name'])) {
         $model = new $class();
         $localKey = $model->getKeyName();
         $template .= $this->modelJs->createItem($self['alias'], $parent['children'], $this->database->getColumns($model->getTable()));
         $template .= $this->modelJs->createItemArray($self['alias'], $class, $localKey, $foreignKey);
         foreach ($parent['children'] ?? [] as $child) {
             $template .= $this->modelToJsClasses($child, $localKey);
         }
     }
     return $template;
 }
Example #4
0
 /**
  * Bind listeners defined in plugins, app and Database
  */
 public function bind()
 {
     $listeners = $this->cache->get('app-listeners', function () {
         foreach ($this->resolver->getListeners() as $file) {
             try {
                 $binding = $this;
                 require_once $file;
             } catch (\Throwable $e) {
                 $this->logger->warn("Unable to include {$file}: " . $e->getMessage());
             }
         }
         $listeners = $this->getListeners();
         if ($this->database->isConnected()) {
             /** @var ModelEx $eventModel */
             if ($eventModel = $this->resolver->getModel('Event', true)) {
                 try {
                     foreach ($eventModel::all() as $item) {
                         $attrs = $item->attributesToArray();
                         list($class, $func) = @explode('@', $attrs['handler']);
                         $event = array_merge($attrs, ['event' => $attrs['name'], 'handler' => [sprintf('\\%s', ltrim($class, '\\')), $func ?? 'index']]);
                         $listeners[] = $event;
                     }
                 } catch (\Exception $e) {
                 }
             }
         }
         return $listeners;
     }, 300);
     foreach ($listeners as $listener) {
         $this->dispatcher->listen($listener['event'], $listener['handler'], $listener['priority'] ?? 99, $listener['data'] ?? '');
     }
 }
Example #5
0
 public function getTargetUserIds(int $ar_list_id) : array
 {
     return $this->qCache->get("ar-list-id-{$ar_list_id}", function () use($ar_list_id) {
         $results = ['positive' => [], 'negative' => []];
         if ($list = MArList::find($ar_list_id)) {
             $sqls = MArListSql::where('ar_list_id', '=', $ar_list_id)->get();
             foreach ($sqls as $sql) {
                 $users = $this->qCache->get(md5($sql->sql), function () use($sql) {
                     foreach ($this->db->getPdo()->query($sql->sql) as $row) {
                         $results[] = $row[0];
                     }
                     return $results ?? [];
                 });
                 $results[$sql->type] = array_merge($results[$sql->type], $users);
             }
         }
         return array_diff($results['positive'], $results['negative']) ?: [];
     }, 3600);
 }
Example #6
0
 public function setup(HttpRequestEx $request)
 {
     $params = $request->getParameters();
     try {
         if (!empty($params['db']['database']) && !empty($params['db']['username']) && !empty($params['db']['password'])) {
             try {
                 $conn = $this->database->connect($params['db']);
                 if ($pdo = $conn->getPdo()) {
                     $conf = sprintf('%s/app/Config/db-config', $this->bootLoader->getBaseDir());
                     if (file_put_contents($conf, sprintf('mysql://%s:%s@%s/%s', $params['db']['username'], $params['db']['password'], $params['db']['host'], $params['db']['database']))) {
                         if ($this->installer->install(['minutephp/site'], 'require', true)) {
                             $sth = $pdo->prepare('REPLACE INTO users SET email = :email, password = :password, ip_addr = :ip, created_at = NOW(), updated_at = NOW(), first_name = "Admin", verified = "true"');
                             $sth->execute(['email' => sprintf('admin@%s', $params['site']['domain'] ?? 'localhost'), 'password' => password_hash(Str::random(), PASSWORD_DEFAULT), 'ip' => $this->sniffer->getUserIP()]);
                             if ($admin_id = $pdo->lastInsertId()) {
                                 $sth = $pdo->prepare('REPLACE INTO m_user_groups set user_id = :user_id, group_name = "admin", created_at = NOW(), updated_at = NOW(), 
                                                                        expires_at = "20200101", credits = 999, comments = "First run"');
                                 $sth->execute(['user_id' => $admin_id]);
                                 $types = ['public' => $params['site'] ?? [], 'private' => []];
                                 foreach ($types as $type => $data) {
                                     $sth = $pdo->prepare('REPLACE INTO m_configs set type = :type, data_json = :data');
                                     $sth->execute(['type' => $type, 'data' => json_encode($data)]);
                                 }
                                 $this->session->startSession($admin_id);
                                 return 'pass';
                             }
                         } else {
                             throw new FirstRunError($this->lang->getText("Unable to run composer"));
                         }
                     }
                 }
             } catch (\Throwable $e) {
                 throw new FirstRunError($this->lang->getText("Unable to connect to database.\n") . $e->getMessage());
             }
         }
         throw new FirstRunError($this->lang->getText('All connection parameters are required. Please check connection details'));
     } catch (\Throwable $e) {
         if (!empty($conf) && file_exists($conf)) {
             @unlink($conf);
         }
         throw new FirstRunError("Error: " . $e->getMessage());
     }
 }
Example #7
0
 public function update(UserUpdateDataEvent $event)
 {
     if ($user = $event->getUser()) {
         $fields = array_diff($this->database->getColumns($user->getTable()), ['user_id', 'ident', 'created_at', 'updated_at']);
         $userDataModel = $this->resolver->getModel('UserData', true);
         foreach ($event->getNewData() as $key => $value) {
             if (in_array($key, $fields)) {
                 if ($event->isOverwrite() || empty($user->{$key}) || $key === 'verified' && $value === 'true') {
                     $dataChanged = $dataChanged ?? $user->{$key} !== $value;
                     $user->{$key} = $key === 'password' ? password_hash($value, PASSWORD_DEFAULT) : $value;
                 }
             } elseif (!empty($userDataModel)) {
                 /** @var MUserDatum $extra */
                 $extra = $userDataModel::firstOrCreate(['user_id' => $user->user_id, 'key' => $key]);
                 if ($event->isOverwrite() || empty($extra->data)) {
                     if (!empty($value)) {
                         $extra->data = $value;
                         $extra->save();
                     } else {
                         $extra->delete();
                     }
                 }
             } else {
                 $event->setError('INVALID_FIELD');
                 throw new UserUpdateDataError("Field {$key} not found in users table");
             }
         }
         if (!empty($dataChanged)) {
             if ($user->save()) {
                 $event->setHandled(true);
             }
         } else {
             $event->setHandled(true);
         }
     } else {
         $event->setError('INVALID_USER');
     }
 }
Example #8
0
 public function importSession(ViewEvent $event)
 {
     if (!$this->database->isConnected()) {
         return;
     }
     /** @var RouteEx $route */
     $view = $event->getView();
     $vars = $view->getVars();
     $route = $view->get('_route');
     $data = $this->getCachedSessionData(false);
     $data['request'] = array_merge(['url' => getenv('REQUEST_URI')], $this->request->getParameters());
     $data['params'] = array_diff_key($route->getDefaults(), array_flip(['controller', 'auth', 'models', '_route']));
     foreach ($vars as $key => $value) {
         if ($key[0] !== '_' && (is_scalar($value) || is_array($value))) {
             $data['vars'][$key] = $value;
         }
     }
     if (!empty($data['site'])) {
         $data['site']['version'] = $this->database->hasRdsAccess() ? 'production' : 'debug';
     }
     $printer = sprintf('<script' . '>Minute.setSessionData(%s)</script>', json_encode($data));
     $event->setContent($printer);
 }
Example #9
0
 /**
  * Automatically fills fields like created_at, updated_at, *_slug, *_safe
  * and sets unset fields to null
  *
  * @param ModelEx $model
  *
  * @return ModelEx
  */
 public function fillMissing($model)
 {
     if ($columns = $this->database->getColumns($model->getTable())) {
         foreach ($columns as $column) {
             if (!$model->{$column} && $model->{$column} !== 0) {
                 if (preg_match('/^(created_at|updated_at)$/', $column)) {
                     $model->{$column} = Carbon::now();
                 } elseif (preg_match('/(\\w+)\\_slug$/', $column, $matches)) {
                     if ($root = $model->{$matches[1]} ?? null) {
                         $pk = $model->getKeyName();
                         $slug = $copy = (new Slugify())->slugify($root);
                         $count = 1;
                         while ($record = $model::where($column, '=', $slug)->first()) {
                             if ($record->{$pk} == $model->{$pk}) {
                                 $slug = false;
                                 break;
                             }
                             $slug = sprintf('%s-%d', $copy, $count++);
                         }
                         if (!empty($slug)) {
                             $model->{$column} = $slug;
                         }
                     }
                 } elseif ($model->isNullable($column)) {
                     $model->{$column} = null;
                 }
             }
             if (preg_match('/\\_safe$/', $column)) {
                 $model->{$column} = Htmlawed::filter($model->{$column});
             } elseif (preg_match('/\\_json$/', $column) && is_array($model->{$column})) {
                 $model->{$column} = json_encode($model->{$column});
             }
         }
     }
     return $model;
 }
Example #10
0
 public function launch()
 {
     global $conn;
     $conn = $this->database->getDsn();
     include_once $this->adminer;
 }
Example #11
0
 public function getConfiguration()
 {
     $conn = ['environments' => ['default_database' => 'dev', "default_migration_table" => "m_phinx_logs", 'dev' => ['name' => $this->database->getDsn()['database'], 'connection' => $this->database->getPdo()]]];
     $paths = ['paths' => ['migrations' => __DIR__ . '/db/migrations']];
     return array_merge($paths, $conn);
 }
Example #12
0
 /**
  * Find the matching route and execute the controller
  *
  * Algorithm - http://www.minutephp.com/ADD
  *      # Get the URL and Method from the event
  *      Find the matching route by URL and method
  *          Get the controller, auth and list of models from route
  *          Throw error if the user is not authorized to access the route
  *
  *          If Method is GET
  *              Parse the models to create parent child relations
  *              Throw error if the user isn't allowed to Read the model
  *              Create a list of constraints using \
  *                  \ conditions in the route model definitions
  *                  \ conditions added using `addConstraint` method
  *              Perform additional checks if permission is SAME_USER
  *              If the controller is null guess it from the route URL
  *              If this is an ajax request with metadata then override the controller with our own GET handler
  *              Call the controller with the constraints using an event
  *
  *          If Method is POST
  *              If this is an ajax request and we have model definitions
  *                  Parse the models in the route
  *                  Get the model, and items and type of operation from the request
  *                  Throw error if the user cannot the operation on selected model
  *                  Create models instances for each item
  *                  Call the GenericPostHandler with the instances using an event
  * end.
  *
  * @param RequestEvent $requestEvent
  *
  * @throws AuthError
  * @throws ModelError
  * @throws RouteError
  * @throws ValidationError
  */
 public function handle(RequestEvent $requestEvent)
 {
     $request = $requestEvent->getRequest();
     //try {
     $parents = null;
     $method = $request->getMethod();
     $this->compile();
     //Since we can have multiple POST requests for a single Url
     if ($method === 'POST' && ($alias = $request->getParameter('alias')) && ($class = $request->getParameter('model'))) {
         $newCollection = new RouteCollection();
         foreach (['alias' => $alias, 'name' => $class] as $key => $value) {
             /** @var RouteEx $routeEx */
             foreach ($this->routeCollection as $name => $routeEx) {
                 if (in_array('POST', $routeEx->getMethods())) {
                     $parents = $routeEx->parsePostModels();
                     if ($filtered = array_filter($parents, function ($m) use($key, $value) {
                         return $m[$key] === $value;
                     })) {
                         $newCollection->add($name, $routeEx);
                     }
                 }
             }
         }
         if ($newCollection->count()) {
             $this->routeCollection = $newCollection;
         } else {
             throw new ResourceNotFoundException("No Post handler configured for alias '{$alias}' or class '{$class}'");
         }
     }
     try {
         $route = $this->match($method, $request->getPath());
     } catch (ResourceNotFoundException $e) {
         $event = new RouterEvent($method, $request->getPath());
         $this->dispatcher->fire(RouterEvent::ROUTER_GET_FALLBACK_RESOURCE, $event);
         if (!($route = $event->getRoute())) {
             throw $e;
         }
     }
     $event = new AuthEvent($route->getDefault('auth'));
     $this->lastMatchingRoute = $route;
     $this->dispatcher->fire(AuthEvent::AUTH_CHECK_ACCESS, $event);
     if ($event->isAuthorized()) {
         $user_id = $event->getActiveUserId();
         $controller = $controllerArgs = null;
         $contentType = 'html';
         if ($method === 'POST') {
             $parents = $route->parsePostModels();
             $controller = $route->getDefault('controller');
             $matches = [];
             if (empty($controller)) {
                 if (!empty($parents)) {
                     $controller = 'Generic/DefaultPostHandler.php';
                 } else {
                     $controller = trim(Str::camel(str_replace(' ', '', ucwords(preg_replace('/(\\W+)/', '\\1 ', $route->getPath())))), '/');
                 }
             }
             if (!empty($parents)) {
                 if ($alias = $request->getParameter('alias')) {
                     $matches = array_filter($parents, function ($f) use($alias) {
                         return $f['alias'] === $alias;
                     });
                 }
                 if (count($matches) === 1) {
                     $model = array_shift($matches);
                 } else {
                     if ($class = $request->getParameter('model')) {
                         $matches = array_filter($parents, function ($f) use($class) {
                             return $f['name'] === $class;
                         });
                     }
                     if (count($matches) === 1) {
                         $model = array_shift($matches);
                     } else {
                         throw new ModelError("Post route does not have a matching model for alias:'{$alias}' or class:'{$class}'");
                     }
                 }
                 /** @var ModelEx $inst */
                 if ($modelClass = $this->resolver->getModel($model['name'])) {
                     $inst = new $modelClass();
                     $cmd = $request->getParameter('cmd', 'save');
                     $pri = $inst->getKeyName();
                     $items = $request->getParameter('items', []);
                     $cols = $this->database->getColumns($inst->getTable());
                     if (!empty($items)) {
                         foreach ((array) $items as $item) {
                             $accessMode = $cmd == 'save' ? !empty($item[$pri]) ? 'update' : 'create' : 'delete';
                             $permission = call_user_func([$route, sprintf('get%sPermission', ucwords($accessMode))], $model['alias']);
                             if ($this->canAccessModel($permission, $event->isLoggedInUser())) {
                                 $fields = $model['fields'] ?? $cols;
                                 $immutable = [$pri, 'user_id'];
                                 $fields = array_diff($fields, $immutable);
                                 $has_uid = in_array('user_id', $cols);
                                 if (count(array_diff(array_keys($item), array_merge($fields, $immutable))) !== 0) {
                                     throw new ValidationError(sprintf("Field restriction on '%s' failed on fields: '%s'", $model['alias'], join(', ', array_keys($item))));
                                 }
                                 if ($accessMode === 'create') {
                                     /** @var ModelEx $m */
                                     $modelClass::unguard();
                                     $instance = new $modelClass($item);
                                 } else {
                                     if ($instance = $modelClass::where($pri, '=', $item[$pri])->first()) {
                                         if ($accessMode !== 'delete') {
                                             foreach ($fields as $field) {
                                                 $instance->{$field} = $item[$field] ?? $instance->{$field} ?? null;
                                             }
                                         }
                                     } else {
                                         throw new ModelError(sprintf("No record found in %s for '%s' = '%s'", $model['name'], $pri, $item[$pri]));
                                     }
                                 }
                                 if ($permission == Permission::SAME_USER) {
                                     if (!$instance->user_id) {
                                         $instance->user_id = $user_id;
                                     } else {
                                         if ($instance->user_id !== $user_id) {
                                             throw new ModelError(sprintf("%s user_id does not match to current user.", ucfirst($model['name'])));
                                         }
                                     }
                                 }
                                 if ($has_uid && $accessMode !== 'delete') {
                                     $instance->user_id = $instance->user_id ?: ($user_id ?: ($item['user_id'] ?: 0));
                                 }
                                 $models[] = $instance;
                             } else {
                                 throw new ModelError(sprintf("%s access denied to '%s' in %s. Enable auth or change read permission", ucwords($accessMode), $model['alias'], $route->getPath()));
                             }
                         }
                     }
                 } else {
                     throw new RouteError(sprintf("Cannot create model for class: %s in %s", $model['name'], $route->getPath()));
                 }
             }
             $controllerArgs = ['_parents' => $parents, '_models' => $models ?? null, '_mode' => $accessMode ?? $cmd ?? 'update'];
         } elseif ($method === 'GET') {
             $parents = $route->parseGetModels();
             $metadata = $request->getParameter('metadata');
             $addConstraints = function ($alias, $permission, $self) use($route, $event, $user_id) {
                 if ($canIgnore = $permission === SpecialPermission::SAME_USER_OR_IGNORE) {
                     $permission = Permission::SAME_USER;
                 }
                 $hasAccess = $this->canAccessModel($permission, $event->isLoggedInUser());
                 if (!$hasAccess && $canIgnore) {
                     $route->addConstraint($alias, function (Builder $builder) {
                         return $builder->whereRaw('1 = 0');
                     });
                 } elseif ($hasAccess) {
                     $colValue = null;
                     if ($permission === Permission::SAME_USER) {
                         $route->addConstraint($alias, ['user_id', '=', $user_id]);
                     }
                     if ($matchInfo = $self['matchInfo'] ?? null) {
                         if (!empty($matchInfo['name'])) {
                             list($name, $col, $type) = [$matchInfo['name'], $matchInfo['col'], $matchInfo['type']];
                             $colValue = $type === 'url_param' ? $route->getDefault($name) : ($type === 'var' ? ${$name} : ($type === 'string' ? $matchInfo['value'] : null));
                         }
                     }
                     if (!empty($col) && isset($colValue)) {
                         $route->addConstraint($alias, [$col, '=', $colValue]);
                     } elseif ($route->getDefault($alias) !== '*' && $permission !== Permission::SAME_USER) {
                         //we block all queries when there is no matchInfo (unless alias explicitly defaults to *)
                         $route->addConstraint($alias, function (Builder $builder) {
                             return $builder->whereRaw('1 = 0');
                         });
                     }
                 } else {
                     throw new ModelError(sprintf("Read access denied to %s in %s. Enable auth or change read permission", $alias, $route->getPath()));
                 }
             };
             $joiner = function ($parents) use(&$joiner, $route, $addConstraints) {
                 foreach ($parents['children'] as $alias => $value) {
                     $permission = $route->getJoinPermission($alias);
                     if ($permission !== Permission::EVERYONE) {
                         $addConstraints($alias, $permission, $value['self']);
                     }
                     if (!empty($value['children'])) {
                         $joiner($value);
                     }
                 }
             };
             foreach ($parents as $key => $value) {
                 $self = $value['self'];
                 if ($parentModelClass = $this->resolver->getModel($self['name'])) {
                     $parentAlias = $self['alias'];
                     $permission = $route->getReadPermission($parentAlias);
                     $addConstraints($parentAlias, $permission, $self);
                 } else {
                     throw new RouteError(sprintf("Cannot create model for class: %s in %s", $self['name'], $route->getPath()));
                 }
                 $joiner($value);
             }
             $controller = $route->getDefault('controller');
             $modifyNodeByAlias = function (&$root, $alias, $modifiers, $append) use(&$modifyNodeByAlias) {
                 foreach ($root as $parent => $child) {
                     if ($parent === $alias) {
                         foreach ($modifiers as $key => $value) {
                             if ($append) {
                                 //conditions
                                 $root[$parent]['self'][$key] = array_merge($root[$parent]['self'][$key] ?? [], $value);
                             } else {
                                 //offset, limit, etc
                                 $root[$parent]['self'][$key] = $value;
                             }
                         }
                         return true;
                     } elseif (!empty($child['children']) && ($found = $modifyNodeByAlias($root[$parent]['children'], $alias, $modifiers, $append))) {
                         return $found;
                     }
                 }
                 return false;
             };
             foreach ($route->getAllConstraints() as $alias => $constraint) {
                 if (!$modifyNodeByAlias($parents, $alias, ['conditions' => $constraint], true)) {
                     throw new ModelError("Could not find model {$alias} to add condition");
                 }
             }
             if ($controller === null) {
                 $controller = 'Generic/Page.php';
             }
             if ($request->isAjaxRequest() && !empty($metadata)) {
                 //we override controller for ajax request which have $_GET['metadata'] set
                 $metadata = json_decode($metadata, true);
                 $contentType = 'ajax';
                 foreach ($metadata ?? [] as $alias => $values) {
                     if (!$modifyNodeByAlias($parents, $alias, $values, false)) {
                         throw new ModelError("Could not find model {$alias} to apply metadata");
                     }
                 }
             }
             $controllerArgs = ['_parents' => $parents];
         }
         $defaults = array_diff_key($route->getDefaults(), array_flip(['url', 'controller', 'auth', 'models', '_route']));
         $params = array_merge($defaults, $request->getParameters());
         $event = new ControllerEvent($controller, array_merge(['_route' => $route, '_method' => $method, '_params' => $params, '_contentType' => $contentType], $params, $controllerArgs ?? []));
         $this->dispatcher->fire(ControllerEvent::CONTROLLER_EXECUTE, $event);
         $response = $event->getResponse();
     } else {
         throw new AuthError('Forbidden');
     }
     $requestEvent->setResponse($response);
 }
Example #13
0
 /**
  * @return \Minute\Model\ModelEx
  */
 protected function getConfigModel()
 {
     return $this->database->isConnected() ? $this->resolver->getModel('Config', true) : null;
 }