Jump to content

dadish

Members
  • Posts

    124
  • Joined

  • Last visited

  • Days Won

    8

Posts posted by dadish

  1. On 2/5/2017 at 11:44 PM, Werner Pilnei said:

    Hello Nurguly,

    I was following this thread from day one and I am deeply impressed by the passion you are putting into your 'baby'. Especially your in-depth explanations helped me understand your concept and getting first results on my own. Very often talented programmers do not have the same skill in teaching others . But you definitely have.

    Thank you @Werner Pilnei. I am excited because I like using ProcessWire :). I try to do my best in introducing this module to the community. GraphQL is very young standard and is not mainstream yet. I intentionally started this thread in the Pub section, to make sure this is not a module support page but more a discussion on GraphQL (as this new api standard by facebook) and ProcessWire. To talk about how they could fit with each other, what ways we could use it, the new ways to use ProcessWire and so on.

    I personally never think about ProcessWire as a CMS. Though it is in fact a true CMS in its literal meaning, it is best at managing your content. But when people are introduced to ProcessWire it is presented as CMS and since the web is cursed by WordPress, people start using ProcessWire with wrong assumptions in their minds which result in negative impressions. I am generalizing here but when an average web developer hears CMS, she thinks it is a ready website with bunch of functionality baked in like tags, searching, blogging, commenting and so on.  Those functionalities become the evaluation criteria and when they see that there is no tags in ProcessWire they count that as one of the things ProcessWire is missing. They don't understand that tags are something ProcessWire shouldn't have, because they are used to see tags in a CMS.

    I don't think that I am telling something new here. The community is well aware of this problem and the release of new site profile states that these problems are being addressed. But it doesn't have to be the only way. The modular architecture of ProcessWire allows us to extend it anyway we want, and this module is one of those attempts in presenting ProcessWire in different perspective. Even if it won't make much difference, I think we should keep trying and experimenting. Who knows what could come up along the way. I was only thinking about SPAs when creating this module. Never thought of PWA and usage with service workers like you approached it. Which is, by the way is great to hear. I hope there will be bunch of other ways people use it.

    • Like 19
  2. 12 hours ago, bernhard said:

    hmmm... so you define your query on the client side? how can you make sure that people do not modify your queries in a way that you do not want? i read of your server-side restrictions regarding templates, but wouldn't it still be possible to modify the queries to some extend (like changing number of records to show, fields and so on). i'm thinking of someone maybe stealing content or creating his own json exports of my site's content...

    That will all depend on how you configure your api. If you wish this module can expose all pages in your website, including the system ones. Or you can restrict to some very limited data. There are more functionality to come,

    but at this stage the module supports:

    • Template restrictions. You can choose which templates are enabled, but in addition it will conform to ProcessWire permissions. So you could limit access to certain templates only to certain user roles. You can enable access to only logged in users for example.
    • Field restrictions. Also supports ProcessWire permissions. Including template context permissions. Meaning, you can allow title to be viewable for one template and restrict for another.
    • Max limit support. Like the one you use in selectors. So users won't be able to get list of data for more than say 50 pages at a time.

    and also many other security options are on the roadmap: 

    • Built in field restrictions. At it's current stage api gives access to page fields like children, parents, createdUser..., or there are path, size, basename for files and images fields. Those will be disabled and available as extras.
    • Query complexity limit. Currently you can build queries as deep as you want, to request ridiculous amounts of data. This will also be limited for only couple levels of complexity and you will be able to increase or decrease it.
    • Like 10
  3. 2 hours ago, bernhard said:

    hi @Nurguly Ashyrov 

    this looks very interesting!

    could you please provide a simple example how one would use graphql and/or your module with simple ajax requests? i guess this could be great to return data for https://datatables.net/ ?

    Hey there @bernhard! I am happy people like it.

    To make an AJAX request to your GraphQL api, you need to send a query variable that describes what you want. You should've seen how the query looks like in the first post of this thread. To send the query you can use any of your favorite AJAX libraries. Here is how it looks like in jQuery.

    $.post(
      '/graphql/', // this is the url where your GraphQL api is exposed.
      {
        query: "{ skyscrapers { list { title } } }", // this is your query, this one requests the skyscrapers and title field for each of them.
      },
      function (res) { console.log(res); } // here you do whatever you want with the returned data
    );

    Or if you prefer to communicate with your api in application/json, you can do that too. Here is how it looks like in my favorite AJAX library - superagent.

    superagent
      .post('/graphql/')
      .set('Content-Type', 'application/json')
      .send({
        query: "{ city { list { title } } }"
      })
      .then(function (res) {
    	console.log(res.body);
      });

    PLEASE NOTE:  When making requests to your api make sure the url is pointing where it is supposed to. The request will not work if you omit the trailing slash. With default settings the request to /graphql won't be processed by ProcessWire but instead redirected to /graphql/ and the ProcessGraphQL module will not receive the query parameter you sent. Same might happen with leading www. prefix. The request to http://example.com will be redirected to http://www.example.com which will also loose the query parameter in the middle.

    • Like 8
  4. 5 minutes ago, horst said:

    in regard to the public / private part, what @blynx mentioned, especially the by PW default "private fields" simply needs to be covered via accessrights on fieldlevel, set by the dev who wants to use this module. There is no need for public/private endpoints in the mosule. Or I'm wrong?

    If you build a site without that module, you are done by simply use display logic in your templatefiles to control output of fields. If you want to use the module, you additionally have to mimik the accessrights via PW fields access settings. Thats how I understand it by just reading this really valuable thread. :)

    That's right. That's the way it will work when module is done. Currently there is a support for template permissions. Support for Field permissions are on the way, I am working on it but it will take some time. For now you can limit the fields via module settings, I pushed support for legal fields two hours ago.

    12 minutes ago, horst said:

    Thumbs up from @horst ! Yeeeey :) I am a big fan of your modules!

    • Like 7
  5. 14 hours ago, blynx said:

    The confusion might be about this:

    Normally in processwire templates you have to "make the fields public" by manually echoing data in a template (echo $page->title) - so actually for a guest user everything is hidden by default - though by permission actually authorized.

    With this module - everything gets "unveiled" (to use another term here) automatically. This is what I meant by "public" and "private".

    ... am I right?

    Ooh, that's right. Now I get what you mean. Thank you for clarifying that for me.

    That's true, with this module it gets available to the public without echoing it explicitly. So you will have to setup extra permissions to make it closed to the guest user. This was the initial intention of this module really. The goal is to build a tool that will allow you to quickly bootstrap an AJAX api of your ProcessWire content to build SPA out of it. For cases like you guys describe, this module might have some drawbacks. But you could always cook your own GraphQL api and make it behave however you want. It's fairly easy after you learn a bit about it. Here is the library I use for this module.

    • Like 5
  6. 3 hours ago, Ivan Gretsky said:

    I was thinking of a few endpoints to handle authentication with the template permissions. One for the public, another for the registered users with the ability to make changes via mutations when they'll be implemented. There are other ways to handle authentication of course. But then we will have to implement some restrictions via module itself. But maybe I am not seeing something obvious here.

    There is not need for different endpoint for users with different roles. The module does not have any authentication/authorization logic on it's own. The users that will be able to authenticate with this module are the same users in your ProcessWire installation. When I mentioned implementing authentication, I was talking about logging in via GraphQL api, like via AJAX. In reality it will be the same $session->login('username', 'password'), nothing more.

    1 hour ago, blynx said:

    Since by defining legal templates in one endpoint those will be legal for anyone.

    No, no. Of course not. I am sorry for the confusion here. Legal templates mean legal for the api. It does not mean it will make it available to the public. Like I mentioned earlier the module checks if the requesting user has permissions to view, edit, create and etc. If say you select user template as legal. It does not mean it will be public. It means it is available via api to those who are authorized to view it, authorized via ProcessWire's access control system.

    I personally don't think there is even a need for the legal templates option. But it is helpful if you have too many templates and selecting only few can reduce the schema size and make api faster.

    I think there is a bit confusion about this. I want emphasize that this module does not make any data public, nor does it anything private. That is not the module's concern. The module's job is to make your data available in a JSON format, in addition providing the ability to consume that JSON data via GraphQL api. If the user does not have permissions to view a certain page according to ProcessWire's access control system then he won't be able to fetch it.

    45 minutes ago, blynx said:

    Maybe it would also be handy to have an option to define "legalFields" in the same way as templates? So you can restrain the amount of data which is instantly public a bit more granularly. So you can just hide anything which doesn't need to be public at all.

    The same goes for fields. When implemented the user will be able to access only those fields that he is authorized via ProcessWire's access control. But I will add an option for legal fields also, because that also could help reduce the initial schema size.

    • Like 3
  7. 19 hours ago, Ivan Gretsky said:

    Here is another idea. What about the ability to make a number of API endpoints with different allowed templates and restrictions based on single instance of a module. By passing an argument to executeGraphQL() or something like that. With the ability do distinguish them in hooks.

    But all you said is already can be achieved. :) No need to do anything on my side. Maybe add documentation on module's properties though. You can modify the module settings via api by overwriting them. So here how you can set different templates for different endpoints.

    // /site/templates/graphql-endpoint1.php
    <?php
    $ProcessGraphQL = $modules->get('ProcessGraphQL');
    $ProcessGraphQL->legalTemplates = array('skyscraper', 'city');
    echo $ProcessGraphQL->executeGraphQL();
    ?>
    
    // /site/templates/graphql-endpoint2.php
    <?php
    $ProcessGraphQL = $modules->get('ProcessGraphQL');
    $ProcessGraphQL->legalTemplates = array('architect', 'basic-page');
    echo $ProcessGraphQL->executeGraphQL();
    
    // /site/templates/graphql-endpoint3.php
    $ProcessGraphQL = $modules->get('ProcessGraphQL');
    echo $ProcessGraphQL->executeGraphQL(); // here it will use default settings that you set via admin interface

     

    For the ability to distinguish the versions of your GraphQL endpoints. That's also doable without much effort. We are talking about ProcessWire after all. Here how it might look like.

    $config->GraphqlEndpointID = 123;
    echo $modules->get('ProcessGraphQL')->executeGraphQL();
    $config->GraphqlEndpointID = false;

    And same thing in other template files with different endpoint id. Now anywhere you attach a hook, you can know which endpoint of your api is being executed, or if it is being executed at all. You just need to add a conditional block in your hook. Something like

    if (wire('config')->GraphqlEndpointID === 123) {
      // some bussiness here
    }

     

    I am a bit confused though. One of selling points of GraphQL is that there is only one url that you need to deal with. It's just `example.com/graphql/` and nothing more. No more this

      GET example.com/graphql/skyscrapers/
      GET example.com/graphql/skyscrapers/{id}
     POST example.com/graphql/skyscrapers/
      PUT example.com/graphql/skyscrapers/{id}
    PATCH example.com/graphql/skyscrapers/{id}
      GET example.com/graphql/architects/
     ...

    It's only one endpoint for everything you need. That's actually is the way it is encouraged to build GraphQL api. Also it is only one HTTP verb you need to use, which is POST. You can stop thinking about dealing with PUT, PATCH, HEAD, OPTION and more. You only need this with GraphQL.

    POST example.com/your-endpoint-url/

    And that's it. One HTTP verb and one url to rule them all :)

    • Like 3
  8. 1 hour ago, BitPoet said:

    Just a thought, since this is what I try to do with my own modules: could you add hookable methods in ProcessGraphQL that allow implementing custom restrictions? These hookables could be no-ops if not hooked and receive all information about the query at the time of calling, enabling users to filter or reject queries before or after they've run.

    That's a very good idea! Will do that. Thank you for the tip.

    • Like 2
  9. On 1/30/2017 at 7:22 PM, teppo said:

    Obviously this is mainly a problem with systems that include a enabled-by-default (or always enabled) built-in public API, and less so when enabling/installing the API itself is a conscious choice.

    Either way, it's good to understand that exposing your content to the world via a publicly queryable API may uncover some surprises. This is one of the reasons why I find certain value in the idea of crafting the API per current needs and so that it only exposes the minimum viable amount of data :)

    You are completely right. I can't argue that "enabled-by-default" approach can lead to lots of security issues. That's why I am limiting the exposable pages only to selected templates. While the selector option is quite simple to implement I don't want to enable this kind of option because I believe it should not be this module's concern.

    The way I see it, if this module stays consistent and retrieves data only through $pages->find() api (or it's equivalent like $page->children(), $page->siblings() etc) that should give the user any type of control with the security. For example what you suggest could be achieved with a single hook. Say this is your template file where you expose your GraphQL api (something like /site/templates/graphql.php).

    <?php
    
    echo $modules->get('ProcessGraphQL')->executeGraphQL();

    What you suggest could be achieved like this.

    <?php
    
    wire()->addHookAfter('Pages::find', function($event) {
      $event->return = $event->return->filter($mySecuritySelector);
    });
    
    echo $modules->get('ProcessGraphQL')->executeGraphQL();

    I would prefer users to approach security this way. This strategy to security gives full control for the user while allowing me to stick to a single rule when concerned about security and makes the code of the module much easier to reason about. I do realize that I could just insert the above code in the module and that's basically an implementation of what you suggest. But I don't want to encourage the user to solve security problems via module settings because no matter how hard I try, I won't be able to make this module dummy proof without limiting it's capabilities.

    Another thing I wanted to mention is that I see this module as a GraphQL representation of ProcessWire api. Like @Ivan Gretsky mentioned, if done right, this could allow us to build lot's of useful developer tools on top of this module. Even a mobile app that gives you limited site administration capabilities. But only if module is consistent with how ProcessWire behaves. And that includes the security of course.

    On 1/30/2017 at 7:22 PM, teppo said:

    Note: don't get me wrong, I'm definitely not against this module. What I've said here is mostly theoretical. I also think that your idea of being able to manually define queryable templates makes a lot of sense. While I'd still suggest enabling a selector instead, you obviously know the use cases (and the implementation) better.

    Oh no sir, not at all. I value your opinion very much. That's exactly what I wanted to hear from the community, opinions. I am thankful to you for mentioning this aspect of the module in it's early stage, before I started to implement other features that depend on it, like authentication or others that I might not think of right now.

    • Like 9
  10. 1 hour ago, teppo said:

    That being said, have you already implemented or are you considering implementing custom selector support for such limits? I.e. allow the developer to manually define a selector that returned pages must match, or alternatively should never match? I think that could make a lot of sense from a security point of view, particularly for public API endpoints, where it might actually work best as a per-endpoint setting :)

    I have not thought about this kind of security layer. Though it sounds reasonable. I will keep in mind this option. For now I plan to add an option to limit the templates that are meant to be accessible via public api by explicitly selecting them.

    • Like 2
  11. 41 minutes ago, teppo said:

    ...sometimes a site may have content that is viewable only if you know the direct URL, and a public API like this may make it "more public" than the developer intended.

    Are you talking about pages with the status hidden? If thats the case, it should behave as expected. At this point this module accesses content only via $pages->find(). As long as $pages->find() does not return pages that are not intended for public this module should not make it accessible. I do not use $pages->get()as it bypasses some permission rules.

    41 minutes ago, teppo said:

    Another thing is that there may be a code-level permission check in place, and a module like this would have hard time figuring that out.

    As a proper citizen of ProcessWire, one would implement code-level permission check by attaching a hook to User::hasPagePermission, User::hasTemplatePermission or any other equivalent, including field level permissions. For that cases this module wouldn't have to figure out anything, it will happen naturally. But for those cases where access to resources are checked outside of ProcessWire's permissions context, this module might not be a good fit for building service api.

    • Like 1
  12. 16 hours ago, Ivan Gretsky said:

    Seems like ProcessWire fits graphql like a glove.

    You couldn't be more precise! GraphQL and ProcessWire fit each other very well. All this module does is just maps the ProcessWire's fieldtypes with GraphQL type system. It literally tells GraphQL that FieldtypeText is a StringType, FieldtypeDate is DateType and so on. And for getting the data, on average, it is less than a single line of code :). Since you can access value of a page field like $pages->$fieldName all primitive fields inherit a method for accessing data from one place. I sure having lots of fun writing this module.

    16 hours ago, Ivan Gretsky said:

    Dries Buytaert, the Drupal "godfather", has a nice article in his blog about the necessity for a contemporary CMS to have support for web-services built in. And his choice seems to be graphql and json api. I am sure that improving and promoting PW as a "headless CMS" kind of thing is something that could bring a lot of frontend developers to use PW. This module is the perfect start.

    I agree with Drupal "godfather" totally. The need for quick bootstrapping of an api service with flexible content structure is in very high demand. I had a hard time landing a job as a ProcessWire developer. So I target myself as a full-stack SPA developer in React.js/Node.js. I tried many of open source REST frameworks in Node.js that would help me get started with a project quickly. But non of them offered enough flexibility for my style of programming (I guess ProcessWire spoiled me :)). At the time I figured out the best way to build REST api in Node.js I found out that REST is not flexible either. When an app starts evolving REST gets very messy. The Github built three versions of their REST api and still are not happy with it and now decided to release a GraphQL api which probably will not introduce breaking changes in the future, because GraphQL is designed that way.

    I think if made correctly, this module could bring a great value to many ProcessWire users.

    16 hours ago, Ivan Gretsky said:

    As I understand, mutations are a way to not only read, but write data? If so, that is certainly worth implementing, so a complete SPA could be possible with this graphql module alone.

     That's right. That is the main goal of this module. I will eventually implement all the features that needed to build a complete SPA with this module. I just try to move carefully and a usage feedback from community would help a lot. Just installing it and making couple queries to confirm that it works as expected would be great.

    • Like 10
  13. 1 hour ago, apeisa said:

    Looks super cool!

    Does it add some public endpoint to fetch data or how does it work outside the "console" example shown on the videos?

    Of course. In one of your templates (edit: In one of your template files) you simply do

    <?php
    echo $modules->get('ProcessGraphQL')->executeGraphQL();

    and that's it. It will handle all GraphQL requests. There is more info in the repository.

    • Like 3
  14. 36 minutes ago, LostKobrakai said:

    There are also field access settings, just to be sure you're aware of them.

    Yes, I am aware of field permissions, thank you for reminding. I have not added support for them yet. Though it is definitely in my todo list for this module.

    • Like 3
  15. On 1/28/2017 at 6:10 PM, teppo said:

    There's also the question of security: there may be cases where something is technically speaking public, but not accessible via your existing web site, and a "generic" REST API could result in some surprises there.. and, of course, if it's not read-only, that's a whole another thing to worry about.

    By the way, in terms of security, this module follows the permission settings in ProcessWire. All it does is collects the templates that are viewable by the client via

    $user->hasPermission('page-view', $template);

    and for every request it makes sure to returns only those pages that have one of those templates. So, as long as user does not have permission to view the page, she won't be able to fetch it. I tried to make module reflect your existing settings as much as possible. It basically delegates everything possible to ProcessWire itself. 

     

    On 1/28/2017 at 6:54 PM, LostKobrakai said:

    I always though graphql would only be for qraph databases, but really this looks damn rad.

    That is so true. I personally thought GraphQL was new SQL.

    • Like 5
  16. NOTE: This thread originally started in the Pub section of the forum. Since we moved it into the Plugin/Modules section I edited this post to meet the guidelines but also left the original content so that the replies can make sense.  

    ProcessGraphQL

    ProcessGraphQL seamlessly integrates to your ProcessWire web app and allows you to serve the GraphQL api of your existing content. You don't need to apply changes to your content or it's structure. Just choose what you want to serve via GraphQL and your API is ready.

    Warning: The module supports PHP version >= 5.5 and ProcessWire version >= 3.

    Links:

    Please refer to the Readme to learn more about how to use the module.

     

    Original post starts here...

    Hi Everyone! I became very interested in this GraphQL thing lately and decided to learn a bit about it. And what is the better way of learning a new thing than making a ProcessWire module out of it! :)

    For those who are wondering what GraphQL is, in short, it is an alternative to REST. I couldn't find the thread but I remember that Ryan was not very happy with the REST and did not see much value in it. He offered his own AJAX API instead, but it doesn't seem to be supported much by him, and was never published to official modules directory. While ProcessWire's API is already amazing and allows you to quickly serve your content in any format with less than ten lines of code, I think it might be convenient to install a module and have JSON access to all of your content instantly. Especially this could be useful for developers that use ProcessWire as a framework instead of CMS.

    GraphQL is much more flexible than REST. In fact you can build queries in GraphQL with the same patterns you do with ProcessWire API.

    Ok, Ok. Enough talk. Here is what the module does after just installing it into skyscrapers profile.

    ProcessGraphQL-Query.gif

    It supports filtering via ProcessWire selectors and complex fields like FieldtypeImage or FieldtypePage. See more demo here

    The module is ready to be used, but there are lots of things could be added to it. Like supporting any type of fields via third party modules, authentication, permissions on field level, optimization and so on. I would love to continue to develop it further if I would only know that there is an interest in it. It would be great to hear some feedback from you. I did not open a thread in modules section of the forum because I wanted to be sure there is interest  in it first.

    You can install and learn about it more from it's repository. It should work with PHP >=5.5 and ProcessWire 3.x.x. The support for 2.x.x version is not planned yet.

    Please open an issue if you find bugs or you want some features added in issue tracker. Or you can share your experience with the module here in this thread.

    • Like 44
    • Thanks 4
  17. Made a little patch (0.1.1). Now it checks if the field is editable and if the page is locked before showing you the button.

    I was about to submit the module into Modules directory, but then I encountered the ProcessWire version compatibility field. The highest option is 2.6 and this module supports only 2.6.5 and higher.

    Can anybody confirm that I wont be able to publish it until 2.7 ?

    • Like 1
  18. Hello and welcome!

    I am not good at MySQL really. But you could achieve that with php script. The below code is an example, not tested yet. But you should get the idea.

    <?php
    
    // First get all the pages you want to manipulate
    $list = $pages->find("template=example");
    
    // The language from which you want the values
    $langFrom = $languages->get('fr-fr');
    
    // The language into which you want the values
    $langTo = $languages->get('fr-be');
    
    // List of all fields that should be copied
    // if a page has it. Some fields does not have a
    // multiple language entries. Like Images, Files, Checkboxes
    // this will help us avoid errors
    $validFields = array(
      'title',
      'body',
      'summary',
      'sidebar'
      );
    
    // Loop through pages
    foreach ($list as $p) {
      
      // Turn off outputFormatting
      $p->of(false);
      
      // then loop through each field
      foreach ($p->fields as $field) {
    
        // If the field is not one of those we want to copy then ignore
        if (!in_array($field->name, $validFields)) continue;
    
        // Get the value of the field for from language
        $fromValue = $p->getLanguageValue($langFrom, $field->name);
    
        // Here you can do any manipulations to your value 
        // if you want to alter it a little
        // ...
        // ...
    
        // Set the value of the field for to language
        $p->setLanguageValue($langTo, $fromValue);
      }
    
      $p->save();
    
      echo "$p->title is updated<br />";
    }

    Note: If there are too many pages on which you need to perform this, consider limiting the list and doing it with small batches. Like...

    <?php
    
    $limit = 100;
    $page = 1;
    $start = ($page - 1) * $limit;
    
    $list = $pages->find("template=example, limit=$limit, start=$start");
    // and the rest is like above
    // ...
    
    • Like 1
  19. Quickly toggle your checkboxes with extra action buttons via AJAX.
     
    The module adds functionality to InputfieldCheckbox so you can toggle the checkbox fields in the extra action buttons intruduced in ProcessWire 2.6.5 via AJAX.
     
     
    Requirements
    This module works only for ProcessWire versions later than 2.6.5.
     
    How to Install
    1. Copy the files to /site/modules/ProcessQuickToggle/ 
     
    2. In your admin, go to Modules > Refresh for new modules. 
     
    3. Click the "Install" button next to "Process Quick Toggle".
     
    Usage
    Go to any checkbox field you want to enable quick toggle feature for. Setup > Fields > my_checkbox_field. There in the Input tab you should see an Enable Quick Toggle field. After you check it you will see some fields that you can fill based on your needs.
     
    settings.png
     
    Then save the field. Now there should be an extra button for every page that has this field in the Pages tree.
     
    list.png
     
    list_action.png
     
    Features 
    • Supports template contexts.
    • Supports core FontAwesome icons.

    Any kind of feedback is appreciated. 

    • Like 15
  20. Nice work :)

    In case for core LanguageSupport module. When you publish an article in english, it will be shown in the russian version of the site too, even if there isn't a russian version of the article. It will also will show in places like navigation, on sidebar with the latest news etc. However if you put everything english under one page and build every parts of your site under that page (your navigation root, latest news) then your article in english language will never be seen in russian version.

    That's not entirely correct. If you don't check the "active" checkbox under Settings when editing a page, the page is not listed for users with this language, e.g. excluded from $pages->find calls. I'm not sure what the default behaviour is when you visit the page directly in a language which is not active. Maybe you'll see the content in the default language. In this case, I'm sure you could also hook into somehwere and throw a 404 or do the prefered action.

    Yep. You're right. I did not even know that. I guess the difference with this module is only the contextual approach to the multi-language sites. I don't think this module is any better than the core LanguageSupport module, just the other way of doing things. I posted it here for community to decide. If we decide we don't need it the thread will stay only for historical reasons I guess.

    Another thing might be the hassle with FieldTypes that do not support multi-language. FieldtypeFile and FieldtypeImage for instance. I know that you can have multi-language descriptions. But what about files themselves. You can't have different files for different languages.

    • Like 4
  21. Seems like a lot of work you have done. But why is this module even needed while we have native PW multi-lingual support. I am probably missing something, so please be so kind to point out the differencies.

    ProcessWire does not support tree based multi-language sites. This is just another approach to handle sites with many languages. Some people prefer to do it this way.

    There are cases where websites have 600 pages in english, 700 pages in russian and only 300 pages in english and russian. In this case it is much better to keep the content separate, each under one section and have their translations linked.

    In case for core LanguageSupport module. When you publish an article in english, it will be shown in the russian version of the site too, even if there isn't a russian version of the article. It will also will show in places like navigation, on sidebar with the latest news etc. However if you put everything english under one page and build every parts of your site under that page (your navigation root, latest news) then your article in english language will never be seen in russian version.

    Update: The last paragraph is not entirely correct. See the comment by @Wanze below.

    • Like 4
  22. Babel is a module that provides functionality for managing section based multilanguage sites. It is inspired by Babel Plugin for MODX.

    There are cases where websites have 600 pages in english, 700 pages in russian and only 300 pages in english and russian. In this case it is much better to keep the content separate, each under one section and have their translations linked. This module helps you to manage those links.



    How to Install

    Warning

    Backup your database before installing this module.

    Requirements

    Babel works on top of core LanguageSupport module. You will have to install it first and create languages for your site. Then:

    1. Copy all the module files to /site/modules/Babel/ 

    2. In your admin, go to Modules > Refresh for new modules. 

    3. Click the "Install" button next to Babel.

    Settings

    First of all you should set the root page for each language. Usually section based multilingual sites will have a structure like...



    Root
      |__Home (English)
           |__About
           |__News
               |__ News Article 1
               |__ News Article 2
               |__ News Article 3
               |__ News Article 4
           |__Contact
           ...
      |__Главная (Russian)
           |__О нас
           |__Новости
               |__ Новостная статья 1
               |__ Новостная статья 2
               |__ Новостная статья 3
               |__ Новостная статья 4
           |__Связаться
           ...
      |__Baş Sahypa (Turkmen)
           |__Barada
           |__Habarlar
               |__ Habar Makalasy 1
               |__ Habar Makalasy 2
               |__ Habar Makalasy 3
               |__ Habar Makalasy 4
           |__Aragatnaşyk
           ...


    You just need to select the languages that you want Babel to manage and select the root pages for each of them.

    bbl_settings.png

    API

    Babel creates couple useful methods and a properties for you.

    language (Page property)

    Every page that is handled by Babel (those who are descendants of root pages that you choose on Babel module's settings page) will have a language property that returns a Language object that they are assigned to. It is determined based on under which rootParent the $page lives;

    Syntax



    $page->language;


    Return

    Language object.

    translation (Page method)

    This method returns the page that was assigned as a translation for the given $language.

    Syntax



    $page->translation($language);


    Arguments

    The $language argument could be either a string (the name of the language), an integer (the id of the language) or a Language object itself.

    Throws

    WireException if the language is not found or isn't handled by Babel.

    Return

    The method returns a Page object or NullPage if the translation is not available.

    translations (Page method)

    This method will return the pages that were assigned as a translation for all $languages that are available for that page. Or an empty PageArray if no translations are available.

    Syntax



    $page->translations();


    Return

    Returns PageArray.

    addTranslation (Page method)

    This method will create a translation link from $page to $otherPage.

    Syntax



    $page->addTranslation($otherPage[, $overwrite]);


    Arguments

    The $otherPage argument should be a Page object. The language of the page will be determined by Babel itself based on under which language section the page is located.

    The $overwrite argument is optional and has three case scenarios:

    • If omitted the reverse link of the translation will be created only if the $otherPage does not have a translation link for the $page->language
    • If set to true the reverse link will be overwritten even if $otherPage does have a translation link for the $page->language.
    • If set to false the reverse translation link will not be created even if the $otherPage does not have any translation links.
    Return

    boolean if the translation link/s is/are created successfully or not.

    removeTranslation (Page method)

    The method removes a translation link between $page and $otherPage.

    Syntax



    $page->removeTranslation($language[, $remove]);


    Arguments

    $language (string|integer|Language) The language link you wish to remove.

    $remove (boolean) If there is a reverse link that points back from the translation to this page, should Babel remove it too. Default is false.

    Throws

    Throws WireException if the language couldn't be found or is not handled by Babel.

    Return

    boolean If the removal of the translation link/s is/are successful.

    closestParentTranslation (Page method)

    Returns the translation of the closest translated parent of the page.

    Syntax



    $page->closestParentTranslation($language);


    Arguments

    $language (string|integer|Language) The language for which you want a translation for.

    Throws

    Throws WireException if the language couldn't be found or is not handled by Babel.

    Return

    Page object.

    getRoot (Babel method)

    Returns the rootPage of the language. The one you have assigned in the module settings page.

    Syntax



    $modules->get('Babel')->getRoot($language);


    Arguments

    $language (string|integer|Language)

    Throws

    Throws WireException if the language couldn't be found or is not handled by Babel.

    Return

    Page object.

    translatable (Babel method)

    Tells if a page is translatable via Babel or not.

    Syntax



    $modules->get('Babel')->translatable($page);


    Arguments

    $page is a Page object. The page you want to check on.

    Return

    boolean If the page is translatable or not.

    getTranslated (Babel method)

    Returns a PageArray of translated pages. 

    Syntax



    $modules->get('Babel')->getTranslated($fromLanguage, $toLanguage[, $limit[, $pageNum]]);


    Arguments

    $fromLanguage (string|integer|Language) The language from which the translation is. If omitted (or null given) then all the translation from all languages will be considered.

    $toLanguage (string|integer|Language) The language to which the translation is. If omitted (or null given) then translation to all languages will be considered.

    $limit is the number of pages you want to receive. Default is 50.

    $pageNum is the page number. Use for pagination of the returned PageArray. Default is 1.

    Throws

    Throws WireException if the language couldn't be found or is not handled by Babel.

    Return

    PageArray.

    getUntranslated (Babel method)

    Returns a PageArray of pages that are not translated to one or the other language.

    Syntax



    $modules->get('Babel')->getUntranslated($fromLanguage, $toLanguage[, $limit[, $pageNum]]);


    Arguments

    $fromLanguage (string|integer|Language) The language from which there is no translation. If omitted (or null given) then all the untranslated pages from any language will be considered.

    $toLanguage (string|integer|Language) The language to which there is no translation. If omitted (or null given) then untranslated pages to any language will be considered.

    $limit is the number of pages you want to receive. Default is 50.

    $pageNum is the page number.  Use for pagination of the returned PageArray. Default is 1.

    Throws

    Throws WireException if the language couldn't be found or is not handled by Babel.

    Return

    PageArray.

    babelHomePage ($config property)

    Reference to the home page. This is usually one of the pages that you chose in the module settings.

    Syntax



    $config->babelHomePage;


    The babelHomePage is determined based on:

      the path that user has entered...

      if not available then the session is checked...

      if not available then it defaults to the core LanguageSupport module's default language;

    Usage Tips

    If your site structure is the way it looks like it is shown above. Your root page usually won't ever render. Whenever a user enters your site at the `/` path you will redirect him to one of language root pages. Here is how I suggest you to do that in your root.php template file.



    $session->redirect($config->babelHomePage->url, false);


    that's pretty much everything you need to have in your root.php file.


    To display links for each language homepage...



    $home = $config->babelHomePage;
    $homePages = $home->translations()->and($home);
    echo "<ul>";
    foreach ($homePages as $p) {
    $title = $p->language->title;
    echo "<li><a href='$p->url'>$title</a></li>";
    }
    echo "</ul>";


    To display links for each translation of the current page...



    echo "<ul>";
    foreach ($page->translations() as $p) {
    $title = $p->language->title;
    echo "<li><a href='$p->url'>$title</a></li>";
    }
    echo "</ul>";



    ProcessBabelTranslate

    Babel comes with very useful admin helper. You can link pages as a translation for each other. You can create translation page if there isn't one yet. There is small indicators on page tree that shows which pages are translated to what languages. And you get a Babel Tanslate page where you can quickly find all the translated and untranslated pages accross your site. See the screenshots below...

    Settings page

    pbt_settings.png

    Translation indicators

    pbt_indicators.png

    Quick action button

    pbt_action_button.png

    Quick modal translate form

    pbt_quick_translate.png

    Tab translate form

    pbt_translate_form.png

    ProcessBabelTranslate page

    pbt_pages_menu.png

    pbt_find.png

    Go ahead and try it out. This is my first module to publish. Please tell me if something need to be added, changed, removed...

    • Like 14
×
×
  • Create New...