Jump to content

suntrop

Members
  • Content Count

    293
  • Joined

  • Last visited

Community Reputation

46 Excellent

1 Follower

About suntrop

  • Rank
    Sr. Member

Profile Information

  • Gender
    Not Telling
  • Location
    Germany, Cologne

Recent Profile Visitors

4,011 profile views
  1. Ok, but that is not what I am looking for. I'd like to keep it in one field. But I guess I'll revert everything to where I had just two textarea fields. That wasn't good to validate, but it worked.
  2. Thanks, but I don't get what you are trying to say. I need to have two separate addresses, because shipping is mostly other than billing address. But I still can't see how PW works exactly saving data. First PW calls sanitizeValue() and next calls sleepValue(). But when I check $value in sleepValue() $sleepValue = [ 'data_billing_company' => isset($value->billing_company) ? $value->billing_company : $page->billing_company, //… 'data_shipping_gender' => isset($value->shipping_gender) ? $value->shipping_gender : $page->shipping_gender, ]; it doesn't work either. Seems to be all $value->xxx are always set, even when not set when saving data from API.
  3. I created a custom fieldtype to store two addresses. It receives its data from a custom form and will later use it in a PDF. However, the problem is, users can save billing and shipping address separately with two slightly different frontend forms. When I save one form only data saved by this from will be saved, all other fields get deleted. So if the user saves his shipping address all billing fields are empty after the page is saved. First version of my fieldtype only had two textarea fields and everything worked fine. But now changed to have more control over every data field (name, street, etc.). I started from copying the Dimension fieldtype, which was a copy of the MapMaker fieldtype I guess. Problem should be inside Address::set() or the ___sleepValue() // Frontend form to save billing address $p->of(false); $p->address->billing_gender = sanitizer()->string( input()->post('gender') ); $p->address->billing_firstname = sanitizer()->string( input()->post('first_name') ); $p->address->billing_lastname = sanitizer()->string( input()->post('last_name') ); $p->address->billing_street = sanitizer()->string( input()->post('street') ); $p->address->billing_zipcode = sanitizer()->string( input()->post('zipcode') ); $p->address->billing_city = sanitizer()->string( input()->post('city') ); $p->address->billing_company = sanitizer()->string( input()->post('company') ); $p->address->billing_vatid = sanitizer()->string( input()->post('vatid') ); $p->address->billing_country = sanitizer()->int( input()->post('country') ); $save = $p->save('address'); <?php namespace ProcessWire; /** * ProcessWire Address Fieldtype * */ class FieldtypeAddress extends Fieldtype { /** * Initialize this Fieldtype * */ public function init() { parent::init(); } /** * get Inputfield for this fieldtype, set config attributes so they can be used in the inputfield * */ public function getInputfield(Page $page, Field $field) { $address = $this->modules->get('InputfieldAddress'); return $address; } /** * Get the database schema for this field * * @param Field $field In case it's needed for the schema, but usually should not. * @return array */ public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'TEXT NOT NULL'; // user last name $schema['data_billing_firstname'] = 'TEXT NOT NULL'; $schema['data_billing_gender'] = 'TEXT NOT NULL'; $schema['data_billing_company'] = 'TEXT NOT NULL'; $schema['data_billing_vatid'] = 'TEXT NOT NULL'; $schema['data_billing_street'] = 'TEXT NOT NULL'; $schema['data_billing_zipcode'] = 'TEXT NOT NULL'; $schema['data_billing_city'] = 'TEXT NOT NULL'; $schema['data_billing_country'] = 'TEXT NOT NULL'; $schema['data_shipping_lastname'] = 'TEXT NOT NULL'; $schema['data_shipping_firstname'] = 'TEXT NOT NULL'; $schema['data_shipping_company'] = 'TEXT NOT NULL'; $schema['data_shipping_street'] = 'TEXT NOT NULL'; $schema['data_shipping_zipcode'] = 'TEXT NOT NULL'; $schema['data_shipping_city'] = 'TEXT NOT NULL'; $schema['data_shipping_country'] = 'TEXT NOT NULL'; $schema['data_shipping_department']= 'TEXT NOT NULL'; $schema['data_shipping_gender'] = 'TEXT NOT NULL'; // Remove index from data column unset($schema['keys']['data']); return $schema; } /** * Any value will get sanitized before setting it to a page object * and before saving the data * */ public function sanitizeValue(Page $page, Field $field, $value) { if(!$value instanceof Address) $value = $this->getBlankValue($page, $field); if ( $value->isChanged('billing_company') || $value->isChanged('billing_gender') || $value->isChanged('billing_vatid') || $value->isChanged('billing_firstname') || $value->isChanged('billing_lastname') || $value->isChanged('billing_street') || $value->isChanged('billing_zipcode') || $value->isChanged('billing_city') || $value->isChanged('billing_country') || $value->isChanged('shipping_company') || $value->isChanged('shipping_firstname') || $value->isChanged('shipping_lastname') || $value->isChanged('shipping_street') || $value->isChanged('shipping_zipcode') || $value->isChanged('shipping_city') || $value->isChanged('shipping_country') || $value->isChanged('shipping_department') || $value->isChanged('shipping_gender') ) { $page->trackChange($field->name); } return $value; } /** * Get raw data values from database and perhaps adjust data for $page output * */ public function ___wakeupValue(Page $page, Field $field, $value) { if(empty($value) ) return false; // get blank $address = $this->getBlankValue($page, $field); // TODO: required? $country = $this->pages()->get( (int)$value['data_billing_country'] ); $address->billing = WireArray::new([ 'gender' => (string)$value['data_billing_gender'], 'company' => (string)$value['data_billing_company'], 'vatid' => (string)$value['data_billing_vatid'], 'first_name' => (string)$value['data_billing_firstname'], 'last_name' => (string)$value['data'], 'street' => (string)$value['data_billing_street'], 'zipcode' => (string)$value['data_billing_zipcode'], 'city' => (string)$value['data_billing_city'], 'country' => $country, 'block' => (string)$value['data_billing_company'] . PHP_EOL . (string)$value['data_billing_firstname'] . ' ' . (string)$value['data'] . PHP_EOL . (string)$value['data_billing_street'] . PHP_EOL . (string)$value['data_billing_zipcode'] . ' ' . (string)$value['data_billing_city'] . PHP_EOL . (string)$country->title, ]); $country = $this->pages()->get( (int)$value['data_shipping_country'] ); $address->shipping = WireArray::new([ 'gender' => (string)$value['data_shipping_gender'], 'company' => (string)$value['data_shipping_company'], 'department' => (string)$value['data_shipping_department'], 'first_name' => (string)$value['data_shipping_firstname'], 'last_name' => (string)$value['data_shipping_lastname'], 'street' => (string)$value['data_shipping_street'], 'zipcode' => (string)$value['data_shipping_zipcode'], 'city' => (string)$value['data_shipping_city'], 'country' => $country, 'block' => (string)$value['data_shipping_company'] . PHP_EOL . (string)$value['data_shipping_firstname'] . ' ' . (string)$value['data_shipping_lastname'] . PHP_EOL . (string)$value['data_shipping_street'] . PHP_EOL . (string)$value['data_shipping_zipcode'] . ' ' . (string)$value['data_shipping_city'] . PHP_EOL . (string)$country->title, ]); return $address; } /** * Get data from $page and adjust data for storing in database * */ public function ___sleepValue(Page $page, Field $field, $value) { // throw error if value is not of the right type if(!$value instanceof Address) throw new WireException('Expecting an instance of Address'); $sleepValue = [ 'data_billing_company' => (string)$value->billing_company, 'data_billing_vatid' => (string)$value->billing_vatid, 'data_billing_gender' => (string)$value->billing_gender, 'data_billing_firstname' => (string)$value->billing_firstname, 'data' => (string)$value->billing_lastname, 'data_billing_street' => (string)$value->billing_street, 'data_billing_zipcode' => (string)$value->billing_zipcode, 'data_billing_city' => (string)$value->billing_city, 'data_billing_country' => (string)$value->billing_country, 'data_shipping_company' => (string)$value->shipping_company, 'data_shipping_firstname' => (string)$value->shipping_firstname, 'data_shipping_lastname' => (string)$value->shipping_lastname, 'data_shipping_street' => (string)$value->shipping_street, 'data_shipping_zipcode' => (string)$value->shipping_zipcode, 'data_shipping_city' => (string)$value->shipping_city, 'data_shipping_country' => (string)$value->shipping_country, 'data_shipping_department' => (string)$value->shipping_department, 'data_shipping_gender' => (string)$value->shipping_gender, ]; return $sleepValue; } /** * Format value for output * */ public function ___formatValue(Page $page, Field $field, $value) { return $value; } /** * * Add mapping to different name for use in page selectors * This enables us to use it like "field.min=10, field.max<=200, field.graduation>10" */ public function getMatchQuery($query, $table, $subfield, $operator, $value) { if ($subfield == 'billing_company') $subfield = 'data_billing_company'; if ($subfield == 'billing_gender') $subfield = 'data_billing_gender'; if ($subfield == 'billing_vatid') $subfield = 'data_billing_vatid'; if ($subfield == 'billing_firstname') $subfield = 'data_billing_firstname'; if ($subfield == 'billing_lastname') $subfield = 'data'; if ($subfield == 'billing_street') $subfield = 'data_billing_street'; if ($subfield == 'billing_zipcode') $subfield = 'data_billing_zipcode'; if ($subfield == 'billing_city') $subfield = 'data_billing_city'; if ($subfield == 'billing_country') $subfield = 'data_billing_country'; if ($subfield == 'shipping_company') $subfield = 'data_shipping_company'; if ($subfield == 'shipping_firstname') $subfield = 'data_shipping_firstname'; if ($subfield == 'shipping_lastname') $subfield = 'data_shipping_lastname'; if ($subfield == 'shipping_street') $subfield = 'data_shipping_street'; if ($subfield == 'shipping_zipcode') $subfield = 'data_shipping_zipcode'; if ($subfield == 'shipping_city') $subfield = 'data_shipping_city'; if ($subfield == 'shipping_country') $subfield = 'data_shipping_country'; if ($subfield == 'shipping_department') $subfield = 'data_shipping_department'; if ($subfield == 'shipping_gender') $subfield = 'data_shipping_gender'; return parent::getMatchQuery($query, $table, $subfield, $operator, $value); } /** * there's none compatible * */ public function ___getCompatibleFieldtypes(Field $field) { return null; } /** * blank value is an WireData object Address * */ public function getBlankValue(Page $page, Field $field) { return new Address(); } /** * Get any inputfields used for configuration of this Fieldtype. * * This is in addition any configuration fields supplied by the parent Inputfield. * * @param Field $field * @return InputfieldWrapper * */ public function ___getConfigInputfields(Field $field) { $inputfields = parent::___getConfigInputfields($field); // nothing yet return $inputfields; } } /** * Helper WireData Class to hold an address object * */ class Address extends WireData { public function __construct() { // bd('address construct'); // $this->set('billing_company', null); // $this->set('billing_gender', null); // $this->set('billing_vatid', null); // $this->set('billing_firstname', null); // $this->set('billing_lastname', null); // $this->set('billing_street', null); // $this->set('billing_zipcode', null); // $this->set('billing_city', null); // $this->set('billing_country', null); // $this->set('shipping_company', null); // $this->set('shipping_firstname', null); // $this->set('shipping_lastname', null); // $this->set('shipping_street', null); // $this->set('shipping_zipcode', null); // $this->set('shipping_city', null); // $this->set('shipping_country', null); // $this->set('shipping_department', null); // $this->set('shipping_gender', null); } public function set($key, $value) { bd('VALUE: '.$value, 'address set: ' . $key ); // if ( // $key == 'billing_company' || // $key == 'billing_gender' || // $key == 'billing_vatid' || // $key == 'billing_firstname' || // $key == 'billing_lastname' || // $key == 'billing_street' || // $key == 'billing_zipcode' || // $key == 'billing_city' || // $key == 'billing_country' || // $key == 'shipping_company' || // $key == 'shipping_firstname' || // $key == 'shipping_lastname' || // $key == 'shipping_street' || // $key == 'shipping_zipcode' || // $key == 'shipping_city' || // $key == 'shipping_department' || // $key == 'shipping_gender' || // $key == 'shipping_country' // ) { // if(!is_string($value) && !is_int($value) && !is_null($value)) { // $value = $this->$key ? $this->$key : 0; // throw new WireException("Address Object only accepts string (string) values"); // } // } return parent::set($key, $value); } public function get($key) { // bd('address get: ' . $key); return parent::get($key); } }
  4. I have a field 'history' to track some field's changes. When changing, for example, the field 'state' in the PW backend I get correct result (1 entry). But when I set 'state' from a template file with: $page->of(false); $page->state = 1138; // field state is Page reference and 1138 is a state with title New $page->save('state'); $page->of(true); … I have two entries in the 'history' field: 2020-02-06 16:19:40 State -/- => New 2020-02-06 16:19:40 State -/- => -/- $this->addHookAfter('Page(template=testtemplate)::changed()', function($event) { /* @var $event HookEvent */ /* @var $page Page */ $page = $event->object; $field = $event->arguments(0); // field $old = $event->arguments(1); // old value $new = $event->arguments(2); // new value $track_fields = ['title', 'state', 'items']; if (!in_array($field, $track_fields)) return; $label = fields()->get($field)->getLabel(); bd($old, $label . ' old'); bd($new, $label . ' new'); $old_value = ($old instanceof Page) ? $old->title : '-/-'; $new_value = ($new instanceof Page) ? $new->title : '-/-'; $history = date('Y-m-d H:i:s') . "\t" . $label . ' ' . $old_value . ' => ' . $new_value; // $page->history = $page->history . PHP_EOL . $history; $page->setAndSave('history', $page->history . PHP_EOL . $history); }); Running this in TracyDebugger will result in two entries as well: $page->setAndSave('state', 'new');
  5. I changed my default setcookie() functions to the newly added $input->cookie method as described in https://processwire.com/blog/posts/pw-3.0.141/ Just noticed no cookies are saved when using PW's $input->cookie->set() (in template file). I am using PW 3.0.148, PHP 7.3 and testing with setcookie('test', 'test') works just fine. Tried to add age, path and domain as well, but nothing works. setcookie('defaultsetcookie', 'works'); $config->cookieOptions = [ 'age' => 604800, 'path' => null, 'domain' => null, 'secure' => null, 'httponly' => false, 'fallback' => true ]; $c = $input->cookie->set('pwcookie', 'wontwork'); print_r( $c ); print_r( $_COOKIE ); Output ProcessWire\WireInputDataCookie Object ( [defaultsetcookie] => works [wire] => 89ortkjmq5dsgb7em2h6ljod9v [pwcookie] => wontwork ) Array ( [defaultsetcookie] => works [wire] => 89ortkjmq5dsgb7em2h6ljod9v [pwcookie] => wontwork ) Any idea what is wrong? It is strange setcookie() works but PW methods won't.
  6. When I check if the page has an image attached, I get the error PHP Warning: count(): Parameter must be an array or an object that implements Countable in But this should be the correct way if ( count(page()->images) ) { $heroimage = page()->images->first()->url; } elseif ( pages()->get('/')->images->first()->url ) { $heroimage = pages()->get('/')->images->first()->url; } I found some issues with this on GitHub, but it was fixed. Using PHP 7.3 and PW 148 (same issue before update).
  7. I've got an existing (pro field) textareas field containing two Page Reference fields. Do you know if subfields are supported using field templates? Those won't work $page->render('related', 'textareas/subfield'); $page->render->textareas; // will output both subfields
  8. Thanks for the link. Had not found the source. It seems the 'data-close' button will only listen for clicks if it is defined in 'data-buttons' as well. But all buttons living there will get the same design. Not that good, when Save and Cancel look the same, but it will work for now.
  9. In a custom process module I open a modal window containing a <form>. When I click the submit button, the modal stays open. I can't find a way, other the X from the title bar, to close the modal window. - How can close the modal, when the submit button was hit? - Or, how can I implement the buttonpane with the Save button like the PageTable module? - Or, at least, how can I include my own close button? That's how I open the modal: $field = wire('modules')->get('InputfieldButton'); $field->attr('id+name', 'status_update'); $field->attr('class', 'pw-modal pw-modal-small ui-button ui-widget ui-corner-all head_button_clone ui-state-default'); $field->attr('value', 'Update'); $field->attr('data-href', './status-update/?id=' . $op->id); $form->add($field); That's my save button $submit = $modules->get('InputfieldSubmit'); $submit->attr('data-close', 'close'); $submit->attr('value', 'Save'); $submit->attr('name', 'submit_dashboard_status_action'); $form->add($submit);
  10. This one has a strange outcome. For example … find('template=city, children.children.created<"14 days ago"'); Result: Hamburg Berlin Hannover Wiesbaden find('template=city, children.children.created>"14 days ago"'); Result: Hamburg Bremen find('template=city, children.children.created<"14 days ago", !children.children.created>"14 days ago"'); Result: Hamburg
  11. Hadn't luck with timestamp either. Changed it now to this, and it's working for me. Anyway, thanks for your answer! $remove = wire('pages')->find('template=city, children.children.created>"14 days ago"'); $cities = wire('pages')->find('template=city, children.children.created<"14 days ago"')->removeItems($remove);
  12. I was trying to get pages that have grand children older than 14 days, but not newer than 14 days. $cities = wire('pages')->find('template=city, !children.children.created>"14 days ago"'); $cities = wire('pages')->find('template=city, children.children.created<"14 days ago"')->not('template=city, children.children.created>"14 days ago"'); $cities = wire('pages')->find('template=city, children.children.created<"14 days ago"')->remove('template=city, children.children.created>"14 days ago"'); The first one is, I guess, not supported by PW (not to mention something like created!>time), but the last two should work in my option. The filter() doesn't work either here. Please note, that all city pages have grand children older than 14 days, so I can't just find('template=cities, children.children.created<"14 days ago"') because that includes those pages having grand children from yesterday for example.
  13. Hi OLSA, thanks for your code. On the second look, I think this can work for me. But this one focuses on the posts again, not the groups. I think I found a fairly simple approach, but haven't tested thoroughly $i = 0; foreach ($user->groups() as $group) { $post = $group->find('template=posting, sort=-created, limit=1')->first(); $user->groups()->eq($i)->data('latest', $post->created); $i++; } // later I can foreach($user->groups()->sort('-latest') as $group) {…} I am just adding a new property 'latest' with the timestamp and can sort my groups by this new property later where I output my groups.
  14. This makes $posts only contain the posts. But I need a list of the groups. The webpage looks something like this at the end: See latest posts from … » GROUP #2 (with a Post from 2018-12-05) » GROUP #1 (with a Post from 2018-12-01) » GROUP #x (with …) » GROUP #y (with …) …
  15. Hi there! I'd like to sort some pages – kind of categorised blog posts. CMS: Blog - GROUP #1 - Cat A - Post 2018-12-01 - Post 2018-10-10 - Cat B - Post 2018-11-11 - Post 2018-05-05 - GROUP #2 - Cat C - Post 2018-12-05 - Post 2018-01-01 - GROUP … The user can select from different options to sort GROUP #1, GROUP #2, GROUP #n pages by date, title and … latest posts. If the user selects "Latest posts" it should show as: See latest posts from … » GROUP #2 (with a Post from 2018-12-05) » GROUP #1 (with a Post from 2018-12-01) » … Is this possible with a selector/PW's API? I guess it doesn't work like that, but perhaps I can utilise the newly added option to create a custom WireArray (https://processwire.com/blog/posts/processwire-3.0.117-core-updates/)? I think I have to loop the groups, find the latest post and create a new property with the timestamp.
×
×
  • Create New...