В общем задам вопрос отдельно.

В  ActivitySectionV2 я добавил свой атрибут

 "activeLeadId":{
           	DataValueType: Terrasoft.DataValueType.STRING
           }

В методе загрузки представления я устанавливаю его значение через  this.set  -  все замечательно, вывожу сообщение - там то, что мне нужно.

А вот затем я формирую состав Url для iframe и мне нужно получить его значение. Не пойму как. Через  this.get не работает.

 diff: /**SCHEMA_DIFF*/[
        	 {
                "operation": "insert",
                "name": "iframe",
                "parentName": "DataViewsContainer",
                "propertyName": "items",
                "values": {
                    "id": "iframe",
                    "selectors": {"wrapEl": "#iframe"},
                    "itemType": Terrasoft.ViewItemType.CONTAINER,
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
					"html": '<iframe name="iframe" id="iframe" width="100%" height="700px"' +
			  		'src="https://domain.com/base.php'+
					'?owner='+
					Terrasoft.SysValue.CURRENT_USER.value + 
					'&leadid='+ 
 
	//Вот сюда надо получить значение атрибута
 
			   		'" frameborder="0"></iframe>',
                    "visible": {"bindTo": "isTimelineDataView"}
             	}

 

Нравится

11 комментариев

Создайте атрибут с нужной вам ссылкой (пecnfyjdbnm его например в init)

забиньте  html на ваш атрибут с сылкой

Пример можно посмотреть в методе getRecommendationConfig схемы GlobalSearchResultPage

 

Григорий Чех,

Я вроде пробую забиндить, только не получается, те пишу в Init пишу

this.set("iframeUrl", '&lt;iframe name="iframe" id="iframe" width="100%" height="700px"' +
					'src="https://domain.com'+
					'" frameborder="0"&gt;&lt;/iframe&gt;');

Затем в Diff

diff: /**SCHEMA_DIFF*/[
        	 {
                "operation": "insert",
                "name": "iframe",
                "parentName": "DataViewsContainer",
                "propertyName": "items",
                "values": {
                    "id": "iframe",
                    "selectors": {"wrapEl": "#iframe"},
 
                    "itemType": Terrasoft.ViewItemType.CONTAINER,
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
				    "html":{"bindTo": "iframeUrl"},
                    "visible": {"bindTo": "isTimelineDataView"}
             	}
             },

И ничего не выводит, только заголовок секции - дальше пусто. Если в "html" прописываю код напрямую, то все работает.  

Проблема даже в другом, я в методе Init задаю значение переменной, оно выводится корректно. Но, когда эту переменную использую в values, мне формируется url предыдущий, как из кэша.

Те через переменную сформировать нужный html получается, но выводится старое значение, через атрибут не получается получить значение(( 

Особенно с переменной непонятно, на каком уровне кэшируется все. Если просто alert или в консоль, но значение правильное, а в iframe старое из предыдущего открытия этой страницы.

 

 В итоге, вместо атрибута я смог вставлять туда функцию обычную, но вот проблема - она не вызывается, если не делать рефреш страницы. Те неважно, какой там код внутри DIFF, он просто не вызывается. Те получается, неважно как я получаю ID, будь то URL или любой другой способ, чтобы вставить его в iframe - нужно как-то его обновить принудительно.

Вот вопрос - как это побороть?

Вот полный исходный код на текущий момент. Куда копать в части принудительного обновления и/или сброса  кэша?

 

define("ActivitySectionV2", [], function() {
 
	var currentLocation = '';
	var leadGuid = '';
 
	 function getFrame()
        {
        	currentLocation = window.location+'';
        	leadGuid = getAllUrlParams(currentLocation).guid;
        	if (leadGuid != null) 
        	{
        	var strFrame = '&lt;iframe name="iframe" id="iframe" width="100%" height="700px"' +
					'src="https://domain.com'+
					'/'+
		             Terrasoft.SysValue.CURRENT_USER.value + 
					'/'+ 
			 		leadGuid +
					'" frameborder="0"&gt;&lt;/iframe&gt;';
 
        	}
			else
			{
				var strFrame = '&lt;iframe name="iframe" id="iframe" width="100%" height="700px"' +
					'src="https://domain.com'+
					'/'+
		             Terrasoft.SysValue.CURRENT_USER.value + 
					'" frameborder="0"&gt;&lt;/iframe&gt;';
 
			}	
			alert(strFrame);
        	return strFrame;
        }
 
	//Функция получения параметра из businessRules
	  function getAllUrlParams(url) {
            //код функции .....
      return obj;
      }
 
    return {
        entitySchemaName: "Activity",
        mixins: {},
        attributes: {
            "isTimelineVisible": {
                dataValueType: Terrasoft.DataValueType.BOOLEAN
            },
 
           // "activeLeadId":{
           //	DataValueType: Terrasoft.DataValueType.STRING
           //},
           // "iframeUrl":{
           //	DataValueType: Terrasoft.DataValueType.TEXT
           // }
 
        },
        messages: {},
        methods: {
 
        	getDefaultDataViews: function(){
        		var baseDataViews = this.callParent();
        		baseDataViews.TimelineDataView = {
        			index: 3,
                    name: "TimelineDataView",
                    caption: "Расписание",
                    hint: "Таймлайн",
                    icon: this.get("Resources.Images.SchedulerDataViewIcon")
        		};
        		return baseDataViews;
        	},
 
        	loadTimelineDataView: function(loadData) {
        		this.set("IsActionButtonsContainerVisible", true);
        		this.set("isTimelineVisible", true);
                this.set("IsAnalyticsActionButtonsContainerVisible", false);
                if (loadData === false) {
                        return;
                }
        	},
        	isTimelineDataView: function() {
                 return (this.get("ActiveViewName") === "TimelineDataView");
        	},
        	isNotTimelineDataView: function() {
                 return !this.isTimelineDataView();
        	}
        },
        rules: {},
        businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/,
        modules: /**SCHEMA_MODULES*/{}/**SCHEMA_MODULES*/,
        diff: /**SCHEMA_DIFF*/[
        	 {
                "operation": "insert",
                "name": "iframe",
                "parentName": "DataViewsContainer",
                "propertyName": "items",
                "values": {
                    "id": "iframe",
                    "selectors": {"wrapEl": "#iframe"},
                    "itemType": Terrasoft.ViewItemType.CONTAINER,
					"layout": { "colSpan": 24, "rowSpan": 1, "column": 0, "row": 4 },
		   			"html":getFrame(),
                    "visible": {"bindTo": "isTimelineDataView"}
             	}
             },
             {
             	"operation": "merge",
                "name": "GridUtilsContainer",
                "propertyName": "items",
                "values": {
                	'visible': {"bindTo": "isNotTimelineDataView"}
                }
             }
        ]/**SCHEMA_DIFF*/
    };
});

 

Для обновления страницы используют this.reloadEntity(); 

Зверев Александр,

 А тут страница раздела, не срабатывает(( Пишет this.reloadEntity() is not a function. Я уже все перепробовал - не совсем пойму, вот у меня в DIFF формируется html и там есть вызов функции (getIframe). Так вот она отрабатывает только если F5 сделать, и все. Такое ощущение, что один раз этот код отработал и все, держит в кэше где-то.

Потом ходишь по меню - никаких изменений. Пробовал на Onrender повесить изменение DOM - тоже не работает, вроде как DOM уже есть, меняю src и ничего, все перезаписывается, в общем везде затык. А после onrender методов и нет никаких.



 

Зверев Александр,

При этом методы Init и OnRender отрабатывают при каждом переходе.

Вопрос - как мне в них изменить массив Values элемента. Если я сделаю как-то через bindTo (хотя не получилось через атрибут - просто не грузится) - это должно работать или также закэшируется, как и обычная функция?

Есть несколько статей и тем о добавлении iframe на страницу. Посмотрите, возможно, в них добиваются того, что Вы хотите сделать.

Показать все комментарии

Вопрос такой - в разделе Активности сделал дополнительное представление. А как его жестко сделать по умолчанию, когда по ссылке в данный раздел перехожу?

И как можно ему принудильно рефреш делать, тк я передаю туда гуид в параметрах URL и затем получаю в переменной, но код в define берет старые значения, нужно принудительно  refresh делать.

Либо может можно как-то более красиво передавать параметр (мне по сути нужно передать в раздел id Лида, из раздела которого я по кнопке в раздел Активности перехожу:

define("LeadSectionV2", [], function() {
	return {
		entitySchemaName: "Lead",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
			{
                                        "operation": "insert",
                                        "name": "CalculateButton",
                                        "values": {
                                        "itemType": Terrasoft.ViewItemType.BUTTON,
                                        "caption": "Бронировать",
                                        "style": Terrasoft.controls.ButtonEnums.style.BLUE,
                                        "visible": true,
                                        "click": {bindTo: "onOpenPrimaryContactClick"},
                                },
                                        "parentName": "CombinedModeActionButtonsCardLeftContainer",//"CombinedModeActionButtonsSectionContainer",
                                        "propertyName": "items",
               }
 
			]/**SCHEMA_DIFF*/,
		methods: {
 
			//
				onOpenPrimaryContactClick: function() {
                // Определение активной записи 
                var LeadId = this.get("ActiveRow");
   						var primaryId = "?guid="+LeadId;
                        // Формирование строки адреса.
                        var requestUrl = "SectionModuleV2/ActivitySectionV2/" + primaryId;
                        this.sandbox.publish("PushHistoryState", {
                            hash: requestUrl
                        });
 
            },
 
 
			//
 
		}
	};
});

 

Нравится

2 комментария
define("ActivitySectionV2", [], function() {
 
	var currentLocation = '';
	var leadGuid = '';
    currentLocation = window.location+'';

А тут в разделе я получаю URL, но он берет старый из кэша, новый только при рефреше

Вопрос передачи данных в новое представление раздела обсуждался тут, также см. статью с пошаговым созданием раздела с несколькими представлениями.

Показать все комментарии

Проблема в блокировке колонок редактируемого реестра. Почти все колонки завязаны на правилах, а требуется заблокировать всю страницу. Использую:

attributes: {

   "IsModelItemsEnabled": {
      dataValueType: Terrasoft.DataValueType.BOOLEAN,
      value: true,
      dependencies: [{
         columns: ["NavOrderStatus"],
         methodName: "changeNavOrderStatus"
      }]
   }

}

А в методе изменения:

this.set("IsModelItemsEnabled", false);

Поля страницы блокируются, но в детали поля, что завязаны на правилах остаются разблокированными. Пробовал блокировать через метод:

getDefaultCellControlsConfig: function(columnName, params) {
   var config = {
      itemType: Terrasoft.ViewItemType.MODEL_ITEM,
      name: columnName,
      labelConfig: {visible: false},
   };

   if(columnName = "Product" && ...){
      config.enabled = false;
   }


   return Ext.apply(config, params);
}

Что тоже не помогает.. Подскажите, пожалуйста решение данной проблемы.

Нравится

9 комментариев
Лучший ответ

я блокировал через метод generateActiveRowControlsConfig. Ниже представлен мой код, Вам соответственно необходимо убрать всякие условия и просто блокировать поля без разбора

generateActiveRowControlsConfig: function (id, columnsConfig, rowConfig) {
                    this.columnsConfig = columnsConfig;
                    var gridData = this.getGridData();
                    var activeRow = gridData.get(id);
                    var isEditableColumn;
                    if (activeRow.values.IDSBParent === "") {
                        isEditableColumn = this.isEditableParentColumn;
                    }
                    else {
                        isEditableColumn = this.isEditableColumn;
                    }
                    var gridLayoutItems = [];
                    var currentColumnIndex = 0;
                    Terrasoft.each(columnsConfig, function (columnConfig) {
                        var cellConfig = this.getActiveRowCellConfig(columnConfig, currentColumnIndex);
                        cellConfig.enabled = isEditableColumn(cellConfig.name);
                        if (!cellConfig.hasOwnProperty("isNotFound")) {
                            gridLayoutItems.push(cellConfig);
                        }
                        currentColumnIndex += cellConfig.layout.colSpan;
                    }, this);
                    this.applyBusinessRulesForActiveRow(id, gridLayoutItems);
                    var viewGenerator = Ext.create(this.getRowViewGeneratorClassName());
                    viewGenerator.viewModelClass = this;
                    var gridLayoutConfig = viewGenerator.generateGridLayout({
                        name: this.name,
                        items: gridLayoutItems
                    });
                    rowConfig.push(gridLayoutConfig);
                }

 

я блокировал через метод generateActiveRowControlsConfig. Ниже представлен мой код, Вам соответственно необходимо убрать всякие условия и просто блокировать поля без разбора

generateActiveRowControlsConfig: function (id, columnsConfig, rowConfig) {
                    this.columnsConfig = columnsConfig;
                    var gridData = this.getGridData();
                    var activeRow = gridData.get(id);
                    var isEditableColumn;
                    if (activeRow.values.IDSBParent === "") {
                        isEditableColumn = this.isEditableParentColumn;
                    }
                    else {
                        isEditableColumn = this.isEditableColumn;
                    }
                    var gridLayoutItems = [];
                    var currentColumnIndex = 0;
                    Terrasoft.each(columnsConfig, function (columnConfig) {
                        var cellConfig = this.getActiveRowCellConfig(columnConfig, currentColumnIndex);
                        cellConfig.enabled = isEditableColumn(cellConfig.name);
                        if (!cellConfig.hasOwnProperty("isNotFound")) {
                            gridLayoutItems.push(cellConfig);
                        }
                        currentColumnIndex += cellConfig.layout.colSpan;
                    }, this);
                    this.applyBusinessRulesForActiveRow(id, gridLayoutItems);
                    var viewGenerator = Ext.create(this.getRowViewGeneratorClassName());
                    viewGenerator.viewModelClass = this;
                    var gridLayoutConfig = viewGenerator.generateGridLayout({
                        name: this.name,
                        items: gridLayoutItems
                    });
                    rowConfig.push(gridLayoutConfig);
                }

 

Если есть возможность, лучше не блокировать программно, а настроить для этого пользователя для этого объекта или только для конкретных записей прав доступа только на чтение. Так можно будет обезопасить систему от разблокировки самостоятельно пользователем путём правки кода через браузер. Другое дело, что настройка прав не всегда покрывает все возможные случаи, когда пользователю нужно закрыть отдельное поле в отдельной записи.

Хорошо.. сформулирую по другому... Надо заблокировать страницу полностью, исключая все правила и тп. Просто на каждой детали делать метод блокировки и все условия, по которому блокируются это выглядит как то костыльно.

 

Просто забрать у пользователя права на запись на объект детали.

Зверев Александр,

Подскажите, как из кода давать и забирать права. К примеру по клику на кнопку права будут ограничиваться только для чтения

Права определяются содержимым таблиц, для изменения прав нужно добавлять/изменять/удалять записи в них. Например, для прав доступа на записи в разделе контактов это SysContactRight. А если это делать в БП, есть специальный элемент изменения прав.

Дмитрий А.,

Проблема в том, что поля детали не блокируются из-за правил, а делал я именно по этим ссылкам

Если отобрать права доступа, правила будут безразличны.

Показать все комментарии

Что-то я запутался совсем. Создаю новый объект в конфигурации, выбираю родительский Базовый объект, а никаких полей не наследуется. Смотрю в базовый объект - там тоже полей нет никаких.

Это нормально? Причем на двух сайтах так разных. 

Пытаюсь наследовать от справочника, а там только поле название, а ID нет тоже.

Соответственно, при сохранении нового объекта у меня не создаются поля ID и тд

Или что-то не так делаю, вроде до этого так и создавал. 

Изображение удалено.Изображение удалено.

Нравится

2 комментария
Лучший ответ

Вам нужно в схеме таблицы в правом верхнем углу нажать кнопку [Настройки] и в появившемся окне установить опцию 'Показывать системные колонки', после сохранить настройки:

Даже, если у Вас эта настройка отключена, то наследуемые поля также создаются, как и при включенной настройке.

Вам нужно в схеме таблицы в правом верхнем углу нажать кнопку [Настройки] и в появившемся окне установить опцию 'Показывать системные колонки', после сохранить настройки:

Даже, если у Вас эта настройка отключена, то наследуемые поля также создаются, как и при включенной настройке.

Алла Савельева,

А, точно, работает!)) Странно, что в других объектах я их видел, а в новых и вот базовых нет. Похоже настройка на уровне объекта эта получается.

Показать все комментарии

В общем все в самом названии вопроса.

При добавлении продуктов в счет, его сумма меняется, но похоже события изменения не происходит. А мне нужно запустить процесс изменения суммы в родительском объекте, к которому счет привязан.

Можно, конечно, попробовать на добавление продуктов в счете сделать, но хотелось бы на счет все-таки. 

Нравится

1 комментарий
Лучший ответ

Добрый день!

При добавлении продуктов в счет сумма в счете обновляется не через esq, так что события на счете отрабатывать не будут





Так что необходимо вашу логику завязывать именно на изменение продукта в счете, а не счета

Добрый день!

При добавлении продуктов в счет сумма в счете обновляется не через esq, так что события на счете отрабатывать не будут





Так что необходимо вашу логику завязывать именно на изменение продукта в счете, а не счета

Показать все комментарии

Подскажите, плз, наиболее правильный путь решения задачки по шагам.

Требуется немного изменить стандартный контрол выбора даты и времени. 

Конкретно - делать валидацию доступности выбранного сотрудника (с этим не проблема) и позволять выбирать только те даты и время, которое свободно (другие даты просто показывать серым цветом). 

Те как я понимаю нужно переписывать логику текущего контрола, а вот как это сделать? С чего начать, не пойму по документации

 

Нравится

2 комментария
Лучший ответ

Насколько мне известно в штатном Terrasoft.DatePicker этой возможности нет и вы можете либо расширить данный контрол добавив в него коллекцию запрещенных дат и ее обработку при выборе или попытаться прикрутить сторонний компонент, который обладает данной функциональностью.

 

Для 1го варианта нужно создать класс-обработчик, в котором будете добавлять логику работы контрола, как то так:

define("NewDatepicker", ["terrasoft"],
	function() {
		Ext.define('Terrasoft.controls.NewDatepicker', {
			extend: 'Terrasoft.DatePicker',
			alternateClassName: 'Terrasoft.NewDatepicker',
			.....

Подробнее за наследование (расширение) смотрите тут

Насколько мне известно в штатном Terrasoft.DatePicker этой возможности нет и вы можете либо расширить данный контрол добавив в него коллекцию запрещенных дат и ее обработку при выборе или попытаться прикрутить сторонний компонент, который обладает данной функциональностью.

 

Для 1го варианта нужно создать класс-обработчик, в котором будете добавлять логику работы контрола, как то так:

define("NewDatepicker", ["terrasoft"],
	function() {
		Ext.define('Terrasoft.controls.NewDatepicker', {
			extend: 'Terrasoft.DatePicker',
			alternateClassName: 'Terrasoft.NewDatepicker',
			.....

Подробнее за наследование (расширение) смотрите тут

Григорий Чех,

Спасибо, будем пробовать!

 

Показать все комментарии

В общем идея такая, требуется расчет каких полей в бизнес процессе, например, автонумерация, или заполнение полей из полей справочника и тп. 

Как сделать так, чтобы после завершения расчет Бизнес процесс обновлял форму, открытую у пользователя, а не надо было делать  refresh. Лучше даже не форму, а конкретное поле.

Может есть пример реализации.

Нравится

8 комментариев
Лучший ответ

Добрый день!

Используйте метод для обновления полей страницы

this.onReloadCard();

либо

this.loadEntity(this.get("Id"));

 

Добрый день!

Используйте метод для обновления полей страницы

this.onReloadCard();

либо

this.loadEntity(this.get("Id"));

 

А для передачи информации о необходимости обновления из БП в браузер используются WebSocket.

Ок, спасибо, буду пробовать!

Зверев Александр,

А есть какой-то пример, как на клиенткой стороне (в какой метод нужно положить код отслеживания и обновления)? Как отправить сообщение более менее понятно, вопрос в примере, как его отследить на странице.

Я правильно понимаю, что в соответствии с https://academy.terrasoft.ru/documents/technic-sdk/7-14/clientmessagebridge-obrabotchik-websocket-soobshcheniya-na-storone-klienta , нужно сделать замещающий клиентский модуль  ClientMessageBridge и затем на самой странице еще метод. И как быть с новым обновлением, что замещать клиентские модули больше нельзя будет? Какая альтернатива?

PS: Извиняюсь за возможно глупые вопросы, просто опыта в серьезной разработке мало очень, тем более в данной системе, я консультант. А фрилансер с авансом сбежал и спрятался, ничего не сделав, приходится вот ночами самому с нуля разбираться, сдавать систему то надо))

Можно попробовать пересмотреть логику, чтобы всё делалось в процессе и карточка записи с обновлёнными значениями полей открывалась уже в конце. Ввод значений для начала вычислений делать при помощи автогенерируемой или преднастроенной страницы.

SERGEY PIMINOV,

Напишите подробно, какую задачу нужно решить, может у Вашей задачи есть более простое решение - такое бывает часто.

Алла Савельева,

Алла, задача простая. Вот пример - есть форма продукта, в нет деталь - состав. Я туда добавляю позиции и рассчитываю себестоимость. Все замечательно, ничего не нужно программировать, но прям беда - нужно форму обновлять вручную.   И таких задач много, вроде бы можно сделать с помощью БП все быстро, но данные не обновляются. Сделали бы какое-то приложение универсальное, чтобы была такая опция - вот прям очень не хватает. В документации тоже много пробелов, если бы кто-то видео записал бы, а так все присылают куски кода, а куда его вставлять и как - не совсем понятно. Как сделать, если нужно для нескольких форм и процессов такое обновление делать - тоже неясно.

Вся логика на уровне БП работает на сервере, а клиентского браузера может при этом даже не быть, если процесс запустился по планировщику. Для передачи сигналов в браузер нужно использовать механизм веб-сокетов, без программирования тут пока не обойтись.

Показать все комментарии

Тут прочитал, что загрузку пакетов в промышленную среду стоит делать только из ZIP пакетов, а не через SVN.

А у меня уже есть пакет, подключенный именно к SVN

Вопрос, как правильно отключить SVN от промышленной среды и загрузить туда пакет через ZIP? Если просто удалить подключенный к SVN и заново загрузить новый уже ZIP архивом, ничего не поломается?

Нравится

3 комментария

Не должно если есть сомнения сделайте бэкап БД на основе него разверните тестовый стенд и проверьте как все пройдет.

И не забывайте делать бэкапы БД перед такими операциями.

 

Попробовал на тесте. Теперь пакет не удаляется, пишет сначала сообщение, что требуется авторизация и предлагает авторизоваться сейчас, затем выдает ошибку. На SVN папку  locks почистил, сервер перезапустил. С чем может быть проблема?

Еще вопрос - не совсем понял из документации, если я устанавливаю пакет из файловой системы как архив, обновление каким образом затем происходит - он автоматически обновит, если я новую версию пакета буду загружать или надо будет удалять и заново устанавливать?

SERGEY PIMINOV пишет:

Теперь пакет не удаляется, пишет сначала сообщение, что требуется авторизация и предлагает авторизоваться сейчас, затем выдает ошибку.

См. аналогичный случай тут.

SERGEY PIMINOV пишет:

он автоматически обновит, если я новую версию пакета буду загружать или надо будет удалять и заново устанавливать?

Главное, не переименовывать архив, поскольку:

При установке приложения marketplace из *.zip-архива название приложения в bpm'online формируется на основании названия *.zip-архива. Если при последующем обновлении этого приложения marketplace использовать *.zip-архив с таким же набором пакетов, но с другим названием, то в разделе [Установка и удаление приложений] будет добавлена запись о новом приложении marketplace. 

 

Показать все комментарии

Добрый день, 

Добавили в объект Product поле PriceVAT тип FLOAT. Также было добавлено аналогичное поле в объект ProductPrice. В базовой логике, при изменении поля Price объекта Product делается update/insert поля Price объекта ProductPrice и наоборот. Подскажите, в каких схемах и/или событиях вызывается этот метод? Мне нужно для своего поля PriceVAT добавить такой же механизм синхронизации. Спасибо

Нравится

1 комментарий
Лучший ответ

Доброе утро!

Данная логика реализована в методе synchronizePrice в схеме ProductPageV2 пакета ProductCatalogue.

Доброе утро!

Данная логика реализована в методе synchronizePrice в схеме ProductPageV2 пакета ProductCatalogue.

Показать все комментарии

Как можно вручную или еще как-то удалить страницу раздела из пакета. Пакет синхронизируется на SVN.

Не могу не изменить ни удалить страницу. Другие страницы редактируются.

Изображение удалено.

 

Нравится

2 комментария