Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 08/13/2013 in all areas

  1. Hi all, Just to let you know that Wanze and I are now working on this (or rather, he's doing most of the coding and am doing the talking ). We concluded it was best to (first) develop a PW Handsontable class that other modules can load, configure settings and use its methods to render CRUD tables. This way, the class can be integrated into other modules as required, e.g. Batcher and use all or only some of its methods, e.g. limited CRUD, readOnly, etc. At this stage, this means that the module is primarily for developers since there is no UI to configure it. However, once the class has been tested, a Process Module should follow. You can follow the project here: https://github.com/wanze/PwHandsontable. Suggestions welcome, thanks!
    7 points
  2. Hey, after many questions here in the forums, (by the way, thank you for the great, fast and straightforward support) we would like to show one of our projects, which we realized using to awesome Processwire. A designer friend of us asked us to transfer one of her webdesigns into a running website. The customer is one of Germany's most experienced and well-known horse transportation companies. The multilingual website is based on Processwire and should be completely responsive, so please feel free to resize your browser or view it on your smartphone or tablet. http://www.johannsmann-pferdetransporte.de Currently the customer is about to fill the website with more data. So please don't mind, if some pages are not complete. Every comment is welcome - either suggestions or constructive criticism. Thank you so much for this great project and this stunning community!
    4 points
  3. --------------------------------------------------------------------------------------------------------------------------------- when working with PW version 2.6+, please use Pim2, not Pim! read more here on how to change from the older to the newer version in existing sites --------------------------------------------------------------------------------------------------------------------------------- PageImage Manipulator, API for version 1 & 2 The Page Image Manipulator is a module that let you in a first place do ImageManipulations with your PageImages. - And in a second place there is the possibility to let it work on any imagefile that exists in your servers filesystem, regardless if it is a 'known PW-image'. The Page Image Manipulator is a Toolbox for Users and Moduledevelopers. It is written to be as close to the Core ImageSizer as possible. Besides the GD-filterfunctions it contains resize, crop, canvas, rotate, flip, sharpen, unsharpMask and 3 watermark methods. How does it work? You can enter the ImageManipulator by calling the method pim2Load(). After that you can chain together how many actions in what ever order you like. If your manipulation is finished, you call pimSave() to write the memory Image into a diskfile. pimSave() returns the PageImage-Object of the new written file so we are able to further use any known PW-image property or method. This way it integrates best into the ProcessWire flow. The three examples above put out the same visual result: a grayscale image with a width of 240px. Only the filenames will slightly differ. You have to define a name-prefix that you pass with the pimLoad() method. If the file with that prefix already exists, all operations are skipped and only the desired PageImage-Object gets returned by pimSave(). If you want to force recreation of the file, you can pass as second param a boolean true: pim2Load('myPrefix', true). You may also want to get rid of all variations at once? Than you can call $pageimage->pim2Load('myPrefix')->removePimVariations()! A complete list of all methods and actions are at the end of this post. You may also visit the post with tips & examples for users and module developers. How to Install Download the module Place the module files in /site/modules/PageImageManipulator/ In your admin, click Modules > Check for new modules Click "install" for PageImageManipulator Done! There are no configuration settings needed, just install and use it. Download (version 0.2.0) get it from the Modules Directory History of origins http://processwire.com/talk/topic/3278-core-imagemanipulation/ ---------------------------------------------------------------------------------------------------------- Page Image Manipulator - Methods * pimLoad or pim2Load, depends on the version you use! pimLoad($prefix, $param2=optional, $param3=optional) param 1: $prefix - (string) = mandatory! param 2: mixed, $forceRecreation or $options param 3: mixed, $forceRecreation or $options return: pim - (class handle) $options - (array) default is empty, see the next method for a list of valid options! $forceRecreation - (bool) default is false It check if the desired image variation exists, if not or if forceRecreation is set to true, it prepares all settings to get ready for image manipulation ------------------------------------------------------------------- * setOptions setOptions(array $options) param: $options - (array) default is empty return: pim - (class handle) Takes an array with any number valid options / properties and set them by replacing the class-defaults and / or the global config defaults optionally set in the site/config.php under imageSizerOptions or imageManipulatorOptions. valid options are: quality = 1 - 100 (integer) upscaling = true | false (boolean) cropping = true | false (boolean) autoRotation =true | false (boolean) sharpening = 'none' | 'soft' | 'medium' | 'strong' (string) bgcolor = (array) css rgb or css rgba, first three values are integer 0-255 and optional 4 value is float 0-1, - default is array(255,255,255,0) thumbnailColorizeCustom = (array) rgb with values for colorize, integer -255 - 255 (this can be used to set a custom color when working together with Thumbnails-Module) outputFormat = 'gif' | 'jpg' | 'png' (Attention: outputFormat cannot be specified as global option in $config->imageManipulatorOptions!) set {singleOption} ($value) For every valid option there is also a single method that you can call, like setQuality(90), setUpscaling(false), etc. ------------------------------------------------------------------- * pimSave pimSave() return: PageImage-Object If a new image is hold in memory, it saves the current content into a diskfile, according to the settings of filename, imagetype, targetFilename and outputFormat. Returns a PageImage-Object! ------------------------------------------------------------------- * release release() return: void (nothing) if you, for what ever reason, first load image into memory but than do not save it, you should call release() to do the dishes! ? If you use pimSave() to leave the ImageManipulator, release() is called automatically. ------------------------------------------------------------------- * getOptions getOptions() return: associative array with all final option values example: ["autoRotation"] bool(true) ["upscaling"] bool(false) ["cropping"] bool(true) ["quality"] int(90) ["sharpening"] string(6) "medium" ["targetFilename"] string(96) "/htdocs/site/assets/files/1124/pim_prefix_filename.jpg" ["outputFormat"] string(3) "jpg" get {singleOption} () For every valid option there is also a single method that you can call, like getQuality(), getUpscaling(), etc. See method setOptions for a list of valid options! ------------------------------------------------------------------- * getImageInfo getImageInfo() return: associative array with useful informations of source imagefile example: ["type"] string(3) "jpg" ["imageType"] int(2) ["mimetype"] string(10) "image/jpeg" ["width"] int(500) ["height"] int(331) ["landscape"] bool(true) ["ratio"] float(1.5105740181269) ["bits"] int(8) ["channels"] int(3) ["colspace"] string(9) "DeviceRGB" ------------------------------------------------------------------- * getPimVariations getPimVariations() return: array of Pageimages Collect all pimVariations of this Pageimage as a Pageimages array of Pageimage objects. All variations created by the core ImageSizer are not included in the collection. ------------------------------------------------------------------- * removePimVariations removePimVariations() return: pim - (class handle) Removes all image variations that was created using the PIM, all variations that are created by the core ImageSizer are left untouched! ------------------------------------------------------------------- * width width($dst_width, $sharpen_mode=null) param: $dst_width - (integer) param: $auto_sharpen - (boolean) default is true was deleted with version 0.0.8, - sorry for breaking compatibility param: $sharpen_mode - (string) possible: 'none' | 'soft' | 'medium' | 'strong', default is 'soft' return: pim - (class handle) Is a call to resize where you prioritize the width, like with pageimage. Additionally, after resizing, an automatic sharpening can be done with one of the three modes. ------------------------------------------------------------------- * height height($dst_height, $sharpen_mode=null) param: $dst_height - (integer) param: $auto_sharpen - (boolean) default is true was deleted with version 0.0.8, - sorry for breaking compatibility param: $sharpen_mode - (string) possible: 'none' | 'soft' | 'medium' | 'strong', default is 'soft' return: pim - (class handle) Is a call to resize where you prioritize the height, like with pageimage. Additionally, after resizing, an automatic sharpening can be done with one of the three modes. ------------------------------------------------------------------- * resize resize($dst_width=0, $dst_height=0, $sharpen_mode=null) param: $dst_width - (integer) default is 0 param: $dst_height - (integer) default is 0 param: $auto_sharpen - (boolean) default is true was deleted with version 0.0.8, - sorry for breaking compatibility param: $sharpen_mode - (string) possible: 'none' | 'soft' | 'medium' | 'strong', default is 'soft' return: pim - (class handle) Is a call to resize where you have to set width and / or height, like with pageimage size(). Additionally, after resizing, an automatic sharpening can be done with one of the three modes. ------------------------------------------------------------------- * stepResize stepResize($dst_width=0, $dst_height=0) param: $dst_width - (integer) default is 0 param: $dst_height - (integer) default is 0 return: pim - (class handle) this performs a resizing but with multiple little steps, each step followed by a soft sharpening. That way you can get better result of sharpened images. ------------------------------------------------------------------- * sharpen sharpen($mode='soft') param: $mode - (string) possible values 'none' | 'soft'| 'medium'| 'strong' return: pim - (class handle) Applys sharpening to the current memory image. You can call it with one of the three predefined pattern, or you can pass an array with your own pattern. ------------------------------------------------------------------- * unsharpMask unsharpMask($amount, $radius, $threshold) param: $amount - (integer) 0 - 500, default is 100 param: $radius - (float) 0.1 - 50, default is 0.5 param: $threshold - (integer) 0 - 255, default is 3 return: pim - (class handle) Applys sharpening to the current memory image like the equal named filter in photoshop. Credit for the used unsharp mask algorithm goes to Torstein Hønsi who has created the function back in 2003. ------------------------------------------------------------------- * smooth smooth($level=127) param: $level - (integer) 1 - 255, default is 127 return: pim - (class handle) Smooth is the opposite of sharpen. You can define how strong it should be applied, 1 is low and 255 is strong. ------------------------------------------------------------------- * blur blur() return: pim - (class handle) Blur is like smooth, but cannot called with a value. It seems to be similar like a result of smooth with a value greater than 200. ------------------------------------------------------------------- * crop crop($pos_x, $pos_y, $width, $height) param: $pos_x - (integer) start position left param: $pos_y - (integer) start position top param: $width - (integer) horizontal length of desired image part param: $height - (integer) vertical length of desired image part return: pim - (class handle) This method cut out a part of the memory image. ------------------------------------------------------------------- * canvas canvas($width, $height, $bgcolor, $position, $padding) param: $width = mixed, associative array with options or integer, - mandatory! param: $height = integer, - mandatory if $width is integer! param: $bgcolor = array with rgb or rgba, - default is array(255, 255, 255, 0) param: $position = one out of north, northwest, center, etc, - default is center param: $padding = integer as percent of canvas length, - default is 0 return: pim - (class handle) This method creates a canvas according to the given width and height and position the memory image onto it. You can pass an associative options array as the first and only param. With it you have to set width and height and optionally any other valid param. Or you have to set at least width and height as integers. Hint: If you want use transparency with rgba and your sourceImage isn't of type PNG, you have to define 'png' as outputFormat with your initially options array or, for example, like this: $image->pimLoad('prefix')->setOutputFormat('png')->canvas(300, 300, array(210,233,238,0.5), 'c', 5)->pimSave() ------------------------------------------------------------------- * flip flip($vertical=false) param: $vertical - (boolean) default is false return: pim - (class handle) This flips the image horizontal by default. (mirroring) If the boolean param is set to true, it flips the image vertical instead. ------------------------------------------------------------------- * rotate rotate($degree, $backgroundColor=127) param: $degree - (integer) valid is -360 0 360 param: $backgroundColor - (integer) valid is 0 - 255, default is 127 return: pim - (class handle) This rotates the image. Positive values for degree rotates clockwise, negative values counter clockwise. If you use other values than 90, 180, 270, the additional space gets filled with the defined background color. ------------------------------------------------------------------- * brightness brightness($level) param: $level - (integer) -255 0 255 return: pim - (class handle) You can adjust brightness by defining a value between -255 and +255. Zero lets it unchanged, negative values results in darker images and positive values in lighter images. ------------------------------------------------------------------- * contrast contrast($level) param: $level - (integer) -255 0 255 return: pim - (class handle) You can adjust contrast by defining a value between -255 and +255. Zero lets it unchanged, negative values results in lesser contrast and positive values in higher contrast. ------------------------------------------------------------------- * grayscale grayscale() return: pim - (class handle) Turns an image into grayscale. Remove all colors. ------------------------------------------------------------------- * sepia sepia() return: pim - (class handle) Turns the memory image into a colorized grayscale image with a predefined rgb-color that is known as "sepia". ------------------------------------------------------------------- * colorize colorize($anyColor) param: $anyColor - (array) like css rgb or css rgba - but with values for rgb -255 - +255, - value for alpha is float 0 - 1, 0 = transparent 1 = opaque return: pim - (class handle) Here you can adjust each of the RGB colors and optionally the alpha channel. Zero lets the channel unchanged whereas negative values results in lesser / darker parts of that channel and higher values in stronger saturisation of that channel. ------------------------------------------------------------------- * negate negate() return: pim - (class handle) Turns an image into a "negative". ------------------------------------------------------------------- * pixelate pixelate($blockSize=3) param: $blockSize - (integer) 1 - ??, default is 3 return: pim - (class handle) This apply the well known PixelLook to the memory image. It is stronger with higher values for blockSize. ------------------------------------------------------------------- * emboss emboss() return: pim - (class handle) This apply the emboss effect to the memory image. ------------------------------------------------------------------- * edgedetect edgedetect() return: pim - (class handle) This apply the edge-detect effect to the memory image. ------------------------------------------------------------------- * getMemoryImage getMemoryImage() return: memoryimage - (GD-Resource) If you want apply something that isn't available with that class, you simply can check out the current memory image and apply your image - voodoo - stuff ------------------------------------------------------------------- * setMemoryImage setMemoryImage($memoryImage) param: $memoryImage - (GD-Resource) return: pim - (class handle) If you are ready with your own image stuff, you can check in the memory image for further use with the class. ------------------------------------------------------------------- * watermarkLogo watermarkLogo($pngAlphaImage, $position='center', $padding=2) param: $pngAlphaImage - mixed [systemfilepath or PageImageObject] to/from a PNG with transparency param: $position - (string) is one out of: N, E, S, W, C, NE, SE, SW, NW, - or: north, east, south, west, center, northeast, southeast, southwest, northwest default is 'center' param: $padding - (integer) 0 - 25, default is 5, padding to the borders in percent of the images length! return: pim - (class handle) You can pass a transparent image with its filename or as a PageImage to the method. If the watermark is bigger than the destination-image, it gets shrinked to fit into the targetimage. If it is a small watermark image you can define the position of it: NW - N - NE | | | W - C - E | | | SW - S - SE The easiest and best way I have discovered to apply a big transparency watermark to an image is as follows: create a square transparent png image of e.g. 2000 x 2000 px, place your mark into the center with enough (percent) of space to the borders. You can see an example here! The $pngAlphaImage get centered and shrinked to fit into the memory image. No hassle with what width and / or height should I use?, how many space for the borders?, etc. ------------------------------------------------------------------- * watermarkLogoTiled watermarkLogoTiled($pngAlphaImage) param: $pngAlphaImage - mixed [systemfilepath or PageImageObject] to/from a PNG with transparency return: pim - (class handle) Here you have to pass a tile png with transparency (e.g. something between 150-300 px?) to your bigger images. It got repeated all over the memory image starting at the top left corner. ------------------------------------------------------------------- * watermarkText watermarkText($text, $size=10, $position='center', $padding=2, $opacity=50, $trueTypeFont=null) param: $text - (string) the text that you want to display on the image param: $size - (integer) 1 - 100, unit = points, good value seems to be around 10 to 15 param: $position - (string) is one out of: N, E, S, W, C, NE, SE, SW, NW, - or: north, east, south, west, center, northeast, southeast, southwest, northwest default is 'center' param: $padding - (integer) 0 - 25, default is 2, padding to the borders in percent of the images length! param: $opacity- (integer) 1 - 100, default is 50 param: $trueTypeFont - (string) systemfilepath to a TrueTypeFont, default is freesansbold.ttf (is GPL & comes with the module) return: pim - (class handle) Here you can display (dynamic) text with transparency over the memory image. You have to define your text, and optionally size, position, padding, opacity for it. And if you don't like the default font, freesansbold, you have to point to a TrueTypeFont-File of your choice. Please have a look to example output: http://processwire.com/talk/topic/4264-release-page-image-manipulator/page-2#entry41989 ------------------------------------------------------------------- PageImage Manipulator - Example Output
    3 points
  4. Also wanted to dive in and say hello. I like coffee. Seriously great stuff, and you're right my DataTables module is only an example and it was a rough coding session never found any more love. But actually to just read and list data with quick edit link in a modal, which is nice for when editing sets of items, and I'm using it in some projects. It would also support inline editing and stuff but never found the energy and time to give it a shot. Making everything configurable and modular/abstract is quite some work, so all the love for people taking this "concept" further. I think there's a need for such things and I love seeing other people getting involved with PW module development. There's so many good things that can be done and PW makes it so easy. But dude that Handsontable is a real beauty and looks like a great concept and API! Keep it up. Thanks for sharing. The right tools make a big difference. If you need a beta tester...
    3 points
  5. Btw, let me clarify one thing; with the exception of "name" [not title] all PW fields are custom fields . The title field in the default install is also a custom field, included with the default profile and "marked" as a global field, hence its appearance everywhere.. How you go about this really depends on a number of things. For instance, do you want each of those projects to have a URL? If so, then each has to be a page. If not, they can all go in one page but that may not be very versatile. So, here's one way of doing it: Note that PW fields are reusable. Create an image field called whatever you want (as per the naming rules for fields). Configure it to either accept only 1 image or multiple depending on your needs. Let's call the field "image" [single image field] or "images" [multiple image field] Create a template file called "projects.php" Create a template and associate it with the "projects.php" template file. Add the "image" [or "images"] field to it. If you are using PW default install you will already have the title field attached to this template since it is marked as "Global". If you want a field to contain text, e.g. a write-up about your projects, then create a text area field and add it to this template. If you will have other pages not related to projects, you may want to group your project pages into one "container", i.e. under one parent. So, go ahead and create a page called "projects". You can make it use the template "projects" or another template [all depends on how you code your template file] For each of your 5 projects, create child pages under the parent page "projects". Call them whatever you wish. Make sure these child pages use the "projects" template. Edit the child pages as you create them, adding a project image [or images] depending on the type of image field you created above. Enter a title and save each page. Now comes the fun part. Go into your template file and use a PW selector to grab and display those project child pages. I suppose in your parent projects page, what you want are links to your 5 projects. The HTML is of course up to you. You could do something like this. $projects = $pages->find('parent=projects');// this will give you all the children of the parent project page with the title "projects". If you wont have a lot of projects, no need to place a limit here on the amount of results you retrieve. Now, the call above will retrieve an array (more than one result returned)...Traverse the array using foreach. With that single call, you have access to everything about those child pages - their custom fields, etc... So, we use foreach to traverse the array...E.g. foreach ($projects as $project) { /*do something. What you want to do depends on you. Do you want to echo the project pages' titles? Do you want to echo a thumbnail of their images? Have a read of the project walkabout in the wiki as well as have a look at the default PW install. If you are still stuck, feel free to ask. */ } Small Project walkthrough Hope this helps
    2 points
  6. Lookin' great so far. And about the coding & talkin: I like it that way, PLEASE stay talkin' on the forums too. (and Wanze coding !) When Joss returns, we have a whole Social Media Platform going on right here. Think this module gonna get really useful for lot's of things.
    2 points
  7. .ui-widget-content needs to calculate it's height, the css overflow property can solve it. Welcome Pauline !
    2 points
  8. Would something like this work? (written in browser, so might need tweaking). This should print all events sorted by date descending, but grouped by day and sorted by time ascending within the day. $allEvents = $pages->find("parent=/events/, sort=-date"); while($allEvents->count()) { $event = $allEvents->first(); $from = strtotime(date('Y-m-d 00:00', $event->getUnformatted('date'))); $to = strtotime("+1 day", $from); $events = $events->find("date>=$from, date<$to, sort=date"); echo "<h3>Events on " . date('F j, Y', $from) . "</h3>"; foreach($events as $event) { echo "<p><a href='$event->url'>$event->title</a> $event->date</p>"; $allEvents->remove($event); } }
    2 points
  9. PageImage Manipulator - Tips & Examples * how to create a PNG with transparency for the watermarkLogo method If you can use adobe photoshop, you simply may download and install this action: photoshop_action_PW-png-creations.zip create an empty image (transparent, not white or black) of 2000 x 2000 pixel write / paste your text and / or logo in a single color, (e.g. black) when finished with your text you should have one single layer with text on transparent background then click the action PW-png-creations -> 2000px-square-to-smooth and you are done. Hhm, maybe before saving you want to tweak the global transparency of the image a bit. That one used in the ProcessWire example was set to 75% instead of 100%. Just try out what looks best for you. ------------------------------------------------------------------- * did you know that you can save to different file formats, regardless of source format? You only have to specify your desired format (gif, jpg, png) with the optionally options array with a key named outputFormat and pass it with your call to pimLoad() or use setOptions() before any other action-method: // assuming the first image is a jpeg $img = $page->images->first(); // define outputFormat $options = array('outputFormat'=>'png'); // apply it together with other actions $myPng = $img->pimLoad('myPrefix')->setOptions($options)->width(240)->pimSave(); //------------------------------------------------------------------------------------------ // you may also do it as a OneLiner only with the image format conversion $myPng = $page->images->eq(0)->pimLoad('myPrefix', array('outputFormat'=>'png'))->pimSave(); // or you can use the setOutputFormat method $myPng = $page->images->first()->pimLoad('myPrefix')->setOutputFormat('png')->pimSave(); ------------------------------------------------------------------- * (how) can I use the ImageManipulator with other imagefiles than PW-Pageimages? You can load any imagefile from your servers filesystem into the ImageManipulator with: $pim = wire('modules')->get('PageImageManipulator')->imLoad($imageFilename); // or $pim = $wire->modules->get('PageImageManipulator')->imLoad($imageFilename, $options); You can directly with the imLoad-method pass specific $options or you can do a separate call to setOptions(). Then you do your desired actions and last but not least you call save()! Most time I think the original passed file gets overwritten with the manipulation result, but you are also able to save to a different name and / or fileformat. If so, it is useful to get the final (sanitized) filename back after saveing. $optionalNewFilename = $pim->setOptions($options)->height(360)->flip()->blur()->save(); Also you may call this in one line if you prefer: if(false!==$wire->modules->get('PageImageManipulator')->imLoad($imageFilename,$options)->height(360)->flip()->blur()->save()) { // success !! } ------------------------------------------------------------------- * how can I use the PageImageManipulator with my own module/s? If you build a module that do some image manipulation you can define the PIM as needed dependency in your ModulesInfo method with the key 'requires'. You may also force to install the PIM if it isn't already with the key 'installs': public static function getModuleInfo() { return array( // ... 'requires' => array('PageImageManipulator'), 'installs' => 'PageImageManipulator', // ... ); } detailed infos are here: http://processwire.com/talk/topic/778-module-dependencies/ additionally, if you need to check if a module dependency has a minimum version number, you can do it in your install method like this: public function ___install() { // check that at least the minimum version number is installed $needed = '0.0.3'; $a = wire('modules')->get('PageImageManipulator')->getModuleInfo(); $actual = preg_replace('/(\d)(?=\d)/', '$1.', str_pad("{$a['version']}", 3, "0", STR_PAD_LEFT)); if(version_compare($actual, $needed, '<')) { throw new WireException(sprintf($this->_(__CLASS__ . " requires PageImageManipulator %s or newer. Please update."), $needed)); return; } // ... more code } ------------------------------------------------------------------- * global options in site/config.php You can create a config-array in your site/config.php. If you look into it you will find a config array for the ImageSizer that comes with the PW core $config->imageSizerOptions = array( 'autoRotation' => true, 'sharpening' => 'soft', 'upscaling' => true, 'cropping' => true, 'quality' => 90 ); You can define another array with these keys: $config->imageManipulatorOptions = array( 'autoRotation' => true, 'sharpening' => 'soft', 'upscaling' => false, 'cropping' => true, 'quality' => 90, 'bgcolor' => array(255,255,255,0), ); You don't have to specify all of the options. PiM reads the imageSizerOptions and merge them with the imageManipulatorOptions. So if you want to have different values you can specify them in imageManipulatorOptions to override those from imageSizerOptions. Only one option isn't present with the imageSizer: bgcolor. This is only used by the imageManipulator. ------------------------------------------------------------------- * using PiM together with the awesome Thumbnails Module (http://mods.pw/1b) If you use the Thumbnails Module together with PiM, you can set two options in the site/config.php under imageManipulatorOptions: $config->imageManipulatorOptions = array( // ... 'thumbnailColorizeCustom' => array(40,-35,0), 'thumbnailCoordsPermanent' => true ); For the colorize dropdown select you can define the custom color here and with the second param you enable/disable permanent storage of RectangleCoords and params. The coords and params are stored with Thumbnails Module per session, but are lost after the session is closed. If you enable permanent storage these data is written into a custom IPTC field of the original imagefile. ------------------------------------------------------------------- * some more examples will folow here . . . -------------------------------------------------------------------
    2 points
  10. Hi rusjoan, Welcome to Pw! What exactly should your module do? How do you plan to integrate the class into ProcessWire? It's not clear from your description. ProcessWire does save the files/images of a page in the folder /site/assets/files/{page-id}/. So if you wanted to save a word file in ProcessWire, then it could be somewhere in the /site/assets/ directory. If you have one word per page, you'd save it like this: $document->save($config->paths->assets . "files/{$page->id}/myName.docx"); This probably didn't answer your question
    2 points
  11. In some previous posts, I demonstrated a simple proof-of-concept CRUD “application” for PW using jTable. I really wanted to use DataTables since it is older, wiser and with a huge fan base. One thing I wanted was for the CRUD system to be as simple as possible and possibly be Excel-like (more below). With DataTables plugins, you can perform stuff like inline-editing and Auto-fill. The latter works just like in Excel and allows you to quickly populate cells (quick copy). But that’s as far as it goes. Google led me to other Table management systems. Some of the front runners include jqGrid and SlickGrid. SlickGrid is nice and using virtual rendering can display millions of rows with ease. Its support for displaying data delivered on demand (Ajax/server) is minimal; it requires you to download all your data before it starts manipulating it. Someone has created an Excel-like extension for it allowing copy-pasting between SlickGrid and Excel. Awesome! But, it still didn't rock my boat completely, especially the Ajax content issue. Then I stumbled upon Handsontable, a new kid on the block that describes itself as “a minimalistic Excel-like data grid editor for HTML, JavaScript & jQuery”. Though relatively new, it is as Excel-like as you can get. You can copy-paste from/to Excel [single to multiple cells and multiple to multiple cells], use the usual shortcuts (Ctrl-A, Ctrl-X, Ctrl-C, Ctrl-V, Ctrl-Y, Ctrl-Z - i.e., undo/redo, etc., and tab navigation, etc.), use Auto-fill (vertically and horizontally), freeze cells/columns, make cells/columns read only, Right-click context menus, insert/delete rows and columns, perform inline-editing, etc. Double awesome! Handsontable (HoT) is minimalistic but comes with a rich API. However, most of the implementation is down to you (sounds familiar?). It will render the Table and provide you with Methods/Events/Options to manipulate it. You just have to provide the data as an Array or JSON defining your columns and containing your data. Soma has a DataTable module that is currently read only; you cannot edit the displayed data directly in the table. It is also in lazy development . So, I started toying with the idea of using HoT as an Excel-like CRUD system (module) for PW. I wanted something that could quickly and easily do mass editing (Batcher-like). Imagine quickly creating basic pages/records by copy-pasting from Excel. Or quickly changing records by dragging (Auto-fill) or pasting new values over old ones. Well, the result can be seen in the screencast below. I’d like to hear your thoughts whether this is something that I should consider developing into a module; something that could be used by Superusers. Depending on implementation, it could be used by other users as well. All HoT needs is data. What you do with that data is up to you (the developer) and your code! This can be very dangerous if not implemented correctly, of course! You could easily delete important records! However, HoT does not touch your database! Deletions, insertions, etc., will not alter your database unless you specifically write the PHP code to do that. All it does is send data to the server via a method your specify (post, get, Ajax, etc.), and even that is your implementation. You do not need to implement deletions or additions. You can use it for updating content only or as read only, but where’s the fun in that ? In the screencast , apart from the table itself, all the other implementations are custom - the buttons, selects, auto-save (can be tricky? can DDOS your server?). The current implementation is not perfect; it is a demo after all. Certain stuff would need to be implemented differently . I didn't want to spend too much time on something that would not be useful in the end. All CRUD operations are via PW API and/or vanilla PHP. Deleted pages are Trashed and not permanently deleted. If there is interest for a module of this kind, I would request all the help I can get to develop it please. I would put it up on Github of course. In the demo, this is running off Diogo’s ACP module. Let me hear your thoughts please. Should a module like this see the light of day? Thanks Wasn't sure in which forum to post this...
    1 point
  12. Hi all, I've finally managed to set up my website where I intend to post ProcessWire tutorials. The site (lessons) is very much WIP. I have tried to make the site as responsive as possible. I have only tested in Chrome and FF. I'd rather write more lessons than test in IE to be honest so I won't pursue that... I will try to post regularly but can't make any promises as to the frequency . I intend to randomly post some pre-planned lessons. I will update this forum post everytime I post something new. If you have questions, pointers, etc about any lesson, or a request for a specific lesson, please post them here. This way, other forum members can assist in answering the questions . Thanks. /k ==================================================================== Edit: 28 August 2014 - Fist Tutorial is up (OK, it's Blog Module documentation ) Creating a Blog in ProcessWire (Blog Module how to) - 28 August 2014 [99% done] Next: 'All you ever wanted to know about ProcessWire Modules [and other cool things]' - COMING very SOON
    1 point
  13. @Soma...Thanks! Well you and the other "usual suspects" are squarely to "blame" for slowly turning me into a coder ! I have recently had the "aha" moment regarding module development; I couldn't agree more - PW makes it so very easy to develop modules! The API is consistent and there's tools for almost everything (well-done Ryan!). Looking at the module code written by you guys now makes much more sense to me. Teppo's write-up on Inputfields and Fieldtypes was one of the tipping points for me; that plus I looked up some really nice and short PHP OOP tutorials. Suddenly, it all made sense . I feel like I've learnt a lot in such a short time, thanks to you guys leading the way . Anyway, I will be doing a write-up sometime about my experiences in module development so watch this space. About "energy", incidentally, I was thinking about that recently; about that time when you feel you've "coded it all" and need something different to excite you. Anyway, I digress. Thanks for offering to test. Looking forward to your suggestions
    1 point
  14. Hi Meepz. Welcome to the forums and the wonderful world of PW Not sure how much you've read of the docs. You want to start there if you haven't. Things will then make a lot more sense. Selectors and page(s) variables are a must read . Check this part of the docs to know how to access images. This thread has links to great resources, tutorials and articles. OK, back to your question... If you had that image as inline in your text, then all you need to do in your template file is to echo echo $page->project; If you had the image in an image custom field that could only take one image, in your template file you could have: echo $page->project;//contains your text if($page->image) echo "<img src='{$page->image->url}'>"; //where image is your image field There are other ways to achieve this; I'm just giving you basic examples here..
    1 point
  15. ProcessWire Productivity App QuickSilver Getting things done QUICK Sequel Pro The easiest way to handle local databases Little Ipsum Never outta reach. Sip Live's so colorfull....
    1 point
  16. . Yeah, the editor can drive you crazy!
    1 point
  17. @Horst: Oh, so now we can talk? I understood your last post before the above to mean you needed space (content overflowing across various posts) to finish writing IManipulator's tutorial b4 we could start posting Congrats on this great achievement!
    1 point
  18. thanks Wanze! Hhm, I think: no! :P I prefer to use photoshop directly with a wrapper class that uses the COM-technique on Windows
    1 point
  19. Do you plan to integrate some of Photoshops filters too? Nice module btw, great work!!!
    1 point
  20. Hi guys, if you have your page tree structure, some of the pages with children act solely as a container. So some of the time, at least when I am structuring web sites the pages don't provide any content themselves. On a regular web site I do these two types of pages are very common: Type 1: Pages that list content, like news items, articles or images and then link to their respective full content view. Type 2: Pages that contain full featured pages which are very individual in content so you don't want to list them on a landing page. How do you treat those type 2 pages in your navigation? I've been using two different methods: They are not clickable, so there is no link associated with them The link leads to their first child I recently added these as options with radio buttons (with the Page Field, more on that) to my custom "options" tab I use on templates: (●) links to first child ( ) no link So in my menu creation loop is use something like this ($currentPage being the current page within the loop): <? if ($currentPage->numChildren > 0 && $currentPage->options->has("name=links_to_first_child")): ?> <li><a href="<?= $currentPage->children->first()->url ?>"><?= $currentPage->title ?></a></li> <? elseif ($currentPage->options->has("name=no_link")): ?> <li><a href="#"><?= $currentPage->title ?></a></li> <? else: ?> <li><?= $currentPage->title ?></li> <? endif ?> So, just wanted to share my method and ask how you go about it.
    1 point
  21. When saving files to a page's path, Wanze's got the right trick there, but do it like this instead (which ensures the directory is created if it's not there already): $document->save($page->filesManager->path . 'myName.docx'); You might also want to add it to an existing files field on your page? $page->files->add($page->filesManager->path . 'myName.docx'); The above assumes your files field is named 'files'. For temporary, non-web accessible files, I usually save to /site/assets/cache/ $document->save($config->paths->cache . 'myName.docx'); If you need to store multiple files, it's best to have your module create it's own directory under /site/assets/cache/.
    1 point
  22. If working on my own personal projects I develop locally on MAMP and then push everything live to the server once done. I don't use Git as much as I should, I need to work on that. For client projects, I have a random domain name that I develop client projects on, I just make sure they're not indexed by any search engines and only the client has the url to watch the site build and progress. Once I'm finished and they're happy and I've been paid I then migrate it to their server. This has worked well for me over the years and keeps clients happy as they can track the progress of the build.
    1 point
  23. I think there should be only one repo per language containing all module translations. One repo per language per module is overkill I think, because most of the modules need only one language file, right? Just for the sake of an comprehensive view in the repo, each Module should have its own folder. The repo then can be managed by someone who speaks that language and can take pull requests from other contributors. I think this central way is more approachable for those who want to help. What could be problematic is that the language files are created in only one line, compressed, which makes it hard to track line by line changes in a repo. I am willing to open such a repo for German if no one else wants to =)
    1 point
  24. Hey Jason Thanks again for pointing me to Evernote. I'm loving it. Especially like the fact that it syncs across my devices so I can post ideas on my iphone while on the train and pick the thought up on my laptop when I get home. I also like the concept of Notebooks because I can separate personal projects from business projects. Loving it so far mary
    1 point
  25. It's a common error in coding and has nothing to do with PHP. I would write the loop and current class like this: foreach($categories as $category) { $class = $category === wire("page") ? ' class="current"' : ''; $out .="<li$class><a href='{$category->url}'>{$category->title}</a></li>"; } === is comparing the objects if they're the same in memory, since $category at some point IS the current $page it works. You could also write: foreach($categories as $category) { $class = $category->id == wire("page")->id ? ' class="current"' : ''; $out .="<li$class><a href='{$category->url}'>{$category->title}</a></li>"; } No need to compare titles as they theoretically could be same for another page. So the id is just as good and better.
    1 point
  26. I think if it is a new topic, you start a new topic unless it's closely related to the current topic. Another reason to starting a new topic, this one already had best answer. For those quickly scanning the forum for unanswered topics, the "new question" might be missed. Just my opinion
    1 point
  27. Horst, finally =) I've yet to dive into all of that but it looks great at first glance. Thanks for the awesome contribution.
    1 point
  28. Something that's in dev since 2.3.1 is that you can make the autoload specific. More infos can found here http://processwire.com/talk/topic/3768-processwire-231-dev-branch/ : 3. Conditional autoload modules. This is because if you look at your module, it will get loaded and hook added in init on EVERY request, even on front-end. So to make this easy you can use a selector or an anonymous function (php 5.3 only). To only load this module in the admin, you could then do "autoload" => "template=admin",
    1 point
  29. Ryan, i can't tell, how much i love you. Works like a charm.
    1 point
  30. @mvolke the module I'm working on synchronizes structure only - Templates, Fields and Fieldgroups. It stores captured model operations in JSON files. I currently have no plans to synchronize content (Pages) though it may prove to be necessary at some point - because Pages are used for so many things, certain Pages may need to synchronized, for example options for drop-downs. I'm having Ryan take a look at the module now, and his initial reaction was positive - I think we can make this work, and I think it'll work well. You would think that, but if you look at the ProcessWire codebase, the entire meta-model, with all possible operations, is encapsulated and supports hooks - so it is actually perfectly feasible to implement this in ProcessWire. As proof of concept, I already have all Field operations captured and repeatable. Because this is implemented at the lowest API level, it is actually independent of controllers and user-interface - that is, if you were to build your own admin modules that (for some reason) make changes to any part of the meta-model, those changes would be correctly captured and would be repeatable, independently of any admin UI. There is still substantial work to do on this module, but I would say it's about half-done at this point, and there are no major roadblocks to completion - the fundamental idea is proven and works, so it's a matter of building it out completely.
    1 point
  31. I use git in conjunction with git-ftp (https://github.com/git-ftp/git-ftp) It's awesome and very convenient.
    1 point
  32. Just recently came across this neat jQuery sorting and filtering plugin. I haven't used it myself but it seems easy enough to implement. Thought i'd share. http://mixitup.io/ one of the demos: http://mixitup.io/demos/parks
    1 point
  33. The creation process really isn't a problem with the tools Ryan mentioned. Especially for stuff like country lists i think it's really powerful to use pages. Easy to maintain; if a country name changes (yes, this does happen every once in a while) you just update that page and everything using it will always be in sync, be it your select options or other places you used the country pages. Maybe you would want to add some additional info, like a country flag or multilingual country names. This stuff all becomes easy and transparent if you use pages. What if later on you would also want to support regions and/or continents? Easy to implement when everything is pages.
    1 point
  34. Why? Almost everything can be relative to document root or the current page and arranged the same way locally and on the server. If something really must be in different places, manage that path info from one central spot. You could put those in /site/config.php as long as you are careful not to use any system names. People have their own ways of doing things but personally I think it's well worth the minimal effort to keep data and files as portable as possible. I haven't been using PW very long but I haven't seen anything turn up that would be a problem. If I'm missing something please correct me. I wouldn't want to get caught out by that.
    1 point
  35. currently I'm back working on the ImageManipulator. I've put it into a module that extends PageImage. It integrates nicely into PWs workflow. There are only a few things that I have to solve before I can release it: finishing and testing the watermark-methods write documentation and some examples provide support for imageVariations, namely: removeVariations() with point 3, I already have a method that can be called when in PIM-mode (PageImageManipulator-mode) which unlink all PIM-created files, but I think it would be more comfortable if a user calls one of the PageImage-methods getVariations() or removeVariations() if the PIM-created variations are included in the collections. EDIT: in the meantime, we have had a release already: http://processwire.com/talk/topic/4264-release-page-image-manipulator/ ----- here are a screenshot of my current Testpage
    1 point
  36. In my opinion CSS3 selectors (nth-child) are really the way to go for this kind of presentational stuff. Browser support is widespread ( http://caniuse.com/#search=nth ), but if you still care about <IE9 you're out of luck unless you use something like http://selectivizr.com/ or go with pure jQuery as Soma showed.
    1 point
  37. jQuery: $('li:odd').addClass("odd"); done.
    1 point
  38. I use timeEdition for timetracking: http://www.timeedition.com/ I recently built me a tailormade "invoicing/quoting/proposaling" tool wit PW, so no tools there that I can recommend. Paper and pen are very useful and I should use them more often to write down/sketch out ideas quick. I tried a lot of todo list apps, but I always come back to just plain text files, in which I cross off those empty square brackets =) [ ] not yet [x] done =) I also use those text files to note anything project related. When I procrastinate too much I force myself to work with the pomodoro technique, very helpful. http://www.pomodorotechnique.com/ There a tons of pomodoro apps out there (http://gigaom.com/2010/11/10/9-free-pomodoro-timers/), or you could just use a kitchen timer, like the inventor did.
    1 point
  39. Thanks! Glad it helps others With external tables you mean custom tables in the same database as Pw? So that one can select the table and columns to edit, e.g. map the columns to Pw-Fields and then handle the CRUD. This sounds great, but IMO this would be a different module because the data is stored "outside" of Pw.
    1 point
  40. This is really awesome! Never heard of this plugin before. I'm thinking of integrating Handsontable into Batcher, there are some other feature requests pending I wanted to integrate sooner or later. Kongondo, are you already developing a separate module? What do you think? Cheers
    1 point
  41. Ryan, nice module! I always liked thumbnail preview rather than big images stacked one after another
    1 point
  42. Thank You Adam & Ryan. Love your theme to adamspruijt.
    1 point
  43. I'm all for more validation options, and generally add them as needs come up. But I want to point out that Fieldtypes define a database schema. That's something that differentiates the need for different fieldtypes, is the fact that most represent different storage needs for data. Behind the scenes, they represent a database table. You don't need different fieldtypes purely for validation reasons. Though FieldtypeEmail and FieldtypeURL (and I'm sure others) probably have very similar storage needs... and maybe they could really be the same Fieldtype if we really wanted it that way, but I'd rather have people choosing an "email" or "URL" field rather than a "email/URL" or generic "text" field. This is more consistent with the way HTML5 is going too, as types like "email" and "date" are being used over some validation attribute on text fields. Overall, I think it's easier on the user if they are choosing reasonably well defined types when creating fields, rather than abstracted "I can be anything" types. As for a "unique" validation, that's different from the others. This would best be handled at the Fieldtype level since it's a type of database index that enforces it. I've been planning to add this to the base Fieldtype class, so that it is available for all Fieldtypes. You may have seen the FieldtypeTextUnique, which was more about proof of concept and solving a need quickly, rather than a suggestion of what path should be taken overall. But we'll definitely be adding support for unique indexes in a manner that spans all Fieldtypes rather than just individually. Fieldtypes are involved in the getting and setting of page variables at the API level, making sure that the format is correct for the storage it represents. This is sanitization, just making sure that what gets set to it is valid in general for the type and suitable for storage. But specific interactive validations are the job of Inputfield modules. If there are any major validations we're missing on any of them, let me know and we can get it on the to-do list. I have been working to expand the validation options behind the InputfieldText module, as it now supports regular expressions, which will let you be as specific as you want with validation. New validations have also been added to several other Inputfields over the last year. Configurable error messages at the field-by-field level are something we don't have, but I would also like to see them. This is something I think we can add in the near future. They are currently configurable at the fieldtype-by-fieldtype level, but only if you have the multi-language support modules installed.
    1 point
  44. If you are using the latest versions of jQuery close-link is not working. The solution: Replace the file AjaxSearch.js line # 95 From: .live('click',function(){ On: .on('click',function(){ Excuse me for my English, I use Google Translate
    1 point
  45. Web apps in PW? Hell yeah, Processwire is great for that. I love how I can map out my app before I get started into a tree. I actually built a lot of web apps on PW. One being http://collabmango.com PW saves a lot of time in building web apps. The selectors and not needing to deal with DB crap is a huge +
    1 point
  46. Thanks for your work here Horst, it looks great! I look forward to going through in more detail so that I can understand it all and then integrate into the dev branch. It makes sense to have this built-in.
    1 point
  47. I think that some kind of aggregate fieldtype that encompasses others would be a fine way to go. I don't see it as something I would personally use all that often, but I can understand why others might find it useful–it's a worthwhile idea. From the implementation side, a single item repeater probably would be the quickest/simplest way to implement such a type of grouping. But there are some significant differences to the point where it may be better off as it's own fieldtype, perhaps that extends FieldtypeRepeater. Either that, or something new that just takes a similar approach. After all, an aggregate field wouldn't need but 1 "ready page", it wouldn't need a repeater parent page, and could be built around the Fieldtype class interface rather than the FieldtypeMulti interface. It would have less overhead than repeaters would in some areas. There are surely even more efficient ways it could be built, but the repeater-model approach would be the most likely way to make it something tangible in a shorter period. I'll keep marinating on it here.
    1 point
  48. Sure, actually will add one of these into first example. But here are both: Carousel.php <?php /* Generates the markup for the frontpage carousel */ if (count($carouselPages) < 1) return; echo "<div id='carousel'><ul class='rslides'>"; foreach($carouselPages as $key => $p) { echo "<li class='c-item c-item-$key'>"; echo "<img src='".$p->image->getThumb('carousel') ."' alt='' />"; echo "<p>$p->summary</p>"; echo "<a class='button' href='{$p->link->url}'>$p->headline</a>"; echo "</li>"; } echo "</ul></div>"; items.php <?php if (isset($title)) echo "<h2 class='items-title'>$title</h2>"; echo "<div class='items'>"; foreach($items as $key => $p) { $key++; if ($key > 3) { $key = 1; echo "<hr class='padding' />"; } echo "<div class='col{$key}of3 item'>"; echo "<img src='". $p->featured_image->getThumb('thumbnail') ."' alt='' />"; echo "<h2 class='title'><a href='$p->url'>$p->title</a></h2>"; echo "</div>"; } echo "</div>"; And as additional bonus, items-with-description.php (it includes the items.php from above): <?php if (count($items) < 1) return; $items = $items->slice(0,3); include('./items.php'); echo "<div class='col1-2of3'><p>$description</p></div>"; echo "<div class='col3of3'><a class='button block' href='$url'>$linkTitle</a></div>"; Uh, just realized that my site has evolved a little. Those in my example actually use "items-with-description.php" instead of just "items.php". There is also my "default-masthead.php": <?php $img = $image->width(304); ?> <div class='col1-2of3'> <h1><?= $title ?></h1> <p class='summary'><?= $summary ?></p> </div> <div class='col3of3'> <img src='<?= $img->url ?>' alt='' /> </div>
    1 point
  49. After Ryan's latest incarnation of Blog Profile I wanted to see if I could learn something from there to my own workflow. It turned out very nicely and this seems like a perfect template approach for me. Wanted to share it with you guys. Folder structure under templates folder: templates/markup/ templates/markup/helpers/ templates/markup/layouts/ templates/scripts/ templates/styles/ And it all begins from here: templates/markup/index.php -this is the "complete" html file, it has doctype, head and starting and ending body. There is very little "logic" here, it's more like a container. There is one very important code snippet there though: <?php if ($page->layout) { include("./markup/layouts/{$page->layout}.php"); } else { include("./markup/layouts/default.php"); } ?> Code above goes between header and footer of your site, that will be the main content. I call it layout, but the better name would be "content layout" or "inner layout" or something like that. Then the templates/markup/layouts/ folder will keep at least default.php file, but probably few others, like "threeColumns.php", "frontpage.php", "gallery.php" etc.. you got the idea. Each of the actual pw template files are then purely "controllers" - no actual markup generated there. All markup are done in files inside templates/markup/ folder and it's subfolders. This is how template file templates/home.php on one site I am building right now looks like: <?php // Carousel items $t = new TemplateFile(wire('config')->paths->templates . 'markup/helpers/carousel.php'); $t->set('carouselPages', $page->carousel); $page->masthead = $t->render(); // Tour themes $t = new TemplateFile(wire('config')->paths->templates . 'markup/helpers/items.php'); $t->set('title', "Get inspired from our <strong>tour themes</strong>"); $t->set('items', $page->featured_themes); $t->set('description', $page->themes_description); $t->set('url', $config->urls->root . "themes/"); $t->set('linkTitle', "All themes"); $page->main .= $t->render(); // National parks $t = new TemplateFile(wire('config')->paths->templates . 'markup/helpers/items.php'); $t->set('title', "Seven beautiful <strong>national parks</strong>"); $t->set('items', $page->featured_parks); $t->set('description', $page->parks_description); $t->set('url', $config->urls->root . "national-parks/"); $t->set('linkTitle', "All national parks"); $page->main .= $t->render(); $page->layout = "frontpage"; include("./markup/index.php"); This uses few "helper" markup files from templates/markup/helpers/ folder (namely carousel.php and items.php). Here is the carousel.php for your reference: <?php /* Generates the markup for the frontpage carousel */ if (count($carouselPages) < 1) return; $styles = ''; echo "<div id='carousel'><ul class='rslides'>"; foreach($carouselPages as $key => $p) { echo "<li class='c-item c-item-$key'>"; echo "<img src='".$p->image->getThumb('carousel') ."' alt='' />"; echo "<p>$p->summary</p>"; echo "<a class='button' href='{$p->link->url}'>$p->headline</a>"; echo "</li>"; } echo "</ul></div>"; Then populates the $page->masthead and $page->main properties and then set's the inner layout to "frontpage". That templates/markup/layouts/frontpage.php file is very simple on this site, but could be much more complicated if needed: <div id="masthead"> <?= $page->masthead; ?> </div> <div id="main" class="wrap"> <?= $page->main; ?> </div> Frontpage is rather unique and I could have done all the markup on the frontpage.php file also. But I do want to re-use those "carousel" and "items" components on other places as well. But if I do have totally unique stuff, where I do want to get "quick and dirty" this approach allows it. Then my template file would be something like this: $page->layout = "campaign2012"; include("./markup/index.php"); And then all the markup would be in that templates/markup/layouts/campaign2012.php Blog profile really gave me a good ideas (cleaner folder structure etc) and using TemplateFile class adds nice possibilities. This is of course just a one way to manage your templates, but hopefully someone of you finds this helpful when thinking about how you like to structure this stuff. PS: If you are just getting started with PW, then I recommend using the head and foot includes method from demo install. There is nothing wrong with that method and only more complicated sites starts to benefit from these methods introduces in this topic.
    1 point
×
×
  • Create New...