SilverStripe ModelAdmin number of records per page for pagination is not working - silverstripe

I am working on a SilverStripe project. In my project, I am building a model admin class to display the list of records within the CMS. I am trying to limit the number of records rendered per page. So I put $page_length field in my model admin class like this.
class OrderAdmin extends ModelAdmin
{
private static $managed_models = [
Order::class
];
private static $url_segment = 'orders';
private static $menu_title = 'Orders';
private static $menu_icon_class = 'font-icon-checklist';
private static $page_length = 25;
//other code goes here
}
As you can see, in the code above, I set the number of records per page to 25. Even though I set it to 25, it is still displaying 15 records per page within the CMS. I rebuilt and refreshed the project as well. What is missing in my code and how can I fix it?

This was a bug in Silverstripe Admin module around version ~4.3. This has since been fixed:
https://github.com/silverstripe/silverstripe-admin/issues/971
Your code private static $page_length = 25; is the correct way to set the page length. This will now work correctly in Silverstripe 4.5

Related

Custom ListPreference in AndroidX

I have customized my Android Application Setting page, I use API 21 or 26. I have added a CustomListPreference java class which was inherited from ListPreference and integrated it into the SettingActivity.
But, I relisted the system doesn't work, as SettingActivity has Setting fragment inherited from androidx.preference.PreferenceFragmentCompat and packages used for the Setting Activity are as follows:
androidx.preference.Preference
androidx.preference.ListPreference
androidx.preference.PreferenceFragmentCompat
If I use packages android.preference.Preference and android.preference.ListPreference for my Custom ListPreference, all my code stops working when Android creates objects for the Setting Activity. It crashes just after the custom ListPreference constructorwith error "Error inflating class com.signatact.doorbell.dialog.preference.AppListPreference".
Digging into details I found the reason of the crash as the last step for new object creation for Setting Activity is the cast to androidx.preference.Preference:
from PreferenceInflater.java:
import androidx.preference;
...
return (Preference) constructor.newInstance(args); // line 242
It is clear, the system fails with cast between android.preference.Preference and androidx.preference.Preference.
However, if I move my custom ListPreference file implementation to androidx, almost all method I used before for customization are not available, hereby is a list of methods which are not available, where I put my custom logic:
// Error(s): Methods don't override methods from its superclass
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder)
...
#Override
protected void onDialogClosed(boolean positiveResult)
It looks like Google dramatically changed their API, can anybody give idea how in AndroidX one can customize ListPreference?
In general, I need standard customization things as follows:
In a row I have a custom set of controls (3 ones - 2x text boxes and 1 checkbox) - I build a custom layout for each row in onPrepareDialogBuilder with my custom ArrayAdapter for the list
I need dynamically update the CustomListPreference values. I populate those values in onResume in SettingActivity
I need to get callback when the list is pressed and new value is selected
I found only one practical guidance here for my case which is as follows: How can I change the appearance of ListPreference Dialog but it is limited and short. I analysed the AndroidX API and it looks like I need more time to come out with a solution and thus any help / idea appreciated...
Thx, Vlad.
Simply override onClick() function to pop out an AlertDialog with custom layout. Remember to call setValue() when anything selected in the dialog.
public class ColorPreference extends ListPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
private String mValue;
private String mSummary;
private AlertDialog mDialog;
public ColorPreference(Context context) {
this(context, null);
}
public ColorPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDefaultValue(Options.DEFAULT_PRIMARY_COLOR_STRING);
}
#Override
protected void onClick() {
mEntries = getEntries();
mEntryValues = getEntryValues();
mSummary = getSummary().toString();
mValue = getValue();
mClickedDialogEntryIndex = findIndexOfValue(mValue);
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setSingleChoiceItems(new ColorAdapter(getContext(), R.layout.pref_color_item),mClickedDialogEntryIndex,null);
mDialog = builder.create();
mDialog.show();
}
}

How do I redraw UICollectionView when MvxCollectionViewSource's data changes?

