Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Rewrite the picker implementation #950

Merged
merged 41 commits into from
Jul 24, 2017

Conversation

aschempp
Copy link
Member

@aschempp aschempp commented Jul 11, 2017

As discussed in several issues, the current picker implementation does not really work. Here's my rewrite to support all current features and some more!

TODO:

  • Improve method names
  • Fix coding style / docblocks
  • Add necessary interfaces
  • Add unit tests

@leofeyer
Copy link
Member

Please revert the naming and namespace changes. Otherwise it is impossible to understand the changes.

@@ -24,6 +33,7 @@ setTimeout(function() {
}
editor.on('focus', function(){ Backend.getScrollOffset(); });
},
file_browser_callback_types: <?= json_encode($pickerTypes) ?>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole logic needs to be moved to the DataContainer class, so we can use $this->pickerTypes in the templates.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is tinyMCE initialized by DataContainer ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 1f24de0

@@ -510,7 +514,19 @@ protected function row($strPalette=null)
}
}

$objWidget->wizard = $wizard;
if ($wizard != '')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this change related to the picker?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It hides the picker icon and spacing if the dcaPicker is not supported (due to permissions etc.)

@leofeyer leofeyer added this to the 4.5.0 milestone Jul 11, 2017
@@ -898,7 +898,7 @@ var Backend =
var opt = options || {},
maxWidth = (window.getSize().x - 20).toInt(),
maxHeight = (window.getSize().y - 192).toInt();
if (!opt.id) opt.id = 'tl_select';
if (!opt.id) opt.id = 'tl_listing';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You must not change this, because it is not backwards compatible with the old PageTree and FileTree widgets.

@leofeyer leofeyer modified the milestones: 4.4.2, 4.5.0 Jul 11, 2017
@leofeyer leofeyer added bug and removed feature labels Jul 11, 2017
@leofeyer leofeyer changed the title Rewrite the picker implementation [RFC] Rewrite the picker implementation Jul 11, 2017
@leofeyer leofeyer closed this Jul 12, 2017
@aschempp aschempp changed the base branch from hotfix/4.4.1 to master July 12, 2017 11:31
@leofeyer leofeyer reopened this Jul 12, 2017
/**
* {@inheritdoc}
*/
public function createFromJson($json)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually add createFromUrlValue() and saveForUrlValue() or something like this. You can keep the others if you like but this would allow us using gzencode() etc. in these methods (or anything else in the future) to transparently and backwards compatible improve the size of the content :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented in a25b132, including Gzip compression

