How to show a module that is used in many tabs in DotNetNuke - dotnetnuke

We are new to DNN and we plan to add a product module that is in charge of adding, editing, deleting, listing, and showing the details of the products.
We have written a UserControl named ProductsList.ascx, which has AddProducts.ascx and ShowPrdoctDetail.ascx defined in it, using Host => Extensions => ProductsList => Module Definition => Add Module Control.
In admin mode,we have created a page and dragged the module in it, so that the admin of the site can add, edit, delete, and see the details of each product.
Also there is a slideshow in the homepage that shows the latest products.In addition, the products are shown in the menu.
Now, we want to redirect user to the product detail page (ShowPrdoctDetail.ascx in our case) whenever he/she clicked the product shown in slideshow or in menu.
We are aware of Globals.NavigateUrl() method, but it needs tabid and mid to redirect to a specific page and module and in DNN every added page by admin will get different tabid and mid.
Since in DNN, admin can create many pages and add this module to them, we have no idea that what tabid and mid we should pass to Globals.NavigateUrl() in order to navigate user to product details page (ShowPrdoctDetail.ascx) when user clicked on a specific product in menu or slideshow.
Any kind of help is highly appreciated.

Try save current tabid to DB when adding product detail module into page. And with ProductId, you can grab tabid of product detail, and use it to redirect to correct page.

The way I would tackle this is to create another Module Definition for the details module and give it a Friendly name like "Product Details" and add the ShowProductDetail.ascx module control as the default view of this new module definition.
Then you can drag that new module onto a page for your product details page.
In your main Product Admin module, you can create a setting view with a dropdown list that contains a list of all tabs (pages) that the "Product Details" module on.
You can use the following method to get the list of tabs in the portal that has an instance of the module:
private List<TabInfo> GetAllModuleTabsbyModuleName(string friendlyName)
{
List<TabInfo> results = new List<TabInfo>();
Dictionary<int, int> dups = new Dictionary<int, int>();
ModuleController mc = new ModuleController();
ArrayList oModules = mc.GetModulesByDefinition(base.PortalId, friendlyName);
TabController tc = new TabController();
TabCollection oTabs = tc.GetTabsByPortal(base.PortalId);
foreach (ModuleInfo oModule in oModules)
{
foreach (KeyValuePair<int, TabInfo> oTab in oTabs)
{
if (oTab.Key == oModule.TabID && !dups.ContainsKey(oModule.TabID))
{
results.Add(oTab.Value);
dups.Add(oModule.TabID, oModule.TabID);
}
}
}
return results;
}
You can bind that to the dropdown list options and an administrator could select the page that will be redirected when a product is clicked on the main module.
ddlProdDetailsTab.DataSource = GetAllModuleTabsbyModuleName("Product Details");
ddlProdDetailsTab.DataValueField = "TabID";
ddlProdDetailsTab.DataTextField = "TabName";
ddlProdDetailsTab.DataBind();
So from the settings, you know the TabId you want to redirect to, then you need the moduleId and you can create the redirect using NavigateUrl().
var pdTab = TabController.Instance.GetTab(Convert.ToInt32(Settings["ProductDetailTabId"]), PortalId);
var pdModule = pdTab.Modules.Cast<ModuleInfo>().FirstOrDefault(m => m.ModuleName == "Product Details");
var productLink = Globals.NavigateURL(pdTab.TabId, "", "mid=" + pdModule.ModuleId, "productId=" + productId);

Related

Adding multiple owners for Google App Maker records

