I've created a serializer and ViewSet for my model and added a template for a list view. To get to see the web page (template) the ordering render classes must be correct and one needs to add the TemplateHTMLRenderer to the list of renderers.
This now leads to the issue that when wanting to browse to a specific record like
/mymodel/5
in the browser, I get shown the list view too.
The goal is to have 1 url that serves the api (json) or the web page both for list and detail views.
(/mymodel = list, /mymodel/5 = detail)
The question is: how can I have multiple templates (list/detail) based on one ViewSet?
The solution is to override the get_template_names method and return the template according to the action being performed.
def get_template_names(self):
if self.action == 'list':
return ['list.html']
elif self.action == 'retrieve':
return ['details.html']
Related
I am trying to use output data that I have set up using the builder plugin through the Record Details component and running into some issues.
In my created plugin, Schools, I have Instructors (set up as a relation to an instructors plugin I created separately). It is possible to have more than one instructor, so they are store in the database as an array. Like so:
[{"instructor":"69"},{"instructor":"79"},{"instructor":"80"},{"instructor":"96"}]
The numbers represent the row ID of the instructor table
In my CMS I can pull all of the School info just fine into a partial (Builder Details), and can pull the array of instructors, but I am struggling to pass this array over to look up the ID and get the instructors information. My thought right now is to send it to another partial like so:
{% "school/instructor" insProfile = instructorID %}
The partial school/instructor is getting the ID just fine. I have included the Builder Details component and set it up with the following:
Alias: builderDetails
ModelClass: Instructors Plugin
Identifier value: insProfile
Key Column: id
Display Column: member_name
I am getting record not found results. I am confused as to how to set the Identifier Value to match the value I passed through my partial. I tried {% set identifierValue = insProfile %} before the {% set record = ... %} is run, but that did not work either.
I cannot use the :slug because that is already generating the content needed for the School page. In a TLDR, it seems I ultimately want to duplicate this function through another partial and a different tag.
Still learning October, so any help is appreciated.
I think the original post was a bit lengthy and ultimately what I wanted to do was pass a variable into a component. Such as:
{% component 'builderDetails' identifierValue=dynamicVar %}
This does not appear to work as the builder details component generates too far into page load to pick up the variable change.
Per the OctoberCMS docs, the best solution for me was to create my own component that would accept the variable before the page processed using onRender() function.
public function onRender()
{
// This code will be executed before the default component
// markup is rendered on the page or layout.
$this->profileID = $this->property('insProfile');
$this->ins = $this->getUserInfo($this->profileID);
}
This allows me to put my component in a partial, and request the partial with the variable 'insProfile'. The component will pick up the property insProfile before page/plugin generates and use that variable instead of the default.
I then set up another function to query the correct user info needed (getUserInfo)
It would be nice if the builder plugin components could be updated in such a way that I did not have to do this as the builder plugin is rather extensive out of the box.
Posting this in case anyone else comes along this problem and isn't sure where/how to pass a partial variable into a component.
I don't know why the collection-valued navigation properties are always return an empty list with a reference to the actual data link.
here's the query I tried :
GET <organization>/api/data/v8.0/new_subjectareas?$expand=new_product_new_subjectarea&$count=true
Response :
{
"#odata.context":"<organization>/api/data/v8.0/$metadata#new_subjectareas",
"#odata.count":150,
"value":[
{
"#odata.etag":"W/\"9644599\"",
"timezoneruleversionnumber":null,
"processid":null,
"_stageid_value":null,
"new_product_new_subjectarea":[],
"new_product_new_subjectarea#odata.nextLink":"<organization>/api/data/v8.0/new_subjectareas(622bcca9-8946-e511-80fb-00155d002810)/new_product_new_subjectarea"
},
....
How can I get the items within the navigation property (new_product_new_subjectarea) without make a new request to it's reference (new_product_new_subjectarea#odata.nextLink)?
The behaviour you are seeing is "by design" (even if one could argue that this particular design choice is questionable).
When you expand a collection-valued navigation property, you will always get an empty array plus a [relation name]#odata.nextLink property. There is currently no way of getting the actual relation rows without making an additional request.
Source: the example in the official documentation.
It looks like you are returning to many entities.
source: https://msdn.microsoft.com/en-us/library/gg334767.aspx#bkmk_limits
I'm looking for a working example of how to sort my pages in the sitetree by title by default. Ideally I only want to sort child pages of a certain type. In my case I want all my Gallery pages under the parent Portfolio to be sorted Alphabetically by their title.
This is for easy searching in the backend CMS as I know how to sort them in the template.
I have found these examples but not enough to work this out for SS3.1+
http://www.ssbits.com/tutorials/2011/custom-sorting-in-the-cms-sitetree/
https://github.com/silverstripe/silverstripe-cms/issues/848
Having a look at the examples you gave and the current Silverstripe source, there is a few ways you could go about this. My solution involves using Silverstripe's extension system to manipulate how the hierarchy is generated.
How the SiteTree is loaded
The way the CMS loads the site tree is a little lengthy so I will quickly simplify:
The template CMSPagesController_Content.ss (used for the pages section) has markup to lazy-load the linked tree view
The linked tree view (a function specified in CMSMain) calls a few internal methods to basically load the CMSMain_TreeView template
This template calls the SiteTreeAsUL function back in CMSMain
Note: SiteTreeAsUL allows us to hook in before returning using the extension system in Silverstripe though we don't want to manipulate
the HTML directly.
getSiteTreeFor, a function part of LeftAndMain, is called inside the SiteTreeAsUL.
getSiteTreeFor calls getChildrenAsUL, a function part of Hierarchy, which actually does the HTML building but most importantly, calls the correct "children" method.
I say correct children method as there is a few:
AllChildren
AllChildrenIncludingDeleted
AllHistoricalChildren
Children
Because getSiteTreeFor is called without specifying the children method, it uses a hardcoded default of AllChildrenIncludingDeleted.
Now, time to sort the children...
Calling the function AllChildrenIncludingDeleted does a few calls but what we want to know is that it internally calls the extension method augmentAllChildrenIncludingDeleted.
So, to do what you are wanting to do, you likely will want to write an extension for SiteTree with the extended function augmentAllChildrenIncludingDeleted. First argument is the list of all children which are stored as an ArrayList.
Technical Note: It actually can be an ArrayList OR DataList
because if there are no live children, it returns the raw result of
stageChildren which is a DataList.
While both have sort functions, they may act differently.
ArrayList provides a sort function which would allow you to do what you were intending.
Something like this should work:
class CMSSiteTreeSortingExtension extends Extension
{
public function augmentAllChildrenIncludingDeleted($Children, $Context = null)
{
if ($this->owner->ClassName == 'GalleryPage')
{
//Do your class specific sorting here....
}
$Children = $Children->sort('Title DESC');
}
}
And just set the extension against SiteTree (or Page if you want, should still work).
Disclaimer: I haven't personally tried this however it follows the standard pattern for how Silverstripe works with extensions so you shouldn't have a problem.
I've been searching for a way to achieve this in SS4 when I couldn't get the above code to work. This is what I've come up with.
use SilverStripe\ORM\DB;
class MemberPage extends Page
{
public function onAfterWrite(){
parent::onAfterWrite();
$pages = MemberPage::get()->sort('Title');
$sortIndex = 0;
foreach ($pages as $page){
//sort indexes start at 1
$sortIndex++;
if ($page->Sort != $sortIndex){
//we can't use $page->write() here, otherwise it'll cause infinite loops,
//we'll just have to run the query on the database directly
DB::query("UPDATE SiteTree SET Sort = {$sortIndex} WHERE ID = {$page->ID}");
}
}
}
}
It's not exactly 'the silverstripe way' but it works.
I'm building a web service for a client that pulls data from the Sitefinity CMS. The problem is they want to pass in a Guid for the service and receive the info about this item. No problem except I only have been able to locate the "live" Guid for one Item (and that was by combing through the HTML in the back end).
I was going to look at the tables in SQL Server but I'm not sure which table to look at. The content items have several tables all related of course and there isn't any documentation on how to look at this. I can find plenty of documentation on querying the master Guid, but no place to find it.
Oh, and these are custom content types built by the Module Builder.
Any Help would be SOOOOO appreciated!
var master = DynamicModuleManager.GetManager().Lifecycle.GetMaster(<liveGuidHere>);
One of the biggest consumers of Sitefinity webservices is Sitefinity. The best place to start looking for that guid is to take a look at what web service calls are being made when you pull up your custom content item list in the backend. I used the chrome developer tools and check in the network tab.
One I found for a stores module made with module builder was something to the effect of http://www.testsite.com/Sitefinity/Services/DynamicModules/Data.svc/?managerType=Telerik.Sitefinity.DynamicModules.DynamicModuleManager&providerName=OpenAccessProvider&itemType=Telerik.Sitefinity.DynamicTypes.Model.Stores.Store&provider=OpenAccessProvider&sortExpression=LastModified%20DESC&skip=0&take=50
The json this returns is a list of all the masters with their ids (note in the list that the content items all have have a status of 0) http://www.sitefinity.com/documentation/documentationarticles/developers-guide/sitefinity-essentials/modules/content-lifecycle
When you go to Administration / Module Builder / Your Module, you will see a link to the API on the top right corner.
This link goes to a page full of API examples for your particular module which is kind of cool.
Basically you would have to find your item first using LINQ and the GetValue extension method.
Once you have the item you can get its ID or any other property.
using Telerik.Sitefinity.Utilities.TypeConverters;
using Telerik.Sitefinity.DynamicModules;
using Telerik.Sitefinity.Model;
....
var mgr = DynamicModuleManager.GetManager();
var countrymasters = from ctry in mgr.GetDataItems(TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Destinations.Destination"))
where ctry.GetValue<string>("culture") == siteid &&
(ctry.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live && ctry.Visible == true)
select new
{
airport_cd = ctry.GetValue<string>("airport_cd"),
country_master_cd = ctry.GetValue<string>("country_master_cd")
};
I've looked at this article and am getting strange behavior in a HAML partial. When I access two different controller actions, one instance works while the other fails. Here's the code:
= link_to_unless_current(t('some.string', :en=>'SomeString'), '/url',{:class=>(controller.controller_name == 'randomController' ? 'current' : 'header-link')})
When I output = controller.controller_name in either view, I get 'randomController.' On the main listing page (where all objects are shown paginated), the class is not applied, but when moving to the 'show' page, the class suddenly appears. As the controller is the same in both (same string is printed in either case), why is it that the class isn't applied equally?
In general, is there a better way to style links based on the current controller, instead of checking the controller name? The current_page helper requires both controller + action, meaning it's not a fitting candidate here.
I'm going to mark this as the answer in case others stumble over the same problem. Hopefully this formats properly:
= link_to_unless_current(t('some.string', :en=>'SomeString'), '/url',{:class=>(controller.controller_name == 'randomController' ? 'current' : 'header-link')}) do
%span.current
= (t('some.string', :en=>'SomeString'
This can be obviously be cleaned up to remove repeated elements, and will probably end up as an application decorator using draper so that I can use it within any view on the site.