'address#', 'd'=>'%date', ]; // Fields that are editable private $fieldoptions = [ 'p'=>['edit'=>TRUE,'mask'=>'*'], // Password 'u'=>['edit'=>TRUE], // User 't'=>['edit'=>TRUE], // Text ]; // @todo Move this to the database private $header = RED.'T'.BLUE.'E'.GREEN.'S'.YELLOW.'T'.MAGENTA.'!'; public function __construct(\App\Models\Frame $o,string $msg=NULL) { $this->frame = $o; $this->output = $this->hasFlag('clear') ? CLS : HOME; // If we have a message to display on the bottom line. if ($msg) $this->output .= UP.$msg.HOME; $startline = 0; if (! $this->hasFlag('ip') AND (! $this->isCUG(0) OR $this->type() !== self::FRAMETYPE_LOGIN)) { // Set the page header: CUG/Site Name | Page # | Cost $this->output .= $this->render_header($this->header). $this->render_page($this->frame->frame,$this->frame->index). $this->render_cost($this->frame->cost); $startline = 1; } // Calculate fields and render output. $this->fields = collect(); // Fields in this frame. $this->fields($startline); } /** * Render the frame * * @return null|string */ public function __toString() { return $this->output; } /** * Return a list of alternative versions of this frame. * * @todo: Need to adjust to not include access=0 frames unless owner */ public function alts(Mode $o) { return \App\Models\Frame::where('frame',$this->frame()) ->where('index',$this->index()) ->where('id','<>',$this->frame->id) ->where('mode_id',$o->id) ->where('access',1) ->limit(9); } /** * Frame Created Date */ public function created() { return $this->frame->created_at; } /** * Convert the frame from Binary to Output * Look for fields within the frame. * * @param int $startline */ abstract public function fields($startline=0); /** * Returns the current frame. */ public function frame() { return $this->frame->frame; } /** * Get the CUG for a frame * * Frame CUG are derived from their frame number. * EG: Frame 642 is a member of 642, or 64, or 6, or 0, whichever matches first. * * @return CUG */ public function getCUG() { $co = NULL; $frame = $this->frame->frame; while (! $co) { $co = CUG::find($frame); if (! $co) { $frame = substr($frame,0,strlen($frame)-1); if (! $frame) $frame = 0; } } return $co; } /** * Return the current field configuration */ public function getField(int $id) { return $this->fields->get($id); } /** * Get a specific key of the field options that passes a filter test * * @param string $type * @param int $after * @return mixed */ public function getFieldId($type='edit',$after=0) { return $this->fields ->search(function($item,$key) use ($type,$after) { return $key >= $after AND $this->isFieldEditable($item->type); }); } public function getFieldOptions(int $id) { return array_get($this->fieldoptions,$this->getField($id)->type); } /** * Return the flag for this page * * CLEAR: Clear Screen before rendering. * * @param $flag * @return bool */ public function hasFlag($flag) { return $this->frame->hasFlag($flag); } /** * Return the frame DB id * * @return mixed */ public function id() { return $this->frame->id; } /** * Current frame index * * @return mixed */ public function index() { return $this->frame->index; } /** * Return the next index */ public function index_next() { return chr(ord($this->frame->index)+1); } public function isAccessible():bool { return $this->frame->access ? TRUE : FALSE; } /** * Determine if the frame is a particular CUG * @param int $cug * @return bool */ public function isCUG(int $cug): bool { return ($this->getCUG()->id == $cug); } /** * Determine if a field is editable * * @param string $field * @return mixed */ public function isFieldEditable(string $field) { return array_get(array_get($this->fieldoptions,$field),'edit',FALSE); } public function isFieldMasked(string $field) { return array_get(array_get($this->fieldoptions,$field),'mask',FALSE); } /** * Is this frame Public * * @return bool */ public function isFramePublic(): bool { return $this->frame->closed ? FALSE : TRUE; } // @todo To implement public function isOwner(User $o):bool { return FALSE; } /** * Return the Page Number */ public function page(bool $as_array=FALSE) { return $as_array ? ['frame'=>$this->frame->frame,'index'=>$this->frame->index] : $this->frame->page; } /** * Return the next page number. * * @param bool $as_array * @return mixed */ public function pagenext(bool $as_array=FALSE) { return $as_array ? ['frame'=>$this->frame->frame,'index'=>$this->index_next()] : $this->frame->frame.$this->index_next(); } /** * Render the cost of the frame * * @param int $cost * @return string * @throws \Exception */ private function render_cost(int $cost) { if ($cost > 999) throw new \Exception('Price too high'); if ($cost > 100) $color = RED; elseif ($cost > 0) $color = YELLOW; else $color = GREEN; return sprintf($color.'% '.static::$cost_length.'.0f%s',$cost,static::$cost_unit); } /** * Render the Site Header * * @param string $header * @return bool|string */ private function render_header(string $header) { $filler = ($this->strlenv($header) < static::$header_length) ? str_repeat(' ',static::$header_length-$this->strlenv($header)) : ''; return substr($header.$filler,0,static::$header_length+strlen($header)-$this->strlenv($header)); } /** * Render the Frame Number * * @param int $num * @param string $frame * @return string * @throws \Exception */ private function render_page(int $num,string $frame) { if ($num > 999999999) throw new \Exception('Page Number too big',500); if (strlen($frame) !== 1) throw new \Exception('Frame invalid',500); return WHITE.$num.$frame.(str_repeat(' ',static::$pagenum_length-strlen($num))); } /** * Get the route for the key press * * @param string $read */ public function route(string $read) { if (! preg_match('/^[0-9]$/',$read)) throw new \Exception('Routes are single digit'); // If we dont have a route record... if (! $this->frame->route) return '*'; $key = 'r'.$read; return $this->frame->route->{$key}; } /** * Calculate the length of text * * ESC characters are two chars, and need to be counted as one. * * @param $text * @return int */ abstract function strlenv($text):int; public static function testFrame(Server $so) { // Simulate a DB load $o = new \App\Models\Frame; $o->content = ''; $o->flags = ['ip']; $o->type = 'a'; $o->frame = 999; $o->index = 'a'; $o->access = 1; $o->closed = 0; // Header $sid = R_RED.'T'.R_BLUE.'E'.R_GREEN.'S'.R_YELLOW.'T'; $o->content .= substr($sid.'-'.str_repeat('12345678901234567890',4),0,static::$header_length+(strlen($sid)-$so->strlenv($sid))). R_WHITE.'999999999a'.R_RED.sprintf('%07.0f',999).'u'; $o->content .= str_repeat('+-',18).' '.R_RED.'01'; $o->content .= 'Name: '.ESC.str_repeat('u',5).str_repeat('+-',14); $o->content .= 'Date: '.ESC.str_repeat('d',25).str_repeat('+-',4); $o->content .= 'Address: '.ESC.str_repeat('a',19).' '.str_repeat('+-',5); $o->content .= ' : '.ESC.str_repeat('a',19).' '.str_repeat('+-',5); return $o; } /** * Return the Frame Type */ public function type() { return $this->frame->type(); } }