I am using Xamarin and MVVMCross to implement an IOS app. A view I'm working on is displaying correctly, but ONLY when I hard-code the data being bound to inside my ViewModel and NOT when (as by design and necessity) my data arrives late from SQLite, only an indeterminate time after the view is shown.
What I am using and accomplished so far:
Working/Showing storyboard for my View that has a UICollectionView
inside (called: CollectionView in code below)
Custom layout and XIB file for every UICollectionViewCell that also displays correctly in my view
A view that works and displays correctly only if ViewModel data is fully populated the moment ViewDidLoad() is called.
Problem:
My data in my ViewModel is updated by the Model's databases in an uncertain amount of time whilst the view is happily being shown. When I bind the data as shown below (and trying two-way/one-way bindings and the like as well), I don't get updates on my view as the final data comes in later.
What I can't seem to do:
Redraw the UICollectionView or maybe refresh the
MvxCollectionViewSource below to ensure that as the ViewModel's data changes, I can actually redraw the UICollectionView and show my
custom cells with new and updated data.
THE CODE(TM)
The CollectionView cells are implemented as follows. I followed all examples online and from that Stuart Bloke and his Kittens to make sure I implement all the patterns exactly the same:
[Register("MyCell")]
public partial class MyCell : MvxCollectionViewCell
{
public static readonly UINib Nib = UINib.FromName("MyCell", NSBundle.MainBundle);
public static readonly NSString Key = new NSString("MyCell");
public MyCell(IntPtr handle) : base(handle)
{
this.DelayBind(() => {
var set = this.CreateBindingSet<MyCell, SomeModelClass>();
set.Bind(Label1).To(item => item.Label1);
set.Bind(Label2).To(item => item.Label2);
set.Apply();
});
}
public static MyCell Create()
{
return (MyCell)Nib.Instantiate(null, null)[0];
}
}
My ViewDidLoad() in the View looks something like this:
CollectionView.RegisterNibForCell(MyCell.Nib, MyCell.Key);
var source = new MvxCollectionViewSource(CollectionView, MyCell.Key);
CollectionView.Source = source;
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(source).To(vm => vm.ListOfStuff);
set.Apply();
CollectionView.ReloadData();
NB! The ListOfStuff shown above is really just a List of a custom class containing 2 strings right now.
TL:DR: I don't know ListOfStuff's values the moment I call the above code. When I hard-code them in the ViewModel, I get joy. If I don't, I don't, even as data gets updated correctly later.
I now reach out to you, the neurons of the brain of crowdsourcing...
Instead of using a List<T> use ObservableCollection<T> and new items should be added to the CollectionView.
The UI needs to know when the collection has changed. ObservableCollection<T> implements INotifyCollectionChanged and INotifyPropertyChanged and communicates with the UI when the collection changes.
You shouldn't need ReloadData() anymore if you're using ObservableCollection<T>.
This extension method might be of use when adding range of IEnumerable<T>
public static class ObservableCollectionExtensionMethod
{
public static void AddRange<TSource>(this ObservableCollection<TSource> source, IEnumerable<TSource> collection)
{
foreach (var i in collection) source.Add(i);
}
}

How can I edit the default TestNG html report