I need help with Google App Maker data model security. I want to set multiple owners for a single record. Like the current user + the assigned admin + super admin.
I need this because all records can have different owners and super-owners/admins.
I know that we can point google app maker to a field containing record owner's email and we can set that field to the current user at the time of the creation of the record.
record.Owner = Session.getActiveUser().getEmail();
I want to know if it is possible to have field owners or have multiple fields like owner1, owner2 and then assign access levels to owner1, owner2...
Or how can we programmatically control the access/security/permissions of records?
The solution I'd use for this one definitely involves a field on the record that contains a comma separated string of all the users who should have access to it. I've worked on the following example to explain better what I have in mind.
I created a model and is called documents and looks like this:
In a page, I have a table and a button to add new document records. The page looks like this:
When I click on the Add Document button, a dialog pops up and looks like this:
The logic on the SUBMIT button on the form above is the following:
widget.datasource.item.owners = app.user.email;
widget.datasource.createItem(function(){
app.closeDialog();
});
That will automatically assign the creator of the record the ownership. To add additional owners, I do it on an edit form. The edit form popus up when I click the edit button inside the record row. It looks like this:
As you can see, I'm using a list widget to control who the owners are. For that, it is necessary to use a <List>String custom property in the edit dialog and that will be the datasource of the list widget. In this case, I've called it owners. I've applied the following to the onClick event of the edit button:
var owners = widget.datasource.item.owners;
owners = owners ? owners.split(",") : [];
app.pageFragments.documentEdit.properties.owners = owners;
app.showDialog(app.pageFragments.documentEdit);
The add button above the list widget has the following logic for the onClick event handler:
widget.root.properties.owners.push("");
The TextBox widget inside the row of the list widget has the following logic for the onValueEdit event handler:
widget.root.properties.owners[widget.parent.childIndex] = newValue;
And the CLOSE button has the following logic for the onClick event handler:
var owners = widget.root.properties.owners || [];
if(owners && owners.length){
owners = owners.filter(function(owner){
return owner != false; //jshint ignore:line
});
}
widget.datasource.item.owners = owners.join();
app.closeDialog();
Since I want to create a logic that will load records only for authorized users, then I had to use a query script in the datasource that will serve that purpose. For that I created this function on a server script:
function getAuthorizedRecords(){
var authorized = [];
var userRoles = app.getActiveUserRoles();
var allRecs = app.models.documents.newQuery().run();
if(userRoles.indexOf(app.roles.Admins) > -1){
return allRecs;
} else {
for(var r=0; r<allRecs.length; r++){
var rec = allRecs[r];
if(rec.owners && rec.owners.indexOf(Session.getActiveUser().getEmail()) > -1){
authorized.push(rec);
}
}
return authorized;
}
}
And then on the documents datasource, I added the following to the query script:
return getAuthorizedRecords();
This solution will load all records for admin users, but for non-admin users, it will only load records where their email is located in the owners field of the record. This is the most elegant solution I could come up with and I hope it serves your purpose.
References:
https://developers.google.com/appmaker/models/datasources#query_script
https://developers.google.com/appmaker/ui/binding#custom_properties
https://developers.google.com/appmaker/ui/logic#events
https://developers-dot-devsite-v2-prod.appspot.com/appmaker/scripting/api/client#Record

How do I get a LazyComboBox to select an item from another page in Vaadin?

I am using Vaadin 7.5.5 and Viritin 1.35. I have implemented a LazyComboBox that is backed by a Spring Data JPA service call and repository.
My basic setup for the LazyComboBox is:
initList(
Site.class,
new FilterablePagingProvider() {
#Override
public List findEntities(int index, String name) {
return sitesService.findByName(name);
}
},
new FilterableCountProvider() {
#Override
public int size(String name) {
return sitesService.countByName(name);
}
},
PAGE_SIZE
);
This works great when I am typing in the combo box.
If I pick an item from the first page (page 0) I can then leave the combo box, come back to it, and click the drop down arrow again and the previously selected item is still selected.
However, if I choose an item from any page other than 0, then clicking the drop down arrow causes the selected item to become null and the user has to find the original item again.
I can post the details of the service and repository if needed but it isn't anything more complicated than a select * from table where name like '%' + name + '%'.
How do I keep the selected value in the combo box no matter what page was chosen?
Thank you.
I think you should call setNullSelectionAllowed(false); on this combo box.
Also, a solution to keep selected value after navigation can be to store it in a session value, then you'll be able to retreive it wherever you are. See Session wiki page

Display module form on admin pages. Drupal 7. Commerce.

I've created a small module that I would like to link account notes with users.
I have written the beginnings of the module that includes a form for adding notes and date created. This works when I access mydomain.com/admin/user_notes.
My question is, how do I get this form to display in the admin section of the site on a users orders history page. eg mydomain.com/users/1245/order-history
I would like our admins who have a specific role to be able to add notes when they view a users order history page.
Thanks in advance for any advice.
you could use a block, create one with the hook_block_info and hook_block_view function.
Like this:
function tips_block_info() {
$block['yourBlockName'] = array(
'info' => t('This my created block'),
'cache' => DRUPAL_NO_CACHE, // Disable caching if you need/want to
);
return $block;
}
Thisby will create an empty block, to fill it with content use hook_block_view:
function moduleName_block_view($delta = '') {
$block = array();
switch($delta) {
// The delta of your block will be the key from the $block array we set in hook_block_info
case 'yourBlockName':
// Set the block title
$block['subject'] = 'Hey I\'m your block title';
$block['content'] = 'Block content goes here can also be the output of any function';
break;
}
return $block;
}
Don't forget to set access permission for your block if you need any, you can do that by editing the block on the block admin page.
Relevant Drupal API links:
hook_block_info
hook_block_view

Ideas on how to implement CMS for language specific page