{
$arrRoot = $this->eliminateNestedPages($arrRoot);
}
$arrRoot = array_intersect($arrRoot, $this->Database->getChildRecords($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['root'], 'tl_page'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be $this->strTable not tl_page right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 368b144

@@ -224,8 +226,23 @@ public function alertsAction()
*/
public function pickerAction(Request $request)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have some extended description in the docblock here? :) Handles the picker redirect. does not really help a lot.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved in 9e53148

* @param FactoryInterface $menuFactory
* @param TokenStorageInterface $tokenStorage
*/
public function __construct(FactoryInterface $menuFactory, TokenStorageInterface $tokenStorage)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer having setter injection for the TokenStorageInterface because getUser() is absolutely optional for an AbstractPickerProvider. I think we should have setTokenStorage() and getTokenStorage(). The getUser() already considers the null case anyway :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes sense.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 37b9d7c

namespace Contao\CoreBundle\Picker;

/**
* Interface for DCA picker providers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to have more description here. Use this if ..... or something. This would help very much.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved in 9e53148

*
* @return string
*/
public function urlEncode($gzip = true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would name this $compress = true. Who cares if this is gzip.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved in 9e53148


if (isset($arrFilter['files']) && $arrFilter['files'] === false)
if (is_array($attributes))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a null check as there can only be array or null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved in 9e53148

{
$attributes = parent::initPicker($picker);

if (is_array($attributes))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for null here and do an early return please. I hate these 5-level nestings 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved in 9e53148

/**
* {@inheritdoc}
*/
public function convertDcaValue(PickerConfig $config, $value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are actually converting the picked value here, aren't we?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're converting the value from DC/database to the one we can pick.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still prefer convertValue() or convertPickedValue(). 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know the method is (only) part of the DcaPickerProviderInterface, right? That's why I named the methods so they are specific to the DCA, if we ever later add similar features for something else…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just think that convertPickedValue() is better understandable for everyone implementing the interface. convertDcaValue() sounds like the load_callback of the field is executed to convert the DCA value. 😉

Copy link
Member

@leofeyer leofeyer Jul 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, first, all the Dca parts in the method names are unnecessary in my opinion, because the class already has Dca in it.

But second, the first two methods actually read data from the DCA, so using Dca in the method name is kind of correct. The third method, however, reads user input, so …

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the interface does?
The convertDcaValue does not really read user input, it's basically a save_callback on the DCA value before it is rendered as a picker radio/checkbox value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course it reads user input. The user picks page ID 12, submits the value and gets back {{link_url::12}}.

Again, I think that convertPickedValue() is much better understandable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not correct. There is no Ajax request anymore, the values are converted before the user selects them in the picker. This also results in an instant update of the field value, contrary to the previous implementation which took seconds to update 😉

Copy link
Member

@leofeyer leofeyer Jul 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, why don't you say that right away? Then convertDcaValue is actually fine. 😄

/**
* {@inheritdoc}
*/
public function jsonSerialize()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method says JSON serialize but it returns an array?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's how the \JsonSerializable interface works 😉

*/
public function urlEncode($gzip = true)
{
$data = json_encode($this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you encoding $this here? Should it be $this->jsonSerialize()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's how the \JsonSerializable interface works 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually because json_encode() can handle objects and not because of the interface. 😉

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well json_encode will realize the object implements \JsonSerializable and uses that method…

*
* @return PickerProviderInterface|null
*/
public function getCurrentProvider();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer getProvider() here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns the currently active provider (based on the value).

Copy link
Member

@leofeyer leofeyer Jul 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I prefer getActiveProvider().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's named after isCurrent, which is based on KnpMenu (as described below). Still wanna change it?

*
* @return string
*/
public function getCurrentUrl();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer getUrl() here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns the URL for the current provider (based on the value).

*
* @return bool
*/
public function isCurrent(PickerConfig $config);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer isActive() here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've named this after the current variable of the KnpMenu

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, ok. We should either say current or active everywhere for consistency. So getCurrentProvider(), getCurrentUrl(), isCurrent() etc. or getActiveProvider(), getActiveUrl(), isActive() etc.

I prefer the latter, but as long as it is consistent, I can live with it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sort of agree. We name it active most of the time in Contao. But we cannot change KnpMenu, and the isCurrent() method is also called in https://github.com/aschempp/contao-core-bundle/blob/37b9d7caa43b196d8b234d59440c1b09f3842e06/src/Picker/AbstractPickerProvider.php#L64 to determine the current menu item…

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use "current" everywhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 so nothing to change 😎


// Deprecated since Contao 4.0, to be removed in Contao 5.0
$objTemplate->language = \Backend::getTinyMceLanguage();

$updateMode = $objTemplate->parse();

unset($file, $type);
unset($file, $type, $pickerBuilder);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also unset $fileBrowserTypes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 9e53148

$this->Template->main .= $this->getBackendModule(\Input::get('do'));
$picker = null;

if (isset($_GET['picker']))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using isset($_GET) cannot be "overwritten" via Input::setGet(). I know that I have been doing it, too – maybe we should discuss it and be consistent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, this will work fine. Input::setGet removes caches and adds the variable to $_GET (see https://github.com/contao/core-bundle/blob/master/src/Resources/contao/library/Contao/Input.php#L326)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. 👍

@@ -2667,7 +2662,7 @@ protected function generateTree($path, $intMargin, $mount=false, $blnProtected=t
}
}

if ($this->blnHideFiles)
if (!$this->blnFiles && !$this->blnFilesOnly)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really work without $this->strPickerField &&?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it does, because $blnFiles is initialized to true and only set to false on initPicker.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

@@ -94,7 +94,7 @@

<?php if ($this->isPopup): ?>
<script>
if ($$('.tl_tree_checkbox,.tl_tree_radio').length > 0) {
if ($$('.tl_tree_checkbox,.tl_tree_radio').length > 0 && !document.location.search.test('act=select')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have this as an outer if condition:

if (!document.location.search.test('act=select')) {
    if ($$('.tl_tree_checkbox,.tl_tree_radio').length > 0)) {
        // …
    } else {
        // …
    }
}

Otherwise the else condition would still be evaluated in "edit multiple" mode.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would mean the buttons are not disabled when in select mode, which is not what we want, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course. Don't know what I was thinking. 😄

@leofeyer
Copy link
Member

Already looking very good! Thank you very much @aschempp.

@leofeyer leofeyer merged commit d9a7c8f into contao:master Jul 24, 2017
leofeyer pushed a commit that referenced this pull request Jul 24, 2017
@leofeyer
Copy link
Member

Actually merged in 874076d.

leofeyer pushed a commit to contao/calendar-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/calendar-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/calendar-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/calendar-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/faq-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/faq-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/news-bundle that referenced this pull request Jul 24, 2017
leofeyer pushed a commit to contao/news-bundle that referenced this pull request Jul 24, 2017
@aschempp aschempp deleted the picker-rewrite branch July 27, 2017 08:58
agoat pushed a commit to agoat/contao-core-bundle that referenced this pull request Aug 1, 2017
@leofeyer leofeyer modified the milestones: 4.4.2, 4.4 May 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants