Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 02/23/2023 in all areas

  1. Two years ago I have been working on two magazines. Both share a one instance of Processwire. I had to programme my own domain based queries. There are articles, categories, featured articles cotegory dependent (and time dependent) and also some scheduleable ads places. https://motormix.cz/ https://ceskeokruhy.cz/ Articles can be shown on both sites.
    5 points
  2. @qubism I wasn't sure if this particular feature was used. Sounds like it is -- I'll add it to TinyMCE.
    5 points
  3. It's not exactly a magazine but I did write up a move we did for a site from WP to PW: It has lots of tagging and authors and and so on. And that write up has some handy before and after metrics in which might help you convince them...
    3 points
  4. Hi @tires We're actually doing a little bit of work on this module to fix some issues we noticed with the upgrade path from sites running the old/original version to the more recent branch @adrian started. We'll test latest PHP at the same time and see if we can fix.
    3 points
  5. Hi @heldercervantes . Here are a few projects I've done, both previously done on WP. The first one is smaller 45k pages, the second one already has 484k pages. https://obukhiv.info/ https://socportal.info/
    2 points
  6. @cb2004 I've got v1 ready and am going to post in ProDevTools board this week.
    2 points
  7. Checkout the new PAGEGRID online demo ? Now I'm working on a couple of video tutorials to highlight some use cases and features. I also added a changelog to the first post so you can stay up to date. As I use PAGEGRID for some of my own projects, I'll be updating the module to meet my needs. But I'm also interested in your feedback. What features would you like to see in PAGEGRID?
    2 points
  8. Fieldtype modules Not really a tutorial, but a bunch of stuff I learnt in building a fieldtype module (FieldtypeMeasurement). That module is used as a starting point for many of the examples. Happy to take corrections and improvements ? Basics For a (full-featured) module, you actually need two module files: FieldtypeModuleName.module; and InputfieldModuleName.module The Fieldtype module defines the general settings for the fieldtype (how it appears in the setup->field page), together with how it interacts with the database, while the Inputfield module defines how the field appears when editing it in a page. In addition, for complex fields, you can define a class to hold your field values in an object. This allows you to provide custom methods for use in the API. Otherwise you can store field values as any existing type or ProcessWire object The chart below summarises the interactions of these elements and the subsequent sections describe the methods in more detail. Fieldtype Module The important methods are described below. __construct() Generally not much is required here (apart from parent::__construct();). If you have a php script with the field object then include it (require_once() ). SQL database interaction getDatabaseSchema(Field $field) This is essential. It states what data will be saved to the SQL database (via the sleepValue function - see below). An example is: public function getDatabaseSchema(Field $field) { $schema = parent::getDatabaseSchema($field); $schema['data'] = 'double NOT NULL'; // value in base units $schema['magnitude'] = 'varchar(64)'; // value in current units - needs to be text to store composite values $schema['unit'] = 'text NOT NULL'; $schema['quantity'] = 'text NOT NULL'; return $schema; } 'data' is required and is a primary key field which means that 'text' cannot be used, although varchar(64) is OK. Often (as here) it would be a numeric field of some type. Other items can be defined as required. ___sleepValue(Page $page, Field $field, $value) This determines how the ProcesssWire field object is mapped to the SQL schema. You need to return an array where the keys are the elements defined in the schema, e.g.: $sleepValue = array( 'data' => $data, 'magnitude' => $magnitude, 'unit' => $value->unit, 'quantity' => $value->quantity ); return $sleepValue; ___wakeupValue(Page $page, Field $field, $value) This is basically the inverse of sleepValue - mapping the array from the database into the field object. In the example below, the field object (Measurement) extends WireData. Properties in WireData objects can be stored in the 'data' property via 'get' and 'set' methods. getBlankValue() (see next section) performs the initial setting of these - config values for the field (see below) can be set, but otherwise the settings are just placeholders. Properties set to a WireData object can also be set or accessed directly, like $item->property or using array access like $item[$property] public function ___wakeupValue(Page $page, Field $field, $value) { // if for some reason we already get a valid value, then just return it if($value instanceof Measurement) return $value; // start a blank value to be populated $measurement = $this->getBlankValue($page, $field); // if we were given a blank value, then we've got nothing to do: just return a blank Measurement object if(empty($value) || !is_array($value)) return $measurement; // create new Measurement object $measurement->quantity = $value['quantity']; // ... custom processing ... $measurement->baseMagnitude = $value['data']; if($value['unit']) { $measurement->unit = $value['unit']; $units = $measurement->getUnits(); if(array_key_exists($value['unit'], $units) && isset($value['magnitude'])) { $measurement->magnitude = explode('|', $value['magnitude']); } else { $measurement->magnitude = $measurement->baseMagnitude; $measurement->unit = $measurement->units->base; $this->error('... error msg ...'); } } else { //... } if(!is_array($measurement->magnitude)) $measurement->magnitude = [$measurement->magnitude]; return $measurement; } getBlankValue(Page $page, Field $field) This should return an empty item of the appropriate type. For instance, if your field object is an array, just return array(); If the field type is an object then you will need to return a 'new ObjectClassName()'. Pre-fill any config values from the Fieldtype settings but leave blank those which are set in the Inputfield, In the above example, the field object data was set as follows: public function getBlankValue(Page $page, Field $field) { /* @var $field FieldtypeMeasurement */ $measurement = new Measurement($field->quantity, null, []); if ($field->quantity) $measurement->set('quantity', $field->quantity); $measurement->set('magnitude', []); $measurement->set('shortLabel', null); $measurement->set('plural', null); return $measurement; } If your object has configurable fields that can be modified according to context (as defined in getConfigAllowContext() - see below), then you will need to deal with this in getBlankValue too, e.g. : public function getBlankValue(Page $page, Field $field): Measurement { //NB Field details may differ between templates so we need to get the field in context $context = ($page && $page->id) ? $field->getContext($page->template) : $field; ... $measurement = new Measurement($context->quantity, null, []); ... return $measurement; } But note that this does not completely deal with the situation where the field is in repeater matrix items where the types might have different contexts - there you might need: public function getBlankValue(Page $page, Field $field): Measurement { //NB Field details may differ between templates so we need to get the field in context $context = ($page && $page->id) ? $field->getContext($page->template) : $field; ... // if($page instanceof RepeaterMatrixPage) { // This does not always work - see edit note if($page->template->pageClass == 'RepeaterMatrixPage') { if($page->getField($field->name)) { $context = $page->fieldgroup->getFieldContext($field, "matrix$page->repeater_matrix_type"); } } ... $measurement = new Measurement($context->quantity, null, []); ... return $measurement; } See this post for more details. Configuration ___getConfigInputfields(Field $field) This defines how the Details tab in the field setup page will look. The best thing to do here is to find a fieldtype module that is similar to the one you want if you are uncertain. Broadly the process is: define the config object - $inputfields = parent::___getConfigInputfields($field); for each config item, use the appropriate input field, e.g . $f = $this->modules->get("InputfieldSelect"); assign the relevant attributes. $f->name = $f_name; is important as it enables the item to be subsequently referred to as $field->f_name in, for example, getBlankValue(). append each item ($inputfields->append($f);) and return $inputfields; ___getConfigAllowContext(Field $field) This determines if the above input fields are allowed to have unique values per Fieldgroup assignment enabling the user to configure them independently per template in the admin, rather than sharing the same setting globally. E.g. public function ___getConfigAllowContext(Field $field) { $a = array('quantity', 'units', 'hide_quantity', 'show_update'); return array_merge(parent::___getConfigAllowContext($field), $a); } In this example the settings 'quantity', 'units', 'hide_quantity' and 'show_update' can be varied in different template contexts. Link with the Inputfield module This is done with getInputfield(Page $page, Field $field) e.g.: public function getInputfield(Page $page, Field $field) { $inputfield = $this->wire('modules')->get('InputfieldMeasurement'); $inputfield->setField($field); return $inputfield; } If you want to reference the current page in the inputfield, you will also need to include $inputfield->setPage($page); If your fieldtype is an object and you want full context flexibility including for different repeater matrix item types, then you may need to use this: public function getInputfield(Page $page, Field $field): Inputfield { $inputfield = $this->wire('modules')->get('InputfieldMeasurement'); if($page->template->pageClass == 'RepeaterMatrixPage') { if($page->getField($field->name)) { $field_in_context = $page->fieldgroup->getFieldContext($field, "matrix$page->repeater_matrix_type"); if($field_in_context) { $field = $field_in_context; } } } $inputfield->setField($field); $inputfield->setPage($page); return $inputfield; } Inputfield Module __construct() Generally not much is required here (apart from parent::__construct();). If you have a php script with the field object then include it (require_once() ). Configuration ___getConfigInputfields() This is pretty much exactly the same construction as the similar method in the Fieldtype class. The only difference is that these settings will appear in the 'input' tab of the fieldtype settings, rather than the 'details' tab. ___getConfigAllowContext(Field $field) This is the equivalent to the Fieldtype::getConfigAllowContext() method, but for the "Input" tab rather than the "Details" tab. Input and output The key methods for this module are to render it from the fieldtype and database and to process the user inputs. ___render() $field = $this->field will have the field config settings from the fieldtype module. $this->attr('value') will have the current values for the field. If there is no current values then, if using a field object, you will need to create a new object, e.g.: if($this->attr('value')) $value = $this->attr('value'); // Measurement object else { $value = new Measurement(); } You can then use $field and $value to display the inputfield (which might be a fieldset) as required using the appropriate pre-existing inputfield modules. (Again, find an existing module that is similar, if you are uncertain). renderValue() This is required where the field is locked (not editable) and therefore render() does not apply. Get the value with $value = $this->attr('value'); and then apply the required formatting, returning the output string. ___processInput(WireInputData $input) Here you take the inputs and update the field values. As in render(), set $value = $this->attr('value') ; and then modify $value for the inputs. For example, set $name = $this->attr('name'); and then assign the inputs thus: $input_names = array( 'magnitude' => "{$name}_magnitude", 'unit' => "{$name}_unit", 'quantity' => "{$name}_quantity", 'update' => "{$name}_update" ); You can then loop through the inputs and carry out the required updates. The example below is slightly convoluted but illustrates this: foreach($input_names as $key => $name) { if(isset($input->$name) && $value->$key != $input->$name) { if($key == 'magnitude') { $input->$name = trim($input->$name); if(!is_numeric($input->$name)) { $magnitude = explode('|', $input->$name); $magnitude = array_filter($magnitude, 'is_numeric'); $value->set($key, $magnitude); } else { $value->set($key, [$input->$name]); } } else { $value->set($key, $input->$name); } $this->trackChange('value'); } } When all is done, return $this; Custom classes As mentioned earlier, for complex field types it may be useful to set up custom classes to hold the data. Typically a custom class would extend WireData, which is ProcessWire's class designed for runtime data storage. It provides this primarily through the built-in get() and set() methods for getting and setting named properties to WireData objects. The most common example of a WireData object is Page, the type used for all pages in ProcessWire. Properties set to a WireData object can also be set or accessed directly, like $item->property or using array access like $item[$property]. If you foreach() a WireData object, the default behaviour is to iterate all of the properties/values present within it. Do not declare any such properties in your class (or declare properties with the same name) otherwise you will end up with two properties, one in the 'data' array and one outside it and endless confusion will result. It is advisable to put any such classes in your own namespace. In that case, you will need to include 'use' statements in your script - e.g. use ProcessWire\{FieldtypeMeasurement, WireData}; use function ProcessWire\{wire, __}; and also include use statements in your module scripts, e.g. use MetaTunes\MeasurementClasses\Measurement;
    1 point
  9. PAGEGRID – A visual page builder for ProcessWire. Design fully responsive websites (or parts of them) without writing any code. Use ProcessWire's native templates (and fields) to create your own blocks. Rearrange and resize items in a visual way and use inline or modal editing to quickly edit the content of your website. Try PAGEGRID for free PAGEGRID is not free software. However, you can try PAGEGRID on your local machine or on a test server as long as you need to make sure it is the right tool for your next project. … and when you’re convinced, buy your license. Get it here Download from GitHub Download from Module Directory Requirements ProcessWire 3.0.210 or greater Installation Go to “Modules > Site > Add New“ in your admin Paste the Module Class Name "FieldtypePageGrid" into the field “Add Module From Directory“ Click “Get Module Info“ On the overview, click “Download And Install“ On the following screen, click “Install Now“ More install options Module install guide Site profile install guide Get up and running Quick start Create your own blocks or install the PageGridBlocks Module (installs premade templates and fields for PAGEGRID blocks). What's PAGEGRID? page-grid.com – Get to know PAGEGRID. Documentation – Read the official documentation. Issues – Report bugs and other problems. Forum – Whenever you get stuck, don't hesitate to reach out for questions and support. Why I build it ProcessWire is super flexible in itself and lets me build whatever I want. But building a custom website can be a lot of work. For some projects, I've ended up using a lot of templates and fields. To make my pages more flexible, I sometimes build my own little page builder based on the RepeaterMatrix or PageTable module. While these page builders were great for the specific site I was building them for, they were never flexible enough to be used for new projects, so I ended up customizing them frequently. The more complex they became, the harder it became to use them for my clients. After playing around with some WYSIWYG page builder tools, I realized that while they can save me a lot of time, they can also be very limiting or have expensive subscriptions and somehow tie you to their ecosystem. So I decided to build my own page builder based on the most flexible CMS I knew. Concept This fieldtype Renders block templates and adds drag and drop functionality in admin, as well as enable inline editing for text, and file fields. It also let's you manipulate CSS in a visual way to design fully responsive websites (or parts of them) without writing code. The fieldtype comes with an optional style panel to manipulate CSS properties directly on the page. You can customize the panel or disable it completely from the module settings (and just use a CSS file that you include in your template). The data to style the items is stored directly on the item using PW's meta data (no extra fields are created). Don't want to give your client all that power? Use ProcessWire’s powerful permission system to control what your clients can edit. You can then also grant access individually to the style panel, resize or drag functionality using ProcessWire's build in permission system. Features Blocks are just pages Blocks are defined by native PW templates and fields Manipulate CSS grid or flexbox based layouts in a visual way to design fully responsive websites (or parts of them) Encapsulated frontend code (PAGEGRID renders the template of your frontend inside an iframe in the backend) Design and editing features can be disabled for certain roles (using ProcessWire's build in permission system) Inline editing of text, textarea, TinyMCE (supports latest version), ckeditor and file fields Simply drag and resize to manipulate grid items directly inside the backend Manipulate grid columns and rows directly on the page (use any number of columns you want) All style manipulations are saved as JSON and used to generate dynamic styles that you render in your main template (no inline styles) Nested groups/grids (child pages of nested blocks are created under group parent) The style panel supports adding custom classes and assigning styles to them. These classes can be used globally on all pages (a css class is also a page) The style panel supports selecting html tags to style tags globally across the whole site Global blocks work with page reference field (changes on one page, changes all blocks on all pages) Manual and auto placement of grid items blocks and nested blocks can be cloned Redo/undo and copy/paste shortcuts Editing block items in modal sidebar immediately updates frontend (Ajax Save). Define custom icons for your blocks via native template settings (template -> advanced -> icon) Automatic page save (Changes are getting saved via ajax, no need to click the save button) NEW: Option to automatically load lazysizes lazyloader (V 0.1.0) Changelog V 0.1.0: Feature: Option to automatically load lazysizes lazyloader (Module Settings > Plugins). V 0.1.5: Fixed bug: Tabs not working when editing items via modal panel. V 0.1.6: Fixed bug: Setting height in VH unit was not working. V 0.1.7: Feature: Option to hide save button (and use automatic ajax save ) if there are no other fields than PAGEGRID on the content tab (Module Settings > Interface). V 0.2.0: Fixed bug: Custom block wrapper element <p> was not working with inline editor. V 0.2.0: Fixed bug: Inline editor would sometimes not save after clicking cancel and then edit item again. V 0.2.0: Feature: Now it's possible to add classes to elements inside richt text fields via style panel. V 0.2.0: Fixed bug: Inline editor was not working after first item was added to the page (needed reloading the page). V 0.2.1: Feature: Updated PageGridBlocks Module: Using TinyMCE as the default editor. V 0.2.1: Feature: Updated PageGridBlocks Module: Group/container wrapper element can now be changed to <div>, <section>, <article>, <header>, <footer>, <nav>. Thanks to everyone who helped me improve my coding skills and for the support of this great community! Special thanks to @diogo for the valuable feedback and @ryan for this great CMS and his support for the PageFrontEdit module!
    1 point
  10. Seasons Greetings to you all! I'm wondering, does anyone know of a good online magazine powered by processwire? Like a proper magazine with sections, articles, tags, featured articles, related articles and all that? Think stuff like https://www.archdaily.com/ or https://www.designboom.com/ I'm in that lovely position of trying to convince someone who's thinking wordpress to build their site properly.
    1 point
  11. I'd say, not much, just that the the "current" page in the dashboard is the page "/processwire/page/edit", not the page actually being edited, that's why trashing in the backend works. Your analysis is otherwise spot on, I was about to write the same. I'd suggest to raise another, more specific, issue on github. I don't see any reason not to trash the current page if the very next step is a redirect.
    1 point
  12. UPDATE: I noticed I can trash these pages through the api as long as do it from other pages. In other words, it is impossible to trash the current page. I found this github issue where someone pointed out that the behavior changed in this commit to not allow current page to be deleted. This only happens on PW installs that happened after a hard-coded date, so maybe why not many people run into this? I didn't see behavior (current page cannot be deleted) documented in the $pages or $page api. In the code of the commit, there are clear error messages returned. Here is the message for trying to delete the current page: "it is the current page being viewed, try \$pages->trash() instead"; But that message does not show in my case. Also, I'm not trying to DELETE the page, but TRASH it. I tried using $pages->trash($page) instead of $page->trash() but still I get the same error. This seems like it should be a common behavior...have a Trash button on the edit page for some resource, that posts back to the page and the page is trashed, then redirecting to a different page. The Admin dashboard operates this way to trash pages. What am I missing?
    1 point
  13. @ryan awesome, I cant wait to give it a try
    1 point
  14. I'm really happy with InputfieldTinyMCE so far. And as threatened to in reply to an earlier core updates post, I took a stab at migrating my autocomplete module over from InputfieldCKEditor. All in all, it was quite a pleasant experience and the JS side was really straight forward. The change also involved a lot of refactoring, and in the aftermath of that there's still a good bit of house cleaning to do, but for anybody willing to toy around with it, there's an alpha release of InlineCompleteTinyMCE on GitHub. A snapshot of InlineCompleteTinyMCEActionUsers in action: And the corresponding configuration in the field's "Input" tab (added to the TinyMCE config section): I haven't tested it with lazy loading or the inline editor yet.
    1 point
  15. Nice overview! For full fledged field types, you'll often also want to implement Fieldtype::getConfigAllowContext and Inputfield::getConfigAllowContext. They return an array with the names of those config fields defined in their respective getConfigInputfields method that may be overridden in template or fieldgroup context. The Inputfield class by default returns these config fields: return array( 'visibility', 'collapsed', 'columnWidth', 'required', 'requiredIf', 'requiredAttr', 'showIf' ); So make sure to call parent::getConfigInputfields first, then add your own config inputs. A good example is how InputfieldText does this: public function ___getConfigAllowContext($field) { $a = array('initValue', 'pattern', 'placeholder', 'maxlength', 'minlength', 'required', 'requiredAttr'); return array_merge(parent::___getConfigAllowContext($field), $a); } Now the initValue, pattern, placeholder, maxlength, required and requiredAttr settings can be overridden in each template the field is used in.
    1 point
×
×
  • Create New...