diff options
-rw-r--r-- | indra/llinventory/llsettingsdaycycle.cpp | 12 | ||||
-rw-r--r-- | indra/llinventory/llsettingsdaycycle.h | 1 | ||||
-rw-r--r-- | indra/newview/llenvironment.cpp | 146 | ||||
-rw-r--r-- | indra/newview/llenvironment.h | 13 | ||||
-rw-r--r-- | indra/newview/llfloatereditextdaycycle.cpp | 36 | ||||
-rw-r--r-- | indra/newview/llfloaterfixedenvironment.cpp | 39 | ||||
-rw-r--r-- | indra/newview/llinventorybridge.cpp | 48 | ||||
-rw-r--r-- | indra/newview/llinventorybridge.h | 8 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 27 |
9 files changed, 285 insertions, 45 deletions
diff --git a/indra/llinventory/llsettingsdaycycle.cpp b/indra/llinventory/llsettingsdaycycle.cpp index e67da95a6c..60e962b612 100644 --- a/indra/llinventory/llsettingsdaycycle.cpp +++ b/indra/llinventory/llsettingsdaycycle.cpp @@ -614,6 +614,18 @@ LLSettingsBase::ptr_t LLSettingsDay::getSettingsAtKeyframe(F32 keyframe, S32 tra return LLSettingsBase::ptr_t(); } +void LLSettingsDay::clearTrack(S32 track) +{ + if ((track < 0) || (track >= TRACK_MAX)) + { + LL_WARNS("DAYCYCLE") << "Attempt to clear track (#" << track << ") out of range!" << LL_ENDL; + return; + } + + mDayTracks[track].clear(); +} + + F32 LLSettingsDay::getUpperBoundFrame(S32 track, F32 keyframe) { return get_wrapping_atafter(mDayTracks[track], keyframe)->first; diff --git a/indra/llinventory/llsettingsdaycycle.h b/indra/llinventory/llsettingsdaycycle.h index 101cacc3a2..6fb48225c7 100644 --- a/indra/llinventory/llsettingsdaycycle.h +++ b/indra/llinventory/llsettingsdaycycle.h @@ -98,6 +98,7 @@ public: LLSettingsSkyPtr_t getSkyAtKeyframe(F32 keyframe, S32 track) const; void setSettingsAtKeyframe(const LLSettingsBase::ptr_t &settings, F32 keyframe, S32 track); LLSettingsBase::ptr_t getSettingsAtKeyframe(F32 keyframe, S32 track) const; + void clearTrack(S32 track); //--------------------------------------------------------------------- void startDayCycle(); diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 228a396c0d..21c38302d1 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -318,6 +318,27 @@ bool LLEnvironment::canEdit() const return true; } +bool LLEnvironment::canAgentUpdateParcelEnvironment(bool useselected) const +{ + if (!LLEnvironment::instance().isExtendedEnvironmentEnabled()) + return false; + // *TODO* + //LLParcel* parcel = (useselected) ? LLViewerParcelMgr::instance().getParcelSelection() : LLViewerParcelMgr::instance().getAgentParcel(); + LLParcel* parcel = LLViewerParcelMgr::instance().getAgentParcel(); + if (parcel) + { + return parcel->allowTerraformBy(gAgent.getID()); + } + + return false; +} + +bool LLEnvironment::canAgentUpdateRegionEnvironment() const +{ + return gAgent.getRegion()->canManageEstate(); +} + + bool LLEnvironment::isExtendedEnvironmentEnabled() const { return !gAgent.getRegionCapability("ExtEnvironment").empty(); @@ -1011,7 +1032,6 @@ void LLEnvironment::recordEnvironment(S32 parcel_id, LLEnvironment::EnvironmentI setEnvironment(ENV_PARCEL, pday, envinfo->mDayLength, envinfo->mDayOffset); } - /*TODO: track_altitudes*/ updateEnvironment(); } @@ -1038,6 +1058,29 @@ void LLEnvironment::updateRegion(LLSettingsDay::ptr_t &pday, S32 day_length, S32 updateParcel(INVALID_PARCEL_ID, pday, day_length, day_offset); } +void LLEnvironment::updateRegion(const LLUUID &asset_id, S32 day_length, S32 day_offset) +{ + if (!isExtendedEnvironmentEnabled()) + { + LL_WARNS("ENVIRONMENT") << "attempt to apply asset id to region not supporting it." << LL_ENDL; + LLNotificationsUtil::add("NoEnvironmentSettings"); + return; + } + + updateParcel(INVALID_PARCEL_ID, asset_id, day_length, day_offset); +} + +void LLEnvironment::updateRegion(LLSettingsSky::ptr_t &psky, S32 day_length, S32 day_offset) +{ + updateParcel(INVALID_PARCEL_ID, psky, day_length, day_offset); +} + +void LLEnvironment::updateRegion(LLSettingsWater::ptr_t &pwater, S32 day_length, S32 day_offset) +{ + updateParcel(INVALID_PARCEL_ID, pwater, day_length, day_offset); +} + + void LLEnvironment::resetRegion() { resetParcel(INVALID_PARCEL_ID); @@ -1051,6 +1094,52 @@ void LLEnvironment::requestParcel(S32 parcel_id) [this](S32 pid, EnvironmentInfo::ptr_t envinfo) { recordEnvironment(pid, envinfo); })); } +void LLEnvironment::updateParcel(S32 parcel_id, const LLUUID &asset_id, S32 day_length, S32 day_offset) +{ + LLSettingsVOBase::getSettingsAsset(asset_id, + [this, parcel_id, day_length, day_offset](LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, LLExtStat) { onUpdateParcelAssetLoaded(asset_id, settings, status, parcel_id, day_length, day_offset); }); +} + +void LLEnvironment::onUpdateParcelAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, S32 parcel_id, S32 day_length, S32 day_offset) +{ + if (status) + { + LL_WARNS("ENVIRONMENT") << "Unable to get settings asset with id " << asset_id << "!" << LL_ENDL; + LLNotificationsUtil::add("FailedToLoadSettingsApply"); + return; + } + + LLSettingsDay::ptr_t pday; + + if (settings->getSettingType() == "daycycle") + pday = std::static_pointer_cast<LLSettingsDay>(settings); + else + { + pday = createDayCycleFromEnvironment( (parcel_id == INVALID_PARCEL_ID) ? ENV_REGION : ENV_PARCEL, settings); + } + + if (!pday) + { + LL_WARNS("ENVIRONMENT") << "Unable to construct day around " << asset_id << "!" << LL_ENDL; + LLNotificationsUtil::add("FailedToBuildSettingsDay"); + return; + } + + updateParcel(parcel_id, pday, day_length, day_offset); +} + +void LLEnvironment::updateParcel(S32 parcel_id, LLSettingsSky::ptr_t &psky, S32 day_length, S32 day_offset) +{ + LLSettingsDay::ptr_t pday = createDayCycleFromEnvironment((parcel_id == INVALID_PARCEL_ID) ? ENV_REGION : ENV_PARCEL, psky); + updateParcel(parcel_id, pday, day_length, day_offset); +} + +void LLEnvironment::updateParcel(S32 parcel_id, LLSettingsWater::ptr_t &pwater, S32 day_length, S32 day_offset) +{ + LLSettingsDay::ptr_t pday = createDayCycleFromEnvironment((parcel_id == INVALID_PARCEL_ID) ? ENV_REGION : ENV_PARCEL, pwater); + updateParcel(parcel_id, pday, day_length, day_offset); +} + void LLEnvironment::updateParcel(S32 parcel_id, LLSettingsDay::ptr_t &pday, S32 day_length, S32 day_offset) { std::string coroname = @@ -1136,6 +1225,20 @@ void LLEnvironment::coroUpdateEnvironment(S32 parcel_id, LLSettingsDay::ptr_t pd if (url.empty()) return; + if (day_length < 1) + { + day_length = getEnvironmentDayLength((parcel_id == INVALID_PARCEL_ID) ? ENV_REGION : ENV_PARCEL).value(); + if ((day_length < 1) && (parcel_id != INVALID_PARCEL_ID)) + day_length = getEnvironmentDayLength(ENV_REGION).value(); + } + + if (day_offset < 1) + { + day_offset = getEnvironmentDayOffset((parcel_id == INVALID_PARCEL_ID) ? ENV_REGION : ENV_PARCEL).value(); + if ((day_offset < 1) && (parcel_id != INVALID_PARCEL_ID)) + day_offset = getEnvironmentDayOffset(ENV_REGION).value(); + } + LLSD body(LLSD::emptyMap()); body["environment"] = LLSD::emptyMap(); @@ -1366,6 +1469,47 @@ LLSettingsDay::ptr_t LLEnvironment::createDayCycleFromLegacyPreset(const std::st return day; } +LLSettingsDay::ptr_t LLEnvironment::createDayCycleFromEnvironment(EnvSelection_t env, LLSettingsBase::ptr_t settings) +{ + std::string type(settings->getSettingType()); + + if (type == "daycycle") + return std::static_pointer_cast<LLSettingsDay>(settings); + + if ((env != ENV_PARCEL) && (env != ENV_REGION)) + { + LL_WARNS("ENVIRONMENT") << "May only create from parcel or region environment." << LL_ENDL; + return LLSettingsDay::ptr_t(); + } + + LLSettingsDay::ptr_t day = this->getEnvironmentDay(env); + if (!day && (env == ENV_PARCEL)) + { + day = this->getEnvironmentDay(ENV_REGION); + } + + if (!day) + { + LL_WARNS("ENVIRONMENT") << "Could not retrieve existing day settings." << LL_ENDL; + return LLSettingsDay::ptr_t(); + } + + day = day->buildClone(); + + if (type == "sky") + { + for (S32 idx = 1; idx < LLSettingsDay::TRACK_MAX; ++idx) + day->clearTrack(idx); + day->setSettingsAtKeyframe(settings, 0.0f, 1); + } + else if (type == "water") + { + day->clearTrack(LLSettingsDay::TRACK_WATER); + day->setSettingsAtKeyframe(settings, 0.0f, LLSettingsDay::TRACK_WATER); + } + + return day; +} void LLEnvironment::legacyLoadAllPresets() { diff --git a/indra/newview/llenvironment.h b/indra/newview/llenvironment.h index 7c8304a7ed..6382e0afd4 100644 --- a/indra/newview/llenvironment.h +++ b/indra/newview/llenvironment.h @@ -148,6 +148,8 @@ public: bool canEdit() const; bool isExtendedEnvironmentEnabled() const; bool isInventoryEnabled() const; + bool canAgentUpdateParcelEnvironment(bool useselected = false) const; + bool canAgentUpdateRegionEnvironment() const; LLSettingsSky::ptr_t getCurrentSky() const { return mCurrentEnvironment->getSky(); } LLSettingsWater::ptr_t getCurrentWater() const { return mCurrentEnvironment->getWater(); } @@ -211,6 +213,9 @@ public: static LLSettingsSky::ptr_t createSkyFromLegacyPreset(const std::string filename); static LLSettingsDay::ptr_t createDayCycleFromLegacyPreset(const std::string filename); + // Construct a new day cycle based on the environment. Replacing either the water or the sky tracks. + LLSettingsDay::ptr_t createDayCycleFromEnvironment(EnvSelection_t env, LLSettingsBase::ptr_t settings); + //------------------------------------------- connection_t setSkyListChange(const change_signal_t::slot_type& cb); connection_t setWaterListChange(const change_signal_t::slot_type& cb); @@ -221,10 +226,16 @@ public: void onLegacyRegionSettings(LLSD data); void requestRegion(); + void updateRegion(const LLUUID &asset_id, S32 day_length, S32 day_offset); void updateRegion(LLSettingsDay::ptr_t &pday, S32 day_length, S32 day_offset); + void updateRegion(LLSettingsSky::ptr_t &psky, S32 day_length, S32 day_offset); + void updateRegion(LLSettingsWater::ptr_t &pwater, S32 day_length, S32 day_offset); void resetRegion(); void requestParcel(S32 parcel_id); + void updateParcel(S32 parcel_id, const LLUUID &asset_id, S32 day_length, S32 day_offset); void updateParcel(S32 parcel_id, LLSettingsDay::ptr_t &pday, S32 day_length, S32 day_offset); + void updateParcel(S32 parcel_id, LLSettingsSky::ptr_t &psky, S32 day_length, S32 day_offset); + void updateParcel(S32 parcel_id, LLSettingsWater::ptr_t &pwater, S32 day_length, S32 day_offset); void resetParcel(S32 parcel_id); void selectAgentEnvironment(); @@ -390,6 +401,8 @@ private: void onAgentPositionHasChanged(const LLVector3 &localpos); void onSetEnvAssetLoaded(EnvSelection_t env, LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status); + void onUpdateParcelAssetLoaded(LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, S32 parcel_id, S32 day_length, S32 day_offset); + //========================================================================= void legacyLoadAllPresets(); static std::string getSysDir(const std::string &subdir); diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index 4703a1ec16..a9f0cffef1 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -267,7 +267,8 @@ void LLFloaterEditExtDayCycle::refresh() mFlyoutControl->setMenuItemEnabled(ACTION_SAVE, is_inventory_avail); mFlyoutControl->setMenuItemEnabled(ACTION_SAVEAS, is_inventory_avail); - + mFlyoutControl->setMenuItemEnabled(ACTION_APPLY_PARCEL, canApplyParcel()); + mFlyoutControl->setMenuItemEnabled(ACTION_APPLY_REGION, canApplyRegion()); LLFloater::refresh(); } @@ -950,26 +951,35 @@ void LLFloaterEditExtDayCycle::doApplyUpdateInventory() void LLFloaterEditExtDayCycle::doApplyEnvironment(const std::string &where) { - LLEnvironment::EnvSelection_t env(LLEnvironment::ENV_DEFAULT); - bool updateSimulator(where != ACTION_APPLY_LOCAL); - if (where == ACTION_APPLY_LOCAL) - env = LLEnvironment::ENV_LOCAL; + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, mEditDay); + } else if (where == ACTION_APPLY_PARCEL) - env = LLEnvironment::ENV_PARCEL; + { + LLParcelSelectionHandle handle(LLViewerParcelMgr::instance().getParcelSelection()); + LLParcel *parcel(nullptr); + + if (handle) + parcel = handle->getParcel(); + if (!parcel) + parcel = LLViewerParcelMgr::instance().getAgentParcel(); + + if (!parcel) + return; + + LLEnvironment::instance().updateParcel(parcel->getLocalID(), mEditDay, -1, -1); + } else if (where == ACTION_APPLY_REGION) - env = LLEnvironment::ENV_REGION; + { + LLEnvironment::instance().updateRegion(mEditDay, -1, -1); + } else { LL_WARNS("ENVIRONMENT") << "Unknown apply '" << where << "'" << LL_ENDL; return; } - LLEnvironment::instance().setEnvironment(env, mEditDay); - if (updateSimulator) - { - LL_WARNS("ENVIRONMENT") << "Attempting apply" << LL_ENDL; - } } void LLFloaterEditExtDayCycle::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results) @@ -1038,7 +1048,7 @@ bool LLFloaterEditExtDayCycle::canApplyParcel() const if (!parcel) return false; - return parcel->allowModifyBy(gAgent.getID(), gAgent.getGroupID()) && + return parcel->allowTerraformBy(gAgent.getID()) && LLEnvironment::instance().isExtendedEnvironmentEnabled(); } diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp index 6c1080294d..e7b8481caa 100644 --- a/indra/newview/llfloaterfixedenvironment.cpp +++ b/indra/newview/llfloaterfixedenvironment.cpp @@ -160,6 +160,8 @@ void LLFloaterFixedEnvironment::refresh() mFlyoutControl->setMenuItemEnabled(ACTION_SAVE, is_inventory_avail); mFlyoutControl->setMenuItemEnabled(ACTION_SAVEAS, is_inventory_avail); + mFlyoutControl->setMenuItemEnabled(ACTION_APPLY_PARCEL, canApplyParcel()); + mFlyoutControl->setMenuItemEnabled(ACTION_APPLY_REGION, canApplyRegion()); mTxtName->setValue(mSettings->getName()); @@ -283,26 +285,41 @@ void LLFloaterFixedEnvironment::doApplyUpdateInventory() void LLFloaterFixedEnvironment::doApplyEnvironment(const std::string &where) { - LLEnvironment::EnvSelection_t env(LLEnvironment::ENV_DEFAULT); - bool updateSimulator( where != ACTION_APPLY_LOCAL ); - if (where == ACTION_APPLY_LOCAL) - env = LLEnvironment::ENV_LOCAL; + { + LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_LOCAL, mSettings); + } else if (where == ACTION_APPLY_PARCEL) - env = LLEnvironment::ENV_PARCEL; + { + LLParcelSelectionHandle handle(LLViewerParcelMgr::instance().getParcelSelection()); + LLParcel *parcel(nullptr); + + if (handle) + parcel = handle->getParcel(); + if (!parcel) + parcel = LLViewerParcelMgr::instance().getAgentParcel(); + + if (!parcel) + return; + + if (mSettings->getSettingType() == "sky") + LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsSky>(mSettings), -1, -1); + else if (mSettings->getSettingType() == "water") + LLEnvironment::instance().updateParcel(parcel->getLocalID(), std::static_pointer_cast<LLSettingsWater>(mSettings), -1, -1); + } else if (where == ACTION_APPLY_REGION) - env = LLEnvironment::ENV_REGION; + { + if (mSettings->getSettingType() == "sky") + LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsSky>(mSettings), -1, -1); + else if (mSettings->getSettingType() == "water") + LLEnvironment::instance().updateRegion(std::static_pointer_cast<LLSettingsWater>(mSettings), -1, -1); + } else { LL_WARNS("ENVIRONMENT") << "Unknown apply '" << where << "'" << LL_ENDL; return; } - LLEnvironment::instance().setEnvironment(env, mSettings); - if (updateSimulator) - { - LL_WARNS("ENVIRONMENT") << "Attempting apply" << LL_ENDL; - } } void LLFloaterFixedEnvironment::onInventoryCreated(LLUUID asset_id, LLUUID inventory_id, LLSD results) diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index fe7d302992..58f6c2065b 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -81,6 +81,8 @@ #include "llwearableitemslist.h" #include "lllandmarkactions.h" #include "llpanellandmarks.h" +#include "llviewerparcelmgr.h" +#include "llparcel.h" #include "llenvironment.h" @@ -6920,11 +6922,16 @@ void LLSettingsBridge::performAction(LLInventoryModel* model, std::string action if (!item) return; LLUUID asset_id = item->getProtectedAssetUUID(); - // *LAPRAS* TODO update on simulator. - LL_WARNS("LAPRAS") << "Only updating locally!!! NOT REALLY PARCEL UPDATE" << LL_ENDL; - LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_LOCAL); - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_PARCEL, asset_id); - LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); + + LLParcel *parcel = LLViewerParcelMgr::instance().getAgentParcel(); + if (!parcel) + { + LL_WARNS("INVENTORY") << "could not identify parcel." << LL_ENDL; + return; + } + S32 parcel_id = parcel->getLocalID(); + + LLEnvironment::instance().updateParcel(parcel_id, asset_id, -1, -1); } else if ("apply_settings_region" == action) { @@ -6933,11 +6940,8 @@ void LLSettingsBridge::performAction(LLInventoryModel* model, std::string action if (!item) return; LLUUID asset_id = item->getProtectedAssetUUID(); - // *LAPRAS* TODO update on simulator. - LL_WARNS("LAPRAS") << "Only updating locally!!! NOT REALLY REGION UPDATE" << LL_ENDL; - LLEnvironment::instance().clearEnvironment(LLEnvironment::ENV_LOCAL); - LLEnvironment::instance().setEnvironment(LLEnvironment::ENV_REGION, asset_id); - LLEnvironment::instance().setSelectedEnvironment(LLEnvironment::ENV_LOCAL); + + LLEnvironment::instance().updateRegion(asset_id, -1, -1); } else LLItemBridge::performAction(model, action); @@ -6988,24 +6992,34 @@ void LLSettingsBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back("Settings Apply Local"); items.push_back("Settings Apply Parcel"); - // *LAPRAS* TODO: test for permission - + if (!canUpdateParcel()) + disabled_items.push_back("Settings Apply Parcel"); + items.push_back("Settings Apply Region"); - // *LAPRAS* TODO: test for permission + if (!canUpdateRegion()) + disabled_items.push_back("Settings Apply Region"); } addLinkReplaceMenuOption(items, disabled_items); hide_context_entries(menu, items, disabled_items); } -std::string LLSettingsBridge::getLabelSuffix() const -{ - return LLItemBridge::getLabelSuffix(); -} BOOL LLSettingsBridge::renameItem(const std::string& new_name) { + /*TODO: change internal settings name? */ return LLItemBridge::renameItem(new_name); } +bool LLSettingsBridge::canUpdateParcel() const +{ + return LLEnvironment::instance().canAgentUpdateParcelEnvironment(); +} + +bool LLSettingsBridge::canUpdateRegion() const +{ + return LLEnvironment::instance().canAgentUpdateRegionEnvironment(); +} + + // +=================================================+ // | LLLinkBridge | // +=================================================+ diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index e7df5e4e93..6a04f08cf8 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -622,13 +622,15 @@ public: virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual std::string getLabelSuffix() const; - virtual BOOL renameItem(const std::string& new_name); + virtual BOOL renameItem(const std::string& new_name); virtual LLSettingsType::type_e getSettingsType() const { return mSettingsType; } - protected: + bool canUpdateRegion() const; + bool canUpdateParcel() const; + LLSettingsType::type_e mSettingsType; + }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index d7193c511f..756b711c4c 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -11113,4 +11113,31 @@ Failed to find the settisgs named [DESC] in database. <tag>fail</tag> </notification> + <notification + icon="notify.tga" + name="FailedToLoadSettingsApply" + persist="true" + type="alertmodal"> +Unable to apply those settings to the environment. + <tag>fail</tag> + </notification> + + <notification + icon="notify.tga" + name="FailedToBuildSettingsDay" + persist="true" + type="alertmodal"> +Unable to apply those settings to the environment. + <tag>fail</tag> + </notification> + + <notification + icon="notify.tga" + name="NoEnvironmentSettings" + persist="true" + type="alertmodal"> +This Region does not support environmental settings. + <tag>fail</tag> + </notification> + </notifications> |