How to add new admin menu items without reinstalling component in joomla? - menu

I have been developing a component for Joomla 1.7.x, during development I need to add new component menu items to admin menu, it was easy by adding new rows to components table in DB in Joomla 1.5 times, but now it seems complicated to add a menu item by adding new row to menu table because of the database structure changes in Joomla 1.7
Is there a easy way to do this without reinstalling the component?
tHanks

The easiest way I found:
$table = JTable::getInstance('menu');
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = 'ITEM TITLE';
$data['alias'] = 'com-component-name';
$data['link'] = 'index.php?option=com_component_name&view=default';
$data['type'] = 'component';
$data['published'] = '0';
$data['parent_id'] = '117'; // ID, under which you want to add an item
$data['component_id'] = '10026'; // ID of the component
$data['img'] = 'components/com_component_name/assets/images/icon.png';
$data['home'] = 0;
if (
!$table->setLocation(117, 'last-child') // Parent ID, Position of an item
|| !$table->bind($data)
|| !$table->check()
|| !$table->store()
){
// Install failed, warn user and rollback changes
JError::raiseWarning(1, $table->getError());
return false;
}
To delete:
$table->delete(120); // item ID
$table->rebuild();
Based on http://docs.joomla.org/Using_nested_sets#Adding_a_new_node

Joomla 1.6+ menu items are stored under the #__menu table, with a special menu type called "main" for the admin menu.
Find your main component admin menu item's ID. You can add sub-items of that by declaring the parent_id column as the id of your main menu item, and setting the level column to 2.
The only other issue you are going to run into is the adoption of nested sets (lft and rgt columns). This is better way to handle parent-child relationships and the ordering of menu items. I'm unsure whether or not the parent_id or lft/rgt are used at this stage, but they are both filled in.
To add a new item, you would have to "shunt" all lft/rgt values by two for menu items with a value greater or equal to the position you wish to add your menu item. This should include the rgt of your parent item. If your parent item has no children, the lft for your new item will be the value of parent's left + 1. The value of the new item's rgt will be parent's lft + 2.
One thing to note with the lft and rgt is that the numbering applies to every menu item (front-end and back-end), so not doing it properly may have the potential to break your entire menu heirarchy. I think this is why the parent_id column is still used, and that there is an option in the admin area to "rebuild" the menu.
http://en.wikipedia.org/wiki/Nested_set_model

Here's a few SQL queries I came up with that did the trick (only the relevant parts shown):
SET #lastRgt := (SELECT rgt + 1 FROM #__menu WHERE alias="alias-of-preceding-menu-item");
UPDATE #__menu SET rgt=rgt+2 WHERE rgt > #lastRgt;
UPDATE #__menu SET lft=lft+2 WHERE lft > #lastRgt;
INSERT INTO #__menu (menutype, title, alias, path, link, type, published, parent_id, level, component_id, img, client_id, params, access, lft, rgt)
VALUES(..., #lastRgt+1, #lastRgt+2);
Worked for me on Joomla 2.5.

Admit's answer is in need of an update for Joomla 3.x
I'm sure it's correct for older Joomla versions which is why I'm not editing it.
This worked for me after further research and editing.
$table = JTableNested::getInstance('Menu');
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = 'ITEM TITLE';
$data['alias'] = 'com-component-name';
$data['link'] = 'index.php?option=com_component_name&view=default';
$data['type'] = 'component';
$data['published'] = '0';
$data['parent_id'] = '117'; // ID, under which you want to add an item
$data['component_id'] = '10026'; // ID of the component
$data['img'] = 'components/com_component_name/assets/images/icon.png';
$data['home'] = 0;
$table->setLocation(117, 'last-child') // Parent ID, Position of an item
if (!$table->bind($data) || !$table->check() || !$table->store()) {
// Install failed, warn user and rollback changes
JError::raiseWarning(1, $table->getError());
return false;
}

Related

Unable to hide element category in a view in Revit file

I want to hide certain elements in the view.
I managed to hid (with view..HideCategoryTemporary) all the elements I wanted except the marked one in the picture attached.
3D_House_before_hide
Element snoop
This element is a building section of category OST_Viewers.
Manually hiding the element category via the view works, but fetching all OST_Viewers in the code and hiding them does not work.
The following code contain the building section elements in addition to the grids,
FilteredElementCollector viewers_sections = new FilteredElementCollector(doc, v_id).OfCategory(BuiltInCategory.OST_Viewers);
FilteredElementCollector grids = new FilteredElementCollector(doc, v_id).OfCategory(BuiltInCategory.OST_Grids);
FilteredElementCollector elements_to_be_hidden = new FilteredElementCollector(doc, v_id);
elements_to_be_hidden.UnionWith(viewers_sections).UnionWith(grids)
foreach (Element e in elements_to_be_hidden)
{
cur_view.HideCategoryTemporary(e.Category.Id);
}
I've checked that viewers_sections contains the mentioned building sections however it is not hidden from the view.
After hide
How do I hide these building sections?
Please use View#SetCategoryHidden instead to turn off the visibility of the category, the result of the View#HideCategoryTemporary will be reset after closing the file. Here is the working example:
var gridCate = this.Document.Settings.Categories.get_Item(BuiltInCategory.OST_Grids);
var sectionsCate = this.Document.Settings.Categories.get_Item(BuiltInCategory.OST_Sections);
using(var trans = new Transaction(this.Document))
{
trans.Start("Hide Grids & Secions");
this.ActiveView.SetCategoryHidden(gridCate.Id, true);
this.ActiveView.SetCategoryHidden(sectionsCate.Id, true);
trans.Commit();
}

How can I detect uiview is an activated viewport

I need to detect whether a Uiview is a standard opened view or if it is an activated viewport on a sheet. Querying the uiview’s view Id returns the Id of the activated viewport's view. I have found no direct way to detect that a uiview is actually a sheet with an activated viewport.
I am already tracking opened views in the view activated event for another purpose. So I considered storing the view Id with the uiview hashcode for later checking that it was indeed a sheetview prior to becoming an activated view. Unfortunately, and I think in opposition to standard use, the uiview hashcode is not stable. Multiple hashcode requests from the uiview object return different values.
Does anyone have a way to detect this condition? I need to be able to use the the methods on the uiview still. So any help to find the actual child windows I would like to relate to the uiview object. The view still says "Sheet: ..." in the title when a view is activated.
TaskDialog mainDialog = new TaskDialog("Hello, viewport check!");
mainDialog.MainInstruction = "Hello, viewport check!";
mainDialog.MainContent =
"Sadly Revit API doesn't automatically know if the user is in an active viewport. "
+ "Please click 'Yes' if your are, or 'No' if your not.";
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1,
"Yes, I am in an active viewport on a sheet.");
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink2,
"No, I am just in an ordinary view.");
mainDialog.CommonButtons = TaskDialogCommonButtons.Close;
mainDialog.DefaultButton = TaskDialogResult.Close;
TaskDialogResult tResult = mainDialog.Show();
bool YesOrNo = true;
if (TaskDialogResult.CommandLink1 == tResult)
{
YesOrNo = true;
}
else if (TaskDialogResult.CommandLink2 == tResult)
{
YesOrNo = false;
}
else{
return;
}
You can use the ViewSheet GetAllViewports method to determine all the viewports on a given sheet. Using that, you could put together a bi-directional dictionary lookup system map any sheet to all the viewports it hosts and vice versa. That should help solve your task. Here is some example usage:
http://thebuildingcoder.typepad.com/blog/2014/04/determining-the-size-and-location-of-viewports-on-a-sheet.html
Im late to the party - but another way to sense if the user is in a viewport is to investigate the Process.MainWindow title. Something like this (in RevitPythonShell):
import threading, clr
from System.Diagnostics import Process
# need winform libraries for feedback form only
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form, Label
app = __revit__.Application
doc = __revit__.ActiveUIDocument.Document
ui = __revit__.ActiveUIDocument
def lookAtWindow(activeView):
# Looking for one of three conditions:
# 1. User is on a sheet (ActiveView will be DrawingSheet)
# 2. User is in an active ViewPort on a sheet (ActiveView will NOT be be DrawingSheet, but MainWindowTitle will contain " - [Sheet: " string)
# 3. User is on a View (neither of the previous two conditions)
result = False
if str(activeView.ViewType) == 'DrawingSheet':
result = 'Youre on a sheet'
else:
processes = list(Process.GetProcesses())
for process in processes:
window = process.MainWindowTitle
if window and 'Autodesk Revit '+app.VersionName[-4:] in window and ' - [Sheet: ' in window and ' - '+doc.Title+']' in window:
result = 'I reckon youre in a Viewport'
if not result:
result = 'so you must be in a '+str(activeView.ViewType)
form = Form()
form.Width = 300
form.Height = 100
label = Label()
label.Width = 280
label.Height = 70
label.Text = result
label.Parent = form
form.ShowDialog()
# need to close RevitPythonShell console before checking MainWindowTitle, so run on timer
threading.Timer(1, lookAtWindow, [ui.ActiveView]).start()
__window__.Close()

Removing menu in MFC