I don't want to create a new one or a custom listener. Is that possible? Where is the html report created in TestNG?
SuiteHTMLReporter [source] is the reporter creating the html report. You can extend and override. Disable default listeners and add your own.
I know this is old , but these reports can be edited and custom reports can be made like below. I have explained here how TestHTMLReporter can be edited
With your customReport You'd have to implement IReporter , extend TestListenerAdapter and override generateReport method if you want to implement a custom TestHTMLReporter . For other reporters you may have to do things a bit differently but the concept will remain the same. You'd achieve custom 'TestHTMLReporter' like below .
Create a CustomReport.java file in your project and copy-paste the whole content of TestHTMLReporter.java , change the name of file in getOutputFile method and it would look like below
public class CustomReport extends TestListenerAdapter implements IReporter {
#Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,
String outputDirectory) {
}
...
//paste the content of TestHTMLReporter.java here
...
...
Make sure all your imports are in place from TestHTMLReporter.java
Now, in this file change as per your requirement . For ex: if you'd like to add the end time of each of the test then at the correct place in generateTable method add the below snippet
// Test class
String testClass = tr.getTestClass().getName();
long testMillis = tr.getEndMillis();
String testMillisString = Long.toString(testMillis);
if (testClass != null) {
pw.append("<br>").append("Test class Name: ").append(testClass);
// this line to add end time in ms
pw.append("<br>").append("End Time(ms): ").append(testMillisString);
// Test name
String testName = tr.getTestName();
if (testName != null) {
pw.append(" (").append(testName).append(")");
}
Then you'll get like below
Now, You'll get two reports one with default and the other with your file name.
The only thing now remains is switching off the default reporting listeners, so you get only one report. For that you can follow this or you may search for solutions. Hope this helps

Is there a Silverstripe has_many field more basic than HasManyComplexTableField?

I'm modelling a very basic association between courses (CoursePage) and instances of that course (CourseInstance extending from DataObject):
class CoursePage extends Page {
public static $has_many = array('Instances' => 'CourseInstance');
}
class CourseInstance extends DataObject {
public static $has_one = array('Course' => 'CoursePage');
}
What I want in the CMS is a simple way to add course instances to the course page. The HasManyComplexDataField is working but means that all course pages have the same (long!) list of instances shared across all courses. What I want is a way to add / remove / edit just instances relating to that course, without any details of instances from any other CoursePage.
Is there a more simple field more suited to my needs? Alternatively, is there any way I can limit the complex field to only show related instances but also allow me to add new ones?
Setting the sourceFilter parameter of the HasManyComplexDataField to the following works:
'CourseID = ' . $this-ID

Dynamic list constraint on Alfresco

I'm trying to follow the examples provided in this post, to create a dynamic list constraint in Alfresco 3.3.
So, I've created my own class extending ListOfValuesConstraint:
public class MyConstraint extends ListOfValuesConstraint {
private static ServiceRegistry registry;
#Override
public void initialize() {
loadData();
}
#Override
public List getAllowedValues() {
//loadData();
return super.getAllowedValues();
}
#Override
public void setAllowedValues(List allowedValues) {
}
protected void loadData() {
List<String> values = new LinkedList<String>();
String query = "+TYPE:\"cm:category\" +#cm\\:description:\"" + tipo + "\"";
StoreRef storeRef = new StoreRef("workspace://SpacesStore");
ResultSet resultSet = registry.getSearchService().query(storeRef, SearchService.LANGUAGE_LUCENE, query);
// ... values.add(data obtained using searchService and nodeService) ...
if (values.isEmpty()) {
values.add("-");
}
super.setAllowedValues(values);
}
}
ServiceRegistry reference is injected by Spring, and it's working fine. If I only call loadData() from initialize(), it executes the Lucene query, gets the data, and the dropdown displays it correctly. Only that it's not dynamic: data doesn't get refreshed unless I restart the Alfresco server.
getAllowedValues() is called each time the UI has to display a property having this constraint. The idea on the referred post is to call loadData() from getAllowedValues() too, so the values will be actually dynamic. But when I do this, I don't get any data. The Lucene query is the same, but returns 0 results, so my dropdown only displays -.
BTW, the query I'm doing is: +TYPE:"cm:category" +#cm\:description:"something here", and it's the same on each case. It works from initialize, but doesn't from getAllowedValues.
Any ideas on why is this happening, or how can I solve it?
Thanks
Edit: we upgraded to Alfresco 3.3.0g Community yesterday, but we're still having the same issues.
This dynamic-list-of-values-constraint is a bad idea and I tell you why:
The Alfresco repository should be in a valid state all the time. Your (dynamic) list of constraints will change (that's why you want it to be dynamic). Adding items would not be a problem, but editing and removing items are. If you would remove an item from your option-list, the nodes in the repository with this property value will be invalid.
You will not be able to fix this easily. The standard UI will fail on invalid-state-nodes. Simply editing this value and setting it to something valid will not work. You have been warned.
Because the default UI widget for a ListConstraint is a dropdown, not every dropdown should be a ListConstraint. ListConstraints are designed for something like a Status property: { Draft, Waiting Approval, Approved }. Not for a list of customer-names.
I have seen this same topic come up again and again over the last few years. What you actually want is let the user choose a value from a dynamic list of options (combo box). This is a UI problem, not a dictionary-model-issue. You should setup something like this with the web-config-context.xml (Alfresco web UI) or in Alfresco Share. The last one is more flexible and I would recommend taking that path.

Resources