/** * Set model attributes from record object */ public function before() { if ($this->_record->id === null) { $this->_new = true; // pass owner id category from construct model $this->dependId = $this->_tmpDependId < 1 ? 1 : $this->_tmpDependId; } else { // make tmp id for frontend form $this->id = $this->_record->id; $path = $this->_record->path; // nesting levels if (Str::contains('/', $path)) { $nestedPath = explode('/', $path); $this->path = array_pop($nestedPath); $this->_pathNested = implode('/', $nestedPath); // get owner category id by nesting path $owner = ContentCategory::getByPath($this->_pathNested); if ($owner !== null) { $this->dependId = $owner->id; } } else { $this->path = $path; } // set data from record $this->title = $this->_record->title; $this->description = $this->_record->description; if ($this->_record->configs !== null && !Str::likeEmpty($this->_record->configs)) { $this->configs = $this->_record->configs; } } }
/** * Save new user group data in database after submit */ public function save() { $this->_role->name = $this->name; if (Str::likeEmpty($this->permissions) || !Str::contains(';', $this->permissions)) { $this->_role->permissions = ''; } else { $this->_role->permissions = implode(';', $this->permissions); } $this->_role->save(); }
/** * Try to find sitemap indexes in storage directory * @throws SyntaxException */ public function before() { if (!Directory::exist(static::INDEX_PATH)) { throw new SyntaxException(__('Directory %dir% for sitemaps is not exists', ['dir' => static::INDEX_PATH])); } $scan = File::listFiles(static::INDEX_PATH, ['.xml'], true); if (Obj::isArray($scan)) { foreach ($scan as $file) { if ($this->_lang !== null && !Str::contains('.' . $this->_lang, $file)) { continue; } $this->files[] = static::INDEX_PATH . '/' . $file; } } }
/** * Prepare field global properties - name, id and value * @param string $name * @param string|null $value * @param array $properties */ private function globalProperties($name, $value = null, &$properties) { // standard property data definition $properties['name'] = $properties['id'] = $this->formName; // form global name if ($value !== null && !Str::likeEmpty($value)) { $properties['value'] = $value; } // sounds like a array-path based obj name if (Str::contains('.', $name)) { $splitedName = explode('.', $name); foreach ($splitedName as $nameKey) { $properties['name'] .= '[' . $nameKey . ']'; $properties['id'] .= '-' . $nameKey; } } else { // standard property definition - add field name $properties['name'] .= '[' . $name . ']'; $properties['id'] .= '-' . $name; } }
/** * Build pathway from array $to. Example: ['controller/action', 'id', 'add', ['get' => 'value'], '#anchor'] * @param array $to * @param bool $encode * @return string|null */ public static function buildPathway(array $to = null, $encode = true) { // if empty passed - let show main page if ($to === null) { return null; } $response = Str::lowerCase(trim($to[0], '/')); // controller/action list($controller, $action) = explode('/', $response); $routing = App::$Properties->getAll('Routing'); // sounds like dynamic callback if (Str::startsWith('@', $controller)) { $controller = trim($controller, '@'); // search callback in properties if (isset($routing['Callback'][env_name]) && Arr::in($controller, $routing['Callback'][env_name])) { $pathInject = array_search($controller, $routing['Callback'][env_name]); // if path is founded - lets set source if ($pathInject !== false) { $controller = Str::lowerCase($pathInject); } } // if controller still looks like path injection - define last entity like controller name if (Str::contains('\\', $controller)) { $controller = Str::lastIn($controller, '\\', true); } $response = $controller . '/' . $action; } // check if controller and action is defined if (Str::likeEmpty($controller) || Str::likeEmpty($action)) { return null; } // id is defined? if (isset($to[1]) && !Str::likeEmpty($to[1])) { $response .= '/' . self::safeUri($to[1], $encode); } // add param is defined? if (isset($to[2]) && !Str::likeEmpty($to[2])) { $response .= '/' . self::safeUri($to[2], $encode); } // try to find static alias if (isset($routing['Alias'][env_name]) && Arr::in('/' . $response, $routing['Alias'][env_name])) { $pathAlias = array_search('/' . $response, $routing['Alias'][env_name]); if ($pathAlias !== false) { $response = Str::lowerCase(trim($pathAlias, '/')); } } // parse get attributes if (isset($to[3]) && Obj::isArray($to[3]) && count($to[3]) > 0) { // check if anchor bindig is exist $anchor = false; if (isset($to[3]['#']) && Obj::isString($to[3]['#']) && Str::startsWith('#', $to[3]['#'])) { $anchor = $to[3]['#']; unset($to[3]['#']); } $queryString = http_build_query($to[3]); if (Str::length($queryString) > 0) { $response .= '?' . http_build_query($to[3]); } if ($anchor !== false) { $response .= $anchor; } } // parse anchor link part #item-related-id-1 if (isset($to[4]) && Obj::isString($to[4]) && Str::startsWith('#', $to[4])) { $response .= $to[4]; } return $response; }
/** * Get input param for current model form based on param name and request method * @param string $param * @param string|null $method * @return string|null|array * @throws \InvalidArgumentException */ public function getRequest($param, $method = null) { // build param query for http foundation request $paramQuery = $this->getFormName(); if (Str::contains('.', $param)) { foreach (explode('.', $param) as $item) { $paramQuery .= '[' . $item . ']'; } } else { $paramQuery .= '[' . $param . ']'; } if ($method === null) { $method = $this->_sendMethod; } // get request based on method and param query $method = Str::lowerCase($method); switch ($method) { case 'get': return App::$Request->query->get($paramQuery, null, true); case 'post': return App::$Request->request->get($paramQuery, null, true); case 'file': return App::$Request->files->get($paramQuery, null, true); default: return App::$Request->get($paramQuery, null, true); } }
/** * Try to find exist viewer full path * @param string $path * @param string|null $source * @return null|string * @throws NativeException */ private function findViewer($path, $source = null) { $tmpPath = null; // sounds like a relative path for current view theme if (Str::contains('/', $path)) { // lets try to get full path for current theme $tmpPath = $path; if (!Str::startsWith($this->themePath, $path)) { $tmpPath = Normalize::diskPath($this->themePath . '/' . $path . '.php'); } } else { // sounds like a object-depended view call from controller or etc // get stack trace of callbacks $calledLog = debug_backtrace(); $calledController = null; // lets try to find controller in backtrace foreach ($calledLog as $caller) { if (isset($caller['class']) && Str::startsWith('Apps\\Controller\\', $caller['class'])) { $calledController = (string) $caller['class']; } } // depended controller is not founded? Let finish if ($calledController === null) { throw new NativeException('View render is failed: callback controller not founded! Call with relative path: ' . $path); } // get controller name $controllerName = Str::sub($calledController, Str::length('Apps\\Controller\\' . env_name . '\\')); $controllerName = Str::lowerCase($controllerName); // get full path $tmpPath = $this->themePath . DIRECTORY_SEPARATOR . $controllerName . DIRECTORY_SEPARATOR . $path . '.php'; } // check if builded view full path is exist if (File::exist($tmpPath)) { return $tmpPath; } // hmm, not founded. Lets try to find in caller directory (for widgets, apps packages and other) if ($source !== null) { $tmpPath = Normalize::diskPath($source . DIRECTORY_SEPARATOR . $path . '.php'); if (File::exist($tmpPath)) { // add notify for native views if (App::$Debug !== null) { App::$Debug->addMessage('Render native viewer: ' . Str::replace(root, null, $tmpPath), 'info'); } return $tmpPath; } } if (App::$Debug !== null) { App::$Debug->addMessage('Viewer not founded on rendering: ' . $path, 'warning'); } return null; }
/** * Build static and dynamic path aliases for working set */ private function runPathBinding() { // calculated depend of language $pathway = $this->getPathInfo(); /** @var array $routing */ $routing = App::$Properties->getAll('Routing'); // try to work with static aliases if (Obj::isArray($routing) && isset($routing['Alias'], $routing['Alias'][env_name])) { $pathway = $this->findStaticAliases($routing['Alias'][env_name], $pathway); } $this->setPathdata(explode('/', trim($pathway, '/'))); // set default controller and action for undefined data if ($this->action == null) { $this->action = 'Index'; } // empty or contains backslashes? set to main if ($this->controller == null || Str::contains('\\', $this->controller)) { $this->controller = 'Main'; } // find callback injection in routing configs (calculated in App::run()) if (Obj::isArray($routing) && isset($routing['Callback'], $routing['Callback'][env_name])) { $this->findDynamicCallbacks($routing['Callback'][env_name], $this->controller); } }
public function createObject($name, $type) { $singleName = false; if (!Str::contains('/', $name)) { if ($type === 'ActiveRecord') { $singleName = true; } else { $this->message = 'Command dosn\'t contains valid name. Example: Front/SomeForm, Admin/SomePkg/SomeInput'; return false; } } $objectDirPath = null; $objectNamespace = null; $objectName = null; $objectTemplate = null; if ($singleName) { $objectDirPath = root . '/Apps/' . $type . '/'; $objectNamespace = 'Apps\\' . $type; $objectName = ucfirst($name); } else { $split = explode('/', $name); $workground = ucfirst(strtolower(array_shift($split))); $objectName = ucfirst(array_pop($split)); $subName = false; if (count($split) > 0) { // some sub-namespace / folder path foreach ($split as $part) { if (Str::length($part) > 0) { $subName[] = ucfirst(strtolower($part)); } } } if ($type === 'Widget') { $objectDirPath = root . '/Widgets/' . $workground; $objectNamespace = 'Widgets\\' . $workground; } else { $objectDirPath = root . '/Apps/' . $type . '/' . $workground; $objectNamespace = 'Apps\\' . $type . '\\' . $workground; } if (false !== $subName) { $objectDirPath .= '/' . implode('/', $subName); $objectNamespace .= '\\' . implode('\\', $subName); } // try to find workground-based controller if (File::exist('/Private/Carcase/' . $workground . '/' . $type . '.tphp')) { $objectTemplate = File::read('/Private/Carcase/' . $workground . '/' . $type . '.tphp'); } } if (!Directory::exist($objectDirPath) && !Directory::create($objectDirPath)) { $this->message = 'Directory could not be created: ' . $objectDirPath; return false; } if ($objectTemplate === null) { $objectTemplate = File::read('/Private/Carcase/' . $type . '.tphp'); if (false === $objectTemplate) { $this->message = 'Php template file is not founded: /Private/Carcase/' . $type . '.tphp'; return false; } } $objectContent = Str::replace(['%namespace%', '%name%'], [$objectNamespace, $objectName], $objectTemplate); $objectFullPath = $objectDirPath . '/' . $objectName . '.php'; if (File::exist($objectFullPath)) { $this->message = $type . ' is always exist: ' . $objectFullPath; return false; } File::write($objectFullPath, $objectContent); $this->message = $type . ' template was created: [' . $objectName . '] in path: ' . Str::replace(root, '', $objectDirPath); return true; }
/** * Check if uri $source is equal to current uri point with array of $aliases and active $order set * @param null $source * @param array|null $aliases * @param bool $order * @return bool */ public static function isCurrentLink($source = null, array $aliases = null, $order = false) { $elementPoint = Url::buildPathway($source); $currentPoint = Url::buildPathwayFromRequest(); // use special active element order type: controller, action switch ($order) { case 'controller': $elementPoint = Str::firstIn($elementPoint, '/'); $active = Str::startsWith($elementPoint, $currentPoint); break; case 'action': $elementArray = explode('/', $elementPoint); if (!Str::contains('/', $elementPoint) || count($elementArray) < 2) { $active = $elementPoint === $currentPoint; } else { $elementPoint = $elementArray[0] . '/' . $elementArray[1]; $active = Str::startsWith($elementPoint, $currentPoint); } break; case 'id': $elementArray = explode('/', $elementPoint); $elementPoint = $elementArray[0] . '/' . $elementArray[1]; if ($elementArray[2] === null) { // looks like id is not defined in element if (Str::contains('?', $currentPoint)) { $currentPoint = Str::firstIn($currentPoint, '?'); } $currentArray = explode('/', $currentPoint); $currentToId = implode('/', array_slice($currentArray, 0, 3)); $active = $elementPoint === $currentToId; } else { $elementPoint .= '/' . $elementArray[2]; $active = Str::startsWith($elementPoint, $currentPoint); } break; default: $active = $elementPoint === $currentPoint; break; } // check if current uri equals with aliases if (Obj::isArray($aliases) && count($aliases) > 0) { foreach ($aliases as $activeUri) { $activeUri = trim($activeUri, '/'); if (Str::endsWith('*', $activeUri)) { $activeUri = rtrim($activeUri, '*'); if (Str::startsWith($activeUri, $currentPoint)) { $active = true; } } else { if ($activeUri === $currentPoint) { $active = true; } } } } return $active; }
/** * Display nav listing block * @param array $elements */ public static function display($elements) { // check if elements isn't empty and contains rows if (!Obj::isArray($elements) || count($elements['items']) < 1) { return null; } // prepare tab order if ($elements['tabAnchor'] === null) { $elements['tabAnchor'] = Str::randomLatin(mt_rand(6, 12)); } // set global element properties $blockProperty = []; if ($elements['blockProperty'] !== null) { if (Obj::isArray($elements['blockProperty'])) { $blockProperty = $elements['blockProperty']; } unset($elements['blockProperty']); } // check if items have defined active order $activeDefined = Arr::in(true, Arr::ploke('active', $elements['items'])); // prepare tab content $tabContent = null; $tabIdx = 1; // initialize dom model $dom = new Dom(); // prepare items to drow listing $items = []; foreach ($elements['items'] as $item) { // its just a link, drow it as is if ($item['type'] === 'link') { $items[] = $item; } elseif ($item['type'] === 'dropdown') { // build bootstrap dropdown properties $item['dropdown'] = ['class' => 'dropdown-toggle', 'data-toggle' => 'dropdown', 'href' => '#']; $item['property']['class'] = Str::concat(' ', 'dropdown', $item['property']['class']); $items[] = $item; } elseif ($item['type'] === 'tab') { $activeObject = false; $item['type'] = 'link'; // fix for global Listing builder $item['link'] = '#' . $elements['tabAnchor'] . $tabIdx; $item['property']['role'] = 'presentation'; // check if active definition is exist in elements options if ($activeDefined) { if ($item['active'] === true) { $activeObject = true; } } elseif ($tabIdx === 1) { // if not exist set first as active $activeObject = true; } // mark active tab if ($activeObject === true) { $item['property']['class'] .= (Str::length($item['property']['class']) > 0 ? ' ' : null) . 'active'; } // tab special properties for bootstrap $item['linkProperty']['aria-controls'] = $elements['tabAnchor'] . $tabIdx; $item['linkProperty']['role'] = 'tab'; $item['linkProperty']['data-toggle'] = 'tab'; $itemContent = $item['content']; unset($item['content']); $items[] = $item; // draw tab content $tabContent .= $dom->div(function () use($item, $itemContent) { if ($item['html'] === true) { if ($item['!secure'] === true) { return $itemContent; } else { return self::safe($itemContent, true); } } else { return self::nohtml($itemContent); } }, ['role' => 'tabpanel', 'class' => 'tab-pane fade' . ($activeObject === true ? ' in active' : null), 'id' => $elements['tabAnchor'] . $tabIdx]); $tabIdx++; } } // check if global class "nav" isset if ($elements['property']['class'] !== null) { if (!Str::contains('nav ', $elements['property']['class'])) { $elements['property']['class'] = 'nav ' . $elements['property']['class']; } } else { $elements['property']['class'] = 'nav'; } // render final output return $dom->div(function () use($elements, $items, $tabContent, $dom) { // drow listing $listing = Listing::display(['type' => 'ul', 'property' => $elements['property'], 'activeOrder' => $elements['activeOrder'], 'items' => $items]); // drow tabs if isset if ($tabContent !== null) { $tabContent = $dom->div(function () use($tabContent) { return $tabContent; }, ['class' => 'tab-content']); } return $listing . $tabContent; }, $blockProperty); }
/** * Console installation * @return string * @throws NativeException */ public function actionInstall() { if (File::exist('/Private/Install/install.lock')) { throw new NativeException('Installation is locked! Please delete /Private/Install/install.lock'); } echo Console::$Output->writeHeader('License start'); echo File::read('/LICENSE') . PHP_EOL; echo Console::$Output->writeHeader('License end'); $config = Console::$Properties->get('database'); $newConfig = []; // creating default directory's foreach (self::$installDirs as $obj) { // looks like a directory if (!Str::contains('.', $obj)) { Directory::create($obj, 0777); } } echo Console::$Output->write('Upload and private directories are successful created!'); // set chmods echo $this->actionChmod(); // database config from input echo Console::$Output->writeHeader('Database connection configuration'); echo 'Driver(default:' . $config['driver'] . '):'; $dbDriver = Console::$Input->read(); if (Arr::in($dbDriver, ['mysql', 'pgsql', 'sqlite'])) { $newConfig['driver'] = $dbDriver; } // for sqlite its would be a path echo 'Host(default:' . $config['host'] . '):'; $dbHost = Console::$Input->read(); if (!Str::likeEmpty($dbHost)) { $newConfig['host'] = $dbHost; } echo 'Database name(default:' . $config['database'] . '):'; $dbName = Console::$Input->read(); if (!Str::likeEmpty($dbName)) { $newConfig['database'] = $dbName; } echo 'User(default:' . $config['username'] . '):'; $dbUser = Console::$Input->read(); if (!Str::likeEmpty($dbUser)) { $newConfig['username'] = $dbUser; } echo 'Password(default:' . $config['password'] . '):'; $dbPwd = Console::$Input->read(); if (!Str::likeEmpty($dbPwd)) { $newConfig['password'] = $dbPwd; } echo 'Table prefix(default:' . $config['prefix'] . '):'; $dbPrefix = Console::$Input->read(); if (!Str::likeEmpty($dbPrefix)) { $newConfig['prefix'] = $dbPrefix; } // merge configs and add new connection to db pull $dbConfigs = Arr::merge($config, $newConfig); Console::$Database->addConnection($dbConfigs, 'install'); try { Console::$Database->connection('install')->getDatabaseName(); } catch (\Exception $e) { return 'Testing database connection is failed! Run installer again and pass tested connection data! Log: ' . $e->getMessage(); } // autoload isn't work here include root . '/Apps/Controller/Console/Db.php'; // import db data $dbController = new DbController(); echo $dbController->actionImportAll('install'); // add system info about current install version $system = new System(); $system->setConnection('install'); $system->var = 'version'; $system->data = Version::VERSION; $system->save(); // set website send from email from input $emailConfig = Console::$Properties->get('adminEmail'); echo 'Website sendFrom email(default: ' . $emailConfig . '):'; $email = Console::$Input->read(); if (!Str::isEmail($email)) { $email = $emailConfig; } // set base domain echo 'Website base domain name(ex. ffcms.org):'; $baseDomain = Console::$Input->read(); if (Str::likeEmpty($baseDomain)) { $baseDomain = Console::$Properties->get('baseDomain'); } // generate other configuration data and security salt, key's and other echo Console::$Output->writeHeader('Writing configurations'); /** @var array $allCfg */ $allCfg = Console::$Properties->getAll('default'); $allCfg['database'] = $dbConfigs; $allCfg['adminEmail'] = $email; $allCfg['baseDomain'] = $baseDomain; echo Console::$Output->write('Generate password salt for BLOWFISH crypt'); $allCfg['passwordSalt'] = '$2a$07$' . Str::randomLatinNumeric(mt_rand(21, 30)) . '$'; echo Console::$Output->write('Generate security cookies for debug panel'); $allCfg['debug']['cookie']['key'] = 'fdebug_' . Str::randomLatinNumeric(mt_rand(8, 32)); $allCfg['debug']['cookie']['value'] = Str::randomLatinNumeric(mt_rand(32, 128)); // write config data $writeCfg = Console::$Properties->writeConfig('default', $allCfg); if ($writeCfg !== true) { return 'File /Private/Config/Default.php is unavailable to write data!'; } File::write('/Private/Install/install.lock', 'Install is locked'); return 'Configuration done! FFCMS 3 is successful installed! Visit your website. You can add administrator using command php console.php db/adduser'; }
/** * Display form field. Allowed type: text, password, textarea, checkbox, select, checkboxes, file, captcha, email, hidden * @param $object * @param $type * @param null|array $property * @param null|string $helper * @param null|string $layerFile * @return null|string * @throws NativeException * @throws SyntaxException */ public function field($object, $type, $property = null, $helper = null, $layerFile = null) { if ($this->model === null) { if (App::$Debug !== null) { App::$Debug->addMessage('Form model is not defined for field name: [' . strip_tags($object) . ']'); } return null; } // can be dots separated object $propertyName = $object; if (Str::contains('.', $propertyName)) { $propertyName = strstr($propertyName, '.', true); } // check if model contains current tag name as property if (!property_exists($this->model, $propertyName)) { if (App::$Debug !== null) { App::$Debug->addMessage('Form field ["' . $object . '"] is not defined in model: [' . get_class($this->model) . ']', 'error'); } return null; } // prepare layer template file path if ($layerFile === null) { switch ($type) { case 'checkbox': $layerFile = static::$structLayer['checkbox']; break; case 'radio': $layerFile = static::$structLayer['radio']; break; default: $layerFile = static::$structLayer['base']; break; } } // prepare labels text and label "for" attr $labelFor = $this->name . '-' . $propertyName; $labelText = $this->model->getLabel($object); $itemValue = $this->model->{$propertyName}; // sounds like a dot-separated $object if ($propertyName !== $object) { $nesting = trim(strstr($object, '.'), '.'); $labelFor .= '-' . Str::replace('.', '-', $nesting); $itemValue = Arr::getByPath($nesting, $itemValue); } // initialize form fields constructor and build output dom html value $constructor = new Constructor($this->model, $this->name, $type); $elementDOM = $constructor->makeTag($object, $itemValue, $property); // if item is hidden - return tag without assign of global template if ($type === 'hidden') { return $elementDOM; } // render output viewer return App::$View->render($layerFile, ['name' => $labelFor, 'label' => $labelText, 'item' => $elementDOM, 'help' => self::nohtml($helper)]); }
/** * Validate defined rules in app * @return bool * @throws SyntaxException */ public final function validate() { // validate csrf token if required if ($this->_tokenRequired && !$this->_tokenOk) { App::$Session->getFlashBag()->add('warning', __('Hack attention: security token is wrong!')); return false; } // get all rules as array from method rules() $rules = $this->rules(); // get default values of attributes $defaultAttr = $this->getAllProperties(); // start validation: on this step class attribute values will be changed to input data if it valid $success = $this->runValidate($rules); // get not-passed validation fields as array $badAttributes = $this->getBadAttribute(); // prevent warnings if (Obj::isArray($badAttributes) && count($badAttributes) > 0) { foreach ($badAttributes as $attr) { if (Str::contains('.', $attr)) { // sounds like dot-separated array attr $attrName = strstr($attr, '.', true); // get attr name $attrArray = trim(strstr($attr, '.'), '.'); // get dot-based array path $defaultValue = new DotData($defaultAttr); // load default attr $dotData = new DotData($this->{$attrName}); // load local attr variable $dotData->set($attrArray, $defaultValue->get($attr)); // set to local prop. variable default value $this->{$attrName} = $dotData->export(); // export to model } else { $this->{$attr} = $defaultAttr[$attr]; // just set ;) } // add message about wrong attribute to session holder, later display it $attrLabel = $attr; if ($this->getLabel($attr) !== null) { $attrLabel = $this->getLabel($attr); } App::$Session->getFlashBag()->add('warning', __('Field "%field%" is incorrect', ['field' => $attrLabel])); } } return $success; }