Giter VIP home page Giter VIP logo

core-bundle's Introduction

Symfony CMF Core Bundle

Latest Stable Version Latest Unstable Version License

Total Downloads Monthly Downloads Daily Downloads

Build Status: Test application

This package is part of the Symfony Content Management Framework (CMF) and licensed under the MIT License.

The CoreBundle for the Symfony content management framework provides common functionality, helpers and utilities for the other CMF bundles. The major features are a publish workflow, a twig extension and php templating helper to walk PHPCR-ODM trees and support for optional translated content.

Requirements

Documentation

For the install guide and reference, see:

See also:

Support

For general support and questions, please use StackOverflow.

Contributing

Pull requests are welcome. Please see our CONTRIBUTING guide.

Unit and/or functional tests exist for this package.

Thanks to everyone who has contributed already.

License

This package is available under the MIT license.

core-bundle's People

Contributors

andrey1s avatar benoitpointet avatar brki avatar cordoval avatar covex-nn avatar dantleech avatar dbu avatar eiannone avatar electricmaxxx avatar emmanuelvella avatar esserj avatar jonasreymen avatar krizon avatar lsmith77 avatar nayzo avatar petk avatar pulzarraider avatar samnela avatar simensen avatar stylecibot avatar uwej711 avatar wouterj avatar xabbuh avatar yoavl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

core-bundle's Issues

A way to convert non-translated documents to translated

After inserting the first documents into Phpcr I decided that I wanted translation support. I couldn't find a way to upgrade the existing field data to translatable fields data. All documents look empty as soon as you make a field translatable because it tries to load the default locale.

I created the following Migration script to convert documents by using the current field data (before translatable was added) data to the translatable default locale.

Warning: Since I have little to no experience with Phpcr I doubt my method is the best way!

However, bits and pieces of this script could be used to support this in Cmf/CoreBundle. Let me know what to improve /change.

<?php

namespace Symfony\Cmf\Bundle\CoreBundle\Migrator\Phpcr;

use Symfony\Component\Console\Output\OutputInterface;
use PHPCR\SessionInterface;
use Doctrine\Bundle\PHPCRBundle\Migrator\MigratorInterface;
use Doctrine\ODM\PHPCR\DocumentManager;
use Doctrine\Bundle\PHPCRBundle\ManagerRegistry;

class Translatable implements MigratorInterface{

    /**
     * @var SessionInterface
     */
    protected $session;

    /*
    * @var OutputInterface
    */
    protected $output;

    /**
     * @var DocumentManager
     */
    protected $dm;

    /**
     * @var string
     */
    protected $toLocale;

    /**
     * @param ManagerRegistry $registry
     */
    public function __construct(ManagerRegistry $registry)
    {
        $this->dm = $registry->getManager();
        $this->session = $registry->getConnection();
    }

    /**
     * @param SessionInterface $session
     * @param OutputInterface $output
     */
    public function init(SessionInterface $session, OutputInterface $output)
    {
        $this->session = $session;
        $this->output = $output;
    }

    /**
     * @param string $path
     * @param int $depth
     * @return int
     */
    public function migrate($path = "/", $depth = -1)
    {
        // Get the node from the path
        $node = $this->dm->find(null, $path);

        $this->migrateNode($node);
        return 0;
    }