In MFC how to remove a menu-item of POPUP type. RemoveMenu() either take ID or position. Since, there is no ID for POPUP menu, option left is by using position.
But I am not getting how and where I can call RemoveMenu().
File Edit Test
Test_submenu_1
Test_submenu_2
Test_submenu_3 > submenu_3_item_1
Test_submenu_4
Test_submenu_5
I want to remove Test_submenu_3? I am not getting how do find CMenu object for Test so that I can call RemoveMenu() by passing position "2" for submenu_3_item_1.
Any suggestion for doing this will be greatly appreciated.
Thanks!
You cannot use LoadMenu, since this function does just that.
After modifying loaded menu it is killed when menu object used to load it goes out of scope. You have to modify menu that is currently used.
Your menu is a popup part of the main menu, second in position. It contains 5 items and second one is another popup. To my understanding, you want to remove this item and popup of this item.
To make it work you will have to ask main window for the current menu:
CMenu* pMenu = GetMenu(); // get the main menu
CMenu* pPopupMenu = pMenu->GetSubMenu(2);//(Test menu with item....)
pPopupMenu->RemoveMenu(2, MF_BYPOSITION);
Of course, this code is from the main frame. If you want to use it elsewhere, you will have to access all using pointer to the main frame.
Try the below. You get the Test sub-menu first (index 2), then once you have that you tell it to remove its Test_submenu_3 submenu by position (also 2).
CMenu topMenu;
topMenu.LoadMenu(IDR_YOUR_MENU);
CMenu& testSubMenu = *topMenu.GetSubMenu(2);
testSubMenu.RemoveMenu(2,MF_BYPOSITION);
'Test' is the 3rd menu item (by position) on the top level menu. It's just been rendered horizontally rather than vertically. Assuming you have a handle to the top level menu use the same code you'd use to the get the sub menus as you would to get the 'Test' menu.
You can use this below code to remove the submenu, by comparing name
bool RemoveSubmenu(CMenu * pMenu) {
for (int pos = 0; pos < pMenu->GetMenuItemCount(); pos++) {
wchar_t *name = new wchar_t[mf.cch + 1];
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf));
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
mf.fType = MIIM_STRING;
mf.dwTypeData = NULL;
if (!GetMenuItemInfo(pMenu->m_hMenu, pos, TRUE, &mf))
break;
if (mf.hSubMenu != NULL){
mf.fMask = MIIM_TYPE;
mf.fType = MFT_STRING;
++mf.cch;
mf.dwTypeData = (LPSTR)name;
if (!GetMenuItemInfo(pMenu->m_hMenu, pos, TRUE, &mf)){
bRet = false;
break;
}
//
// compare sub menu name (i.e mf.dwTypeData) here, do the required
// modifications
//
pMenu->RemoveMenu(pos, MF_BYPOSITION);
bRet = true;
break;
}
}
}

C# TableLayoutPanel replace control?

I was wondering if it was possible to replace one control in a TableLayoutPanel with another at runtime. I have a combo box and a button which are dynamically added to the TableLayoutPanel at runtime, and when the user selects an item in the combo box and hits the button, I'd like to replace the combobox with a label containing the text of the selected combo box item.
Basically, if I could simply remove the control and insert another at it's index, that would work for me. However I don't see an option like "splice" or "insert" on the Controls collection of the TableLayoutPanel, and I was wondering if there was a simple way to insert a control at a specific index. Thanks in advance.
Fixed this by populating a panel with the two controls I wanted to swap and putting that into the TableLayoutPanel. Then I set their visibility according to which I wanted to see at what time.
This is what I've been able to come up with for what I needed. It gets the position of the ComboBox and makes a new label using the selected value.
// Replaces a drop down menu with a label of the same value
private void lockDropMenu(ComboBox dropControl)
{
TableLayoutPanelCellPosition pos = myTable.GetCellPosition(dropControl);
Label lblValue = new Label();
myTable.Controls.Remove(dropControl);
if (dropControl.SelectedItem != null)
{
lblValue.Text = dropControl.SelectedItem.ToString();
lblValue.Font = lblValue.Font = dropControl.Font;
// Just my preferred formatting
lblValue.AutoSize = true;
lblValue.Dock = System.Windows.Forms.DockStyle.Fill;
lblValue.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
myTable.Controls.Add(lblValue, pos.Column, pos.Row);
}
}

Grouping custom tool parts in with other custom properties

I have a webpart. I have a list of items (schools) that I must pick from. This list comes from another Sharepoint list. I made a tool part. This tool part creates a drop down list and populates it with the correct data. When I went to edit the web part properties, the drop down list is sitting outside of any other group. I want to add that drop down list into the "Miscellaneous" group. If I can't put it there, I want to create a custom group.
How do I do this?
To add other groups, you will need some client manipulations (jquery).
Or you can just create extra groups:
protected override void CreateChildControls() {
Panel panel = new Panel();
panel.CssClass = "ms-ToolPartSpacing";
Table table = new Table();
table.CellPadding = 0;
table.CellSpacing = 0;
table.Style["border-collapse"] = "collapse";
table.Attributes.Add("width", "100%");
TableRow row = new TableRow();
TableCell cell = new TableCell();
cell.Controls.Add(new LiteralControl("<div class=\"UserSectionHead\"><b>Your Other Group:</b></div>"));
cell.Controls.Add(new LiteralControl("<div class=\"UserSectionBody\"><div class=\"UserControlGroup\">"));
Table innertable = new Table();
//build your innertable
cell.Controls.Add(innertable);
cell.Controls.Add(new LiteralControl("</div></div>"));
cell.Controls.Add(new LiteralControl("<div style='width:100%' class='UserDottedLine'></div>"));
row.Cells.Add(cell);
table.Rows.Add(row);
panel.Controls.Add(table);
this.Controls.Add(panel);
base.CreateChildControls();
}

Resources