I'm building a web shop and I'm implementing language selection as well as a CMS. I also have to provide an administrator of the site with the means to be able to edit a page through the CMS.
Therein doesn't lay the problem.
The problem is how I should build up my tables for these pages. I've made my database design but didn't think the web pages part through.
I already have the following table structure for the info that is equal throughout all languages for a page (called Webpages) and for language or culture specific info (called Webpages_local).
Which attributes could I add or remove so that I can easily and dynamically perform the CRUD actions?
I'm using MVC4 with razor syntax with the following url structure:
url: "{language}/{controller}/{action}/{id}"
My main concern is now that I'm not sure on how to show the language specific content of a page when a visitor presses, for example, the link to the About page.
Maybe use the controller part of the url and save it as a key in my Webpages table and filter on that as well as the selected language?
So when a visitor goes to http://example.com/nl/About, I in my AboutController I retrieve "nl" and "about", of course filter them first and then with a query to the database select the correct nl content?
How should I go about this technically?
I would use OnActionExecuting to handle the retrieve lang process, something like:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
#region set Language
var lang = string.Empty;
if (filterContext.RouteData.Values["lang"] != null && !string.IsNullOrWhiteSpace(filterContext.RouteData.Values["lang"].ToString()))
{
// set the culture from the route data (url {lang})
lang = filterContext.RouteData.Values["lang"].ToString();
switch (lang)
{
case "es":
break;
case "en":
break;
default:
lang = "es";//default language
filterContext.RouteData.Values["lang"] = lang;
filterContext.HttpContext.Response.Redirect("/");
break;
}
}
else
{
//set default language
lang = "es";
filterContext.RouteData.Values["lang"] = lang;
}
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(lang);
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(lang);
#endregion
base.OnActionExecuting(filterContext);
}
Then anywhere in your code you just read the Thread.CurrentThread.CurrentCulture (this will be your global lang indicator) to show the correct language.
UPDATE - know I understand your point =)
If the pages will be dynamically created then you wont have an ActionResult per page, you need just one ActionResult like:
public ActionResult ShowPage(int id,string slug)
{
//Use the slug to check for url attacks and ensure 301 redirections to the correct url
var page = db.Webpages_local.First(p=> p.id == id
&& p.culture.name == Thread.CurrentThread.CurrentCulture);
return View(page);
}
For SEO reasons i would suggest you define a route like:
routes.MapRoute(
name: "LocalizedPages",
url: "{lang}/p/{slug}/{id}",
defaults: new { controller = "Page", action = "Show", id = UrlParameter.Optional },
constraints: new { lang = #"(es|en|fr|nl)" }
);
That give you urls like:
/nl/p/about/1 //the p is just an identifier for 'page', to differentiate this routes from others
I would add a column called language or similar to your table instead of having multiple tables.
Then your controller can fetch the right page after finding the requested language key in your menu table and then it can read the content in your webpages table.

Drupal 7 load profile2 programmatically

I have two profile2 profiles defined - main and customer_profile. Also, I have a node type called Customer.
When creating a new Customer node, I would like to load the custom_profile form. The idea is to create a node and a profile simultaneously.
I know it's definately a hook_form_alter solution but can someone tell me how to programmatically load a profile while creating or editing a Customer node.
You can load profile type and data by using these function
$types = profile2_get_types();
profile2_load_by_user($account, $type_name = NULL)
For Example :
$types = profile2_get_types();
if (!empty($types)) {
foreach ($types as $type) {
$profile = profile2_load_by_user($uid, $type->type);
}
}
Even if you are able to load the customer_profile form, you will need to handle the values separately as they are two different nodes.
I would suggest capturing those fields in the customer node form, and then create a customer_profile programmatically from the values.
If you want to get the profile2 form itself then you can use something like
module_load_include('inc', 'profile2_page', 'profile2_page');
$profile2 = profile2_by_uid_load($uid, 'seeker_profile');
$entity_form = entity_ui_get_form('profile2', $profile2, 'edit');
and then add that to the form you want to place it in.
You can load full profile data using profile2_load_by_user();
params like:-
profile2_load_by_user($account,$type_name)
$account: The user account to load profiles for, or its uid.
$type_name: To load a single profile, pass the type name of the profile to load
So code like bellow
$account->uid = $existingUser->uid;
$type_name = 'user_about';
$profile = profile2_load_by_user($account, $type_name);
//$profile variable have full data of profile fields
//changing data profile2 fields
if(isset($_POST['field_user_first_name'])&& !empty($_POST['field_user_first_name'])){
$profile->field_user_first_name['und'][0]['value'] = $_POST['field_user_first_name'];
}
profile2_save($profile);
Well When creating a new Profile , Profile2 fields are not visible until a manual save is done.
To Automatically create the profile2 object , We use rules Module
Step
1) Go to Drupal admin/config/workflow/rules
2) create new rule
3) Give a name and Select in react/event "After saving a new user account"
4) Action,>> Add Action >> Execute custom PHP code
5) insert php code
$profile = profile_create(array('type' => 'profile2 type machine name', 'uid' => $account->uid));
profile2_save($profile);
6)Save >> Save changes.
This will create profile2 field when a new user is Created.
I had a similar need for creating a custom tab at the user page and loading the user profile2 form in it.
Here is a snap code of how I managed to accomplish just that:
MYMODULE.module https://gist.github.com/4223234
MYMODULE_profile2_MYPROFILE2TYPE.inc https://gist.github.com/4223201
Hope it helps.

Resources