    /**
     * @param $node
     */
    protected function migrateNode($node){

        // Save the id and class name of the node
        $nodeId = $node->getId();
        $nodeClass = get_class($node);

        // Check if the node is translatable
        if ($this->dm->isDocumentTranslatable($node)){

            // Find the current translator value on the object
            $metaData = $this->dm->getClassMetadata($nodeClass);

            // If a translator is defined in the mapping
            if (($translator = $metaData->translator) !== null){

                // Clear the unit of work
                $this->dm->getUnitOfWork()->clear();

                // Temporary set the translator to null
                $metaData->translator = null;

                // Find the node without translation
                $node = $this->dm->find(null, $nodeId);

                // Get data for translation
                $data = [];

                // Get the data from the translatable fields
                foreach ($metaData->translatableFields as $field){

                    // Get the non translated value
                    $data[$field] = call_user_func(array($node, "get".ucfirst($field)));

                    // Reference to the field
                    $fieldReference = $nodeId."/".$field;

                    $this->output->writeLn("Removing: ".$fieldReference);

                    // Remove the field if it exists
                    if ($this->session->itemExists($fieldReference))
                        $this->session->removeItem($fieldReference);
                }

                // Save the changes
                $this->dm->flush($node);

                // Clear the unit of work
                $this->dm->getUnitOfWork()->clear();

                // Reset the translator value to its original value
                $metaData->translator = $translator;

                // Find the node with translation
                $node = $this->dm->find(null, $nodeId);

                // Set the new data if the current value is null
                foreach ($data as $field => $value){

                    // Get the translated value, if any
                    $current = call_user_func(array($node, "get".ucfirst($field)));

                    // If the translated value is not there yet
                    if (is_null($current)){

                        // Set the translated value
                        call_user_func_array(array($node, "set".ucfirst($field)), [$value]);
                    }
                }

                // Save the changes
                $this->dm->flush($node);

                $this->output->writeLn("Migrated: ".$nodeId);
            }

        }else{
            $this->output->writeLn("Skipped: ".$nodeId);
        }

        // Clear the unit of work and retrieve un untouched node
        $this->dm->getUnitOfWork()->clear();
        $node = $this->dm->find(null, $nodeId);

        // Check if the node has childeren
        if (method_exists($node, "getChildren")){

            // Migrate the children
            $children = $node->getChildren();
            if (!is_null($children)){
                foreach ($children as $childNode){
                    $this->migrateNode($childNode);
                }
            }
        }

    }

} 

security.context should not be required for publish workflow

The idea is that the publish workflow works with or without security enabled.

However for the cmf.symfony.com website we discovered that an error is thrown if the service security.context is not available. It happens when Symfony\Bundle\SecurityBundle\SecurityBundle() is not added to AppKernel and/or security.yml is empty.

The security.context is loaded in the security.xml of the SecurityBundle. In the extension I found that if the config for security is empty the security.xml file is not loaded: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php#L54

On the other hand in the PublishWorkflowChecker the security.context is asked from the container without knowing if it exists: https://github.com/symfony-cmf/CoreBundle/blob/master/PublishWorkflow/PublishWorkflowChecker.php#L101

I am not sure how to fix this, maybe it is just adding a check to see if the security.context is in the container and otherwise generate a token differently. Or add validation in the CoreBundleExtension and if needed throw an error that explains what to do.

Error message when missing ODM config options not clear

When adding Symfony CMF to an existing project if the ODM config is missed out the error isn't very clear. Running any command produces this error:

[Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException]
You have requested a non-existent parameter "None of the managerParameters resulted in a valid name".

This is the required config addition:

    doctrine_phpcr:
        odm:
            auto_mapping: true
            auto_generate_proxy_classes: "%kernel.debug%"
        ...

Would something like "ODM config not found" make more sense?

Thanks to @lsmith77 for pointing this out on #symfony-cmf

have a special column for publication information

Group all info of the 3 admin extensions in one column: Published, XYZ until XZY | Published, XYZ | Published, until XZY

This should be an optional feature, some might prefer to have separate columns for ordering and filtering.

That would probably work like this: https://github.com/symfony-cmf/CoreBundle/blob/master/Admin/Extension/TranslatableExtension.php#L50

Suggestion from https://blog.liip.ch/archive/2014/12/11/improving-the-user-experience-of-the-symfony-cmf-backend.html : We could use an icon for the column heading of publishable to save some space.

[AdminExtensions] make the form groups configurable

and default to form.group_general

N.B. I can do the PR if this is OK for others something like

cmf_core:
    admin:
        extensions:
            translateable:
                form_group: my-group

or maybe make it configurable per admin class ... that probably would make more sense, but looks not so easy without changes to sonata admin.

changing this in the admin class seems not possible since the extension gets called after configureFormFields.

State Voter

we could have a state based publish workflow. for the access check, it would look like the following. additionally we need to set the available states on the admin (another admin extension i guess).
then we would need to build some sort of event system around the state changes and maybe provide a simple way to define what should happen for which state. also editing permissions will be more complicated than the simple VIEW permissions.

interface ObjectStateInterface
{
    /**
    * A state label
    *
    * @return string
    */
    getState()
}

class PublishStateVoter
{

    /**
    * @param array $stateRoleMap mapping of state names to required symfony role or true for public
    */
    __construct($container, $stateRoleMap)
    ...

    /**
    *
    vote(...)
    {
        $state = $object->getState;
        if (! isset($this->stateRoleMap[$state])) {
            throw ...
        }
        if (true === $this->stateRoleMap[$state]) {
            return self::ACCESS_GRANTED;
        }
        if VIEW_ANONYMOUS is required, return self::ACCESS_DENIED at this point
        $context = $this->container->get('security.context');
        if (null === $context->getToken()) {
            // no firewall, the user is not authorised
            return self::ACCESS_DENIED;
        }
        if ($context->isGranted($this->stateRoleMap[$state])) {
            return self::ACCESS_GRANTED;
        }

        return self::ACCESS_DENIED;

Role Voter for closed user groups

this is somewhat similar to the state voter, but would be more explicit and straightforward to configure (or maybe its just some syntactic sugar around the state voter).

    RoleAccessVoter
    $role = $object->getRole();
    if (!$role) {
        return self::ACCESS_ABSTAIN;
    }
    return $context->isGranted($role) ? self::ACCESS_GRANTED :  self::ACCESS_DENIED;

unpublish now

have a js button next to the publication end date to set the end date to the current date + time to unpublish a document.

fix lower dependency build

PHP Fatal error: Class Symfony\Cmf\Bundle\RoutingBundle\Doctrine\Phpcr\Route contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Symfony\Cmf\Bundle\CoreBundle\Model\ChildInterface::setParentObject, Symfony\Cmf\Bundle\CoreBundle\Model\ChildInterface::getParentObject) in /home/travis/build/symfony-cmf/CoreBundle/vendor/symfony-cmf/routing-bundle/Symfony/Cmf/Bundle/RoutingBundle/Doctrine/Phpcr/Route.php on line 321

https://travis-ci.org/symfony-cmf/CoreBundle/jobs/71390432

expand a twig prev/next function

the idea is to be able to cycle through nodes with a common parent.

f.e.

/news
/news/foo
/news/bar
/news/ding
/news/dong
/news/hello
/news/world

In case I am on /news/ding. The prev button should link to /news/bar and the next to /news/dong

There are a few considerations, like what happens if there is a /news/ding/booo link ? I think there should be an option to define if children of sibling nodes should be considered or not. Furthermore we will likely add some logic to control which pages are accessable (publish/unpublish, publish date etc) which will be handled via some interface, which also need to be considered here.

proposed API:

{# only cycles through exact siblings of the node represented by content #}
{% prev_document = prev_sibling(content) %}

{# cycles through children of siblings of the node represented by content, using the 2nd parameter as the lower bound #}
{% next_document(content, parent) %}

Returns a document instance. The twig function returns Both content and parent can either be Node instances, Document instances or path strings.

update: prev($content)/next($content) is already implemented, but missing support for "rooting" to a specific parent

Why pass request object to ->checkIsPublishable?

Would it not be better to set the request with a kernel event then potentially spreading dependencies?

    // PublishWorkFlowChecker
    public function checkIsPublished($document, $ignoreRole = false, Request $request = null)
    {
    }

Filter by locales

Is it possible filter static content by locales? TranslatableExtension.php doesn't have method configureDatagridFilters. If i wish filter content by language, what i must do?

I tried with add method configureDatagridFilters with content $datagridMapper->add('locales', 'doctrine_phpcr_string'); but I had error Could not find a mapped field or association named "locales" for alias "a"

publication workflow in the area of the save buttons?

As the publish decision is about saving the content and not just one of many sections of the content, the controls would ideally be shown next to the save buttons. we could even replace "Save" with a "Publish" and "Save Draft" resp. "Unpublish" button. (without versioning, saving draft of a published content is not possible, only unpublish)

Check if the Helpers are still needed

there are a few Helpers that do not make much sense, as we put most of their logic into repositories. the handling of binaries could be done with a new controller that serves binary streams. not sure if this would belong here or into a SymfonyCmfMediaBundle

BC break in recent change

this change did a BC break for any applications that configured admin extensions, like the sandbox does:
5c87bb9#commitcomment-4948382

extensions:
    cmf_core.admin_extension.publish_workflow.time_period:
        implements:
            - Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishTimePeriodInterface

i propose that we revert that, the name is not perfect but its not important enough to warrant the BC break imho.

Publish workflow, querying published content only

As for now the publish workflow allows to check if a content is "published" or not.
It would be nice to have something that allows to query the database, filtering only published content.

The typical use case of this is when you want to render a list of contents (maybe a long paginated list): you don't want to get 2k documents from the content repository to just show 20 published content.

limit fields that are shown when embedding editing

this looks pretty bad (the current sandbox is still the same):
http://cmf.liip.ch/de/admin/bundle/content/multilangstaticcontent/cms/content/demo/edit

is there a way we can not have the admin extension trigger when embedding? i guess that is up to the extension itself to figure it out - should we adjust it? i guess we should also hide the name as its not editable anyways.

according to thomas rabaix, the proper way to know if an admin is embedded is to check getParentFieldDescription for null. if i add this to the publish workflow extensions, it gets a lot better:

    if (null !== $formMapper->getAdmin()->getParentFieldDescription()) {
        return;
    }

shall we do that? do we have to make this configurable?

Another option is to have a less compact view ith sonata_type_collection disable the two lines

                    'edit' => 'inline',
                    'inline' => 'table',

which makes the items show as in the stand alone editing with one row per field. that is more usable then what we end up here. however, the layout then still could use a lot of tweaking.

expand twig children function

note: see discussion at the end, we will do a cmf_descendants method that gives descendants paths and cmf_find_many to get documents from paths, with offset and filter

the idea is to be able to list a set of children

f.e.

/news
/news/foo
/news/bar
/news/ding
/news/dong
/news/hello
/news/world

When I am on /news I may want to list all the children

There are a few considerations, like what happens if there is a /news/ding/booo link ? I think there should be an option to define if children of child nodes should be considered or not. Furthermore we will likely add some logic to control which pages are accessable (publish/unpublish, publish date etc) which will be handled via some interface, which also need to be considered here.

proposed API:

{{ children = children(content, limit, offset, depth) }}

content can either be Node instances, Document instances or path strings

it also seems like its useful to be able to get the nodes in reverse order.

this is what is already implemented: children($current, $limit = false, $ignoreRole = false)

Improve location information in admin

This applies to all bundles providing sonata admin classes.

  • Have a content breadcrumb (content ancestor) in parallel with the administrative breadcrumb
  • show tree with location of this document (on the left side?, instead of the parent selector?)

command to build all base paths

currently you need to write some fixture class to create the base paths like /cms/content or /cms/routes. we could provide a compiler pass to allow any bundles to register things that need to be done to initialize the database. then we could build a command in the spirit of init:acl that would have all bundles create their base paths.

Doctrine is not available

On Sylius we've got an issue
An exception has been thrown during the rendering of a template ("Doctrine is not available.")
More details on Sylius and stackoverflow.

Probably introduced by d47ac0f and a proposed fix from @Niiko is here. Is that a proper fix or there is other way to fix this?

Remove MultilangAdmin's

It seems we now have the possiblity of removing the MultilangAdmin classes by using the new Extension feature of sonata_admin, something like:

sonata_admin
  extension:
    cmf_core.admin_extension_multilang:
         implements: Foo/Bar/GetSetLocaleInterface

This would I guess apply to RoutingBundle, ContentBundle, MenuBundle and maybe some others.

Need to decide on an interface to implement (or another way of identifying which Documents need the extension).

Resolve unexpected dependency from CoreBundle to SonataAdminBundle

Due to the recent addition of the PublishWorkflowExtension.php, the CoreBundle now depends on some classes from the SonataAdminBundle included explicitly in the extension.

This dependency should be reflected under "require" in composer.json, or the PublishWorkflowExtension.php execution in the post-install composer.phar hooks should be made optional by default.

I managed to fix this by adding "sonata-project/doctrine-phpcr-admin-bundle": "1.0.*" to my project's composer.json, but I guess this is not an ideal solution.

publication permission

we could make the PublishWorkflowExtension have a constructor argument for who may publish and inject the security context.

it should be quite simple. we can default to ROLE_CAN_PUBLISH or some similar specific role.

publish workflow naming

i wonder about the publish workflow naming and concept. should we have a concept that PublishWorkflowCheckerInterface can be chained, having isPublished return true|false|null ?

also i wonder if PublishWorkflowChecker should be called TimebasedPublishChecker or something, and the PublishWorkflowInterface TimebasedWorkflowInterface? we could already now split out the ispublished flag from the timebased and start to have a chain. then it would be easy to add other workflow checkers to the chain, for example based on a user role or whatever people come up with.

Put SlugifierInterface into its own repository.

We have discussed this a few times already. But I want to propose simply creating a new package for the Slugifier interface - symfony-cmf/api-slugifier.

The class would be caled Symfony/Cmf/Api/Slugifier/SlugifierInterface.

Maybe in the future we will find a better way, or maybe we will create more packages like this, however I think decoupling this interface from the CoreBundle is important.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.