FoxPro Club Главная

Конференция Решения Фотоальбом Сайт М.Дроздова Статьи Файловый архив Книга Visual FoxPro 9.0 Русский Help для Visual FoxPro
Пользователей: 9145
Вход
Создание диаграмм для отчетов OpenOffice.org Calc 2.4+ средствами VFP

Автоматизация OpenOffice.org (далее OOo) из VFP с использованием нового API-кода дело не совсем простое и требует больших усилий и временных затрат для экспериментов. Особенно, с учетом того, что API-код в отличие от устаревшего Dispatch-кода, не записывается при автоматической записи действий в макросы, как в MSO. Хотя API-код для OOo и выглядит красиво, для его создания требуется глубокое изучение нового API, введенного в OOo, начиная с версии 2.4 и выше. Справку по API можно найти на сайте SUN: http://api.openoffice.org/docs/common/ref/index-files/index-1.html Данный пример написан мной пару дней назад и протестирован на версии OOo 3.0.1 (максимальной, доступной в настоящий момент). В коде я постарался привести максимум комментариев, чтобы его работа была понятна всем загрузившим данный пример. Для работы кода требуется библиотека LibOOo.prg, не включенная в данный архив, но доступная на форуме или, например, здесь: http://www.foxclub.ru/sol/index.php?act=view&id=590. Надеюсь, что данный пример многим поможет в деле освоения замечательного, бурно-развивающегося в последнее время и находящего всё большее число сторонников, свободного офисного пакета программ OpenOffice.org, совместимого с форматами MSO: DOC и XLS, а начиная с версии 3.0 и с форматами DOCX и XLSX.


 
Прислал: Кольцов Роман Васильевич   Категория: Foxpro и другие приложения


* VFPCalcXYDiagram. Создание диаграмм для отчетов OpenOffice.org Calc 2.4+ средствами VFP
* (c) Koltsov Roman a.k.a. rvc44, Russia, Tambov, 25.02.2009, ICQ 44612299

* enum SymbolStyle determines what kind of symbol to use
* http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/SymbolStyle.html
#define ooSymbolStyleNONE 0 && The symbol is invisible
#define ooSymbolStyleAUTO 1 && The symbol is taken automatically
#define ooSymbolStyleSTANDARD 2 && uses one of the standard symbols. Which standard symbol is given in Symbol::StandardSymbol
#define ooSymbolStylePOLYGON 3 && uses the symbol given in the ::com::sun::star::drawing::PolyPolygonBezierCoords given in Symbol::PolygonCoords
#define ooSymbolStyleGRAPHIC 4 && uses the graphic given in Symbol::Graphic as symbol

PROCEDURE VfpCalcXYDiagram()
LOCAL oDesktop, cURL, aFileProperties[2], oDoc, oSheet, lcOOoVersion, oCalcCtrl

*-- Подключаем библиотеку
If AT("LibOOo", SET("PROCEDURE")) = 0
SET PROCEDURE TO LibOOo ADDITIVE
EndIf

*-- Проверяем установлен ли OpenOffice.org на компьютере
If OOoIsInstalled()
WAIT WINDOW NOWAIT "Запуск OpenOffice.org Calc. Ждите..."
Else
=MessageBox('Пакет OpenOffice.org не установлен на данном компьютере!',48,'Измените настройки программы')
Return .F.
EndIf

*-- Создаем новый документ OpenOffice.org Calc, подобно тому как это делается
*-- в библиотечной функции OOoOpenURL("private:factory/scalc"), но наш объект создается скрытым (Hidden)!
*-- Открытие документа происходит аналогично, но:
*-- cURL = "file:///" + CHRTRAN( sFileName, "\", "/" ), где sFileName полный путь к Вашему файлу и
*-- aFileProperties[2] = OOoPropertyValue( "FilterName", "MS Excel 97" )
oDesktop = OOoGetDesktop()
cURL = "private:factory/scalc"
aFileProperties[1] = OOoPropertyValue( "Hidden", .T. )
aFileProperties[2] = OOoPropertyValue( "ReadOnly", .F. )
oDoc = oDesktop.LoadComponentFromUrl( cURL, "_blank", 0, @ aFileProperties )
COMARRAY( oDoc, 10 )

*-- Получаем ссылку на доступ к параметрам уровня приложения
oView = oDoc.getCurrentController()

*-- Получаем ссылку на фрейм документа, свойство Title которого содержит заголовок окна с документом
oFrame = oView.getFrame()

*-- Прячем окно Calc'а чтобы пользователь не видел процесс заполнения
oWindow = oFrame.getContainerWindow()
oWindow.SetVisible(.F.)

*-- Получаем ссылку на первый лист в документе
oSheet = oDoc.getSheets().getByIndex( 0 )

*-- Предварительно установим массив исходных данных для построения двух XY-графиков на диаграмме
oSheet.getCellByPosition( 0, 0 ).setString( Chr(36) ) && Лист1.A1 && Chr(36) = "Бакс"
oSheet.getCellByPosition( 3, 0 ).setString( Chr(136) ) && Лист1.D1 && Chr(136) = "Евро"
*-- Зададим значения в диапазоне: Лист1.A3:A7
oSheet.getCellByPosition( 0, 2 ).setValue( 3 ) && Лист1.A3
oSheet.getCellByPosition( 0, 3 ).setValue( 4 ) && Лист1.A4
oSheet.getCellByPosition( 0, 4 ).setValue( 5 ) && Лист1.A5
oSheet.getCellByPosition( 0, 5 ).setValue( 6 ) && Лист1.A6
oSheet.getCellByPosition( 0, 6 ).setValue( 7 ) && Лист1.A7
*-- Зададим значения в диапазоне: Лист1.B3:B7
oSheet.getCellByPosition( 1, 2 ).setValue( 23 ) && Лист1.B3
oSheet.getCellByPosition( 1, 3 ).setValue( 45 ) && Лист1.B4
oSheet.getCellByPosition( 1, 4 ).setValue( 23 ) && Лист1.B5
oSheet.getCellByPosition( 1, 5 ).setValue( 45 ) && Лист1.B6
oSheet.getCellByPosition( 1, 6 ).setValue( 7 ) && Лист1.B7
*-- Зададим значения в диапазоне: Лист1.D3:D7
oSheet.getCellByPosition( 3, 2 ).setValue( 3 ) && Лист1.D3
oSheet.getCellByPosition( 3, 3 ).setValue( 4 ) && Лист1.D4
oSheet.getCellByPosition( 3, 4 ).setValue( 5 ) && Лист1.D5
oSheet.getCellByPosition( 3, 5 ).setValue( 6 ) && Лист1.D6
oSheet.getCellByPosition( 3, 6 ).setValue( 7 ) && Лист1.D7
*-- Зададим значения в диапазоне: Лист1.E3:E7
oSheet.getCellByPosition( 4, 2 ).setValue( 69 ) && Лист1.E3
oSheet.getCellByPosition( 4, 3 ).setValue( 11 ) && Лист1.E4
oSheet.getCellByPosition( 4, 4 ).setValue( 53 ) && Лист1.E5
oSheet.getCellByPosition( 4, 5 ).setValue( 60 ) && Лист1.E6
oSheet.getCellByPosition( 4, 6 ).setValue( 14 ) && Лист1.E7

*-- Добавление нескольких серий графиков в диаграмму Calc происходит довольно сложным способом.
*-- Скорее всего пересоздание всех серий проще, чем их модификация.
*-- Для создания диаграммы из нескольких серий графиков XY можно поступить так:
*-- 1. Создать диаграмму, содержащую один график (одну серию)
*-- 2. Добавить дополнительные серии или заменить все серии

*-- Returns the collection of charts
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/table/XTableChartsSupplier.html#getCharts
oCharts = oSheet.getCharts()

*-- Создадим структуру CellRangeAddress для указания двух диапазонов ячеек с данными для осей X и Y для серии 1:
*-- getCellRangeByPosition(StartColumn, StartRow, EndColumn, EndRow)
LOCAL aRange[2] && as com.sun.star.table.CellRangeAddress
aRange[1] = oSheet.getCellRangeByPosition(0,2,0,6).getRangeAddress() && Диапазон: Лист1.A3:A7
aRange[2] = oSheet.getCellRangeByPosition(1,2,1,6).getRangeAddress() && Диапазон: Лист1.B3:B7

*-- Создадим структуру Rectangle для задания положения и размера диаграммы на листе Calc/Excel
*-- Вместо отсутствующей в VFP функции CreateUnoStruct("com.sun.star.awt.Rectangle")
*-- воспользуемся функцией OOoCreateStruct из библиотеки поддержки OOo для VFP: LibOOo.prg
LOCAL oRectangle && as com.sun.star.awt.Rectangle
oRectangle = OOoCreateStruct( "com.sun.star.awt.Rectangle" )
With oRectangle
.X = 1300 && Позиция X на листе: 1,30 см
.Y = 11300 && Позиция Y на листе: 11,30 см
.Width = 7000 && Ширина диаграммы: 7,00 см
.Height = 5000 && Высота диаграммы: 5,00 см
EndWith

*-- Имя добавляемой диаграммы
sChartName = "Chart10"

*-- Если диаграммы на листе ещё нет
If !oCharts.hasByName(sChartName)
*-- Воспользуемся "мостиком автоматизации" для приведения массивов VFP к типу Sequence, используемому в OOo
ooServiceManager = OOoGetServiceManager()
unoWrap = ooServiceManager.Bridge_GetValueObject()
unoWrap.set("[]com.sun.star.table.CellRangeAddress", @aRange) && Здесь передавать по ссылке ОБЯЗАТЕЛЬНО!

*-- Добавим новую диаграмму
*-- Для VFP используйте предварительно подготовленный объект unoWrap вместо aRange, т.к. с последним не работает!
oCharts.addNewByName(sChartName, oRectangle, unoWrap, .F., .F.)

*-- oChart = oCharts.getByIndex( 0 ).getEmbeddedObject()
oChart = oCharts.getByName(sChartName).getEmbeddedObject()

*-- В будущем, SUN обещает поддержку программного доступа из StarBasic более чем к одной диаграмме,
*-- поэтому для получения ссылки на диаграмму, вместо метода getDiagram следует использовать getFirstDiagram
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XChartDocument.html#getFirstDiagram
oDiagram = oChart.getFirstDiagram()

*-- Изменим тип диаграммы
*-- Sets a new component that is able to create different chart type templates (components of type ChartTypeTemplate)
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XChartDocument.html#getChartTypeManager
oChartTypeManager = oChart.getChartTypeManager()
*-- service ChartTypeTemplate: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/ChartTypeTemplate.html
*-- interface XChartTypeTemplate: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XChartTypeTemplate.html
oChartTypeTemplate = oChartTypeManager.createInstance( "com.sun.star.chart2.template.ScatterLineSymbol" )
oChartTypeTemplate.changeDiagram(oDiagram)

*-- Получим систему координат (retrieve all coordinate systems)
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XCoordinateSystemContainer.html#getCoordinateSystems
oCooSyss = oDiagram.getCoordinateSystems()
*-- Проверим, что возвращен массив
*-- Такая проверка на массив работает только для VFP9! Подробнее:
*-- http://forum.foxclub.ru/read.php?29,329669,329669#msg-329669
*-- http://forum.foxclub.ru/read.php?29,336384,336390#msg-336390
If Type("oCooSyss",1) <> "A" OR VarType(oCooSyss[1]) <> "O"
=MessageBox('Не могу получить систему координат!',16,'Ошибка')
Return .F.
EndIf
oCooSys = oCooSyss[1]

*-- Получим тип диаграммы (retrieve all chart types)
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XChartTypeContainer.html#getChartTypes
oChartTypes = oCooSys.getChartTypes()
*-- Проверим, что возвращен массив
*-- Такая проверка на массив работает только для VFP9!
If Type("oChartTypes",1) <> "A" OR VarType(oChartTypes[1]) <> "O"
=MessageBox('Не могу получить тип диаграммы!',16,'Ошибка')
Return .F.
EndIf
oChartType = oChartTypes[1]

*-- Method getDataProvider() of interface XChartDocument returns the currently set data provider.
*-- This may be an internal one, if createInternalDataProvider has been called before,
*-- or an external one if ::XDataReceiver::attachDataProvider has been called.
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XChartDocument.html#getDataProvider
oDataProvider = oChart.getDataProvider()
*-- Method getDefaultColorScheme() of interface XDiagram sets an XColorScheme that defines the default
*-- colors for data series (or data points) in the diagram
*-- http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XDiagram.html#getDefaultColorScheme
oColorScheme = oDiagram.getDefaultColorScheme()

*-- Преобразуем координаты рядов данных из цифрового вида (Col1,Row1,Col2,Row2) в буквенный вида, типа: "A3:A7"

oRanges = oDoc.createInstance("com.sun.star.sheet.SheetCellRanges") && Иногда это называют: oSheetCellRanges

oRangeAddress = oSheet.getCellRangeByPosition(0,2,0,6).getRangeAddress() && Диапазон: Лист1.A3:A7
oRanges.addRangeAddress(oRangeAddress, .F.)
sRange1_X = oRanges.getRangeAddressesAsString() && sRange1_X = 'Лист1.A3:A7'
oRanges.removeRangeAddress(oRangeAddress) && removeRangeAddress в данном контексте делать надо обязательно!

oRangeAddress = oSheet.getCellRangeByPosition(1,2,1,6).getRangeAddress() && Диапазон: Лист1.B3:B7
oRanges.addRangeAddress(oRangeAddress, .F.)
sRange1_Y = oRanges.getRangeAddressesAsString() && sRange1_Y = 'Лист1.B3:B7'
oRanges.removeRangeAddress(oRangeAddress) && removeRangeAddress в данном контексте делать надо обязательно!

oRangeAddress = oSheet.getCellRangeByPosition(0,0,0,0).getRangeAddress() && Диапазон: Лист1.A1
oRanges.addRangeAddress(oRangeAddress, .F.)
sRange1_Label = oRanges.getRangeAddressesAsString() && sRange1_Label = 'Лист1.A1'
oRanges.removeRangeAddress(oRangeAddress) && removeRangeAddress в данном контексте делать надо обязательно!

oRangeAddress = oSheet.getCellRangeByPosition(3,2,3,6).getRangeAddress() && Диапазон: Лист1.D3:D7
oRanges.addRangeAddress(oRangeAddress, .F.)
sRange2_X = oRanges.getRangeAddressesAsString() && sRange2_X = 'Лист1.D3:D7'
oRanges.removeRangeAddress(oRangeAddress) && removeRangeAddress в данном контексте делать надо обязательно!

oRangeAddress = oSheet.getCellRangeByPosition(4,2,4,6).getRangeAddress() && Диапазон: Лист1.E3:E7
oRanges.addRangeAddress(oRangeAddress, .F.)
sRange2_Y = oRanges.getRangeAddressesAsString() && sRange2_Y = 'Лист1.E3:E7'
oRanges.removeRangeAddress(oRangeAddress) && removeRangeAddress в данном контексте делать надо обязательно!

oRangeAddress = oSheet.getCellRangeByPosition(3,0,3,0).getRangeAddress() && Диапазон: Лист1.D1
oRanges.addRangeAddress(oRangeAddress, .F.)
sRange2_Label = oRanges.getRangeAddressesAsString() && sRange2_Label = 'Лист1.D1'
oRanges.removeRangeAddress(oRangeAddress) && removeRangeAddress в данном контексте делать надо обязательно!

*-- creat series

*----- Этот блок можно оформить функцией: CreateDataSeries_XYDiagram(oDataProvider, sRange1_X, sRange1_Y, sRange1_Label )
*-- oSeries1 = CreateDataSeries_XYDiagram(oDataProvider, sRange1_X, sRange1_Y, sRange1_Label )

*-- Значения по оси X заданы в диапазоне sRange1_X = 'Лист1.A3:A7'
oDataX = OOoServiceManager_CreateInstance("com.sun.star.chart2.data.LabeledDataSequence") && createUnoService("com.sun.star.chart2.data.LabeledDataSequence")
oSequenceX = oDataProvider.createDataSequenceByRangeRepresentation(sRange1_X)
If !IsNull(oSequenceX)
oSequenceX.Role = "values-x"
oDataX.setValues(oSequenceX)
EndIf

*-- Значения по оси Y заданы в диапазоне sRange1_Y = 'Лист1.B3:B7'
oDataY = OOoServiceManager_CreateInstance("com.sun.star.chart2.data.LabeledDataSequence") && createUnoService("com.sun.star.chart2.data.LabeledDataSequence")
oSequenceY = oDataProvider.createDataSequenceByRangeRepresentation(sRange1_Y)
If !IsNull(oSequenceY)
oSequenceY.Role = "values-y"
oDataY.setValues(oSequenceY)
EndIf

*-- Установим легенду для серии 1. Возьмем ее текстовое значение из ячейки Лист1.A1
oSequenceLabel = oDataProvider.createDataSequenceByRangeRepresentation(sRange1_Label)
If !IsNull(oSequenceY)
oSequenceLabel.Role = ""
EndIf
oDataY.setLabel(oSequenceLabel)

*-- Имя aData[2] в VFP использовать нельзя, т.к. это имя зарезервировано!
LOCAL laData[2]
laData[1] = oDataY
laData[2] = oDataX

*-- Воспользуемся "мостиком автоматизации" для приведения массивов VFP к типу Sequence, используемому в OOo
*-- service LabeledDataSequence: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/data/LabeledDataSequence.html
*-- interface XLabeledDataSequence: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/data/XLabeledDataSequence.html
ooServiceManager = OOoGetServiceManager()
unoWrap = ooServiceManager.Bridge_GetValueObject()
unoWrap.set("[]com.sun.star.chart2.data.XLabeledDataSequence", @laData) && Здесь передавать по ссылке ОБЯЗАТЕЛЬНО!

oSeries1 = OOoServiceManager_CreateInstance("com.sun.star.chart2.DataSeries") && createUnoService("com.sun.star.chart2.DataSeries")
*-- Для VFP параметр - предварительно подготовленный объект unoWrap вместо laData, т.к. с последним не работает!
oSeries1.setData(unoWrap)
*----- конец блока 1 CreateDataSeries_XYDiagram

*----- Этот блок можно оформить функцией: CreateDataSeries_XYDiagram(oDataProvider, sRange2_X, sRange2_Y, sRange2_Label )
*-- oSeries2 = CreateDataSeries_XYDiagram(oDataProvider, sRange2_X, sRange2_Y, sRange2_Label )

*-- Значения по оси X заданы в диапазоне sRange2_X = 'Лист1.D3:D7'
oDataX = OOoServiceManager_CreateInstance("com.sun.star.chart2.data.LabeledDataSequence") && createUnoService("com.sun.star.chart2.data.LabeledDataSequence")
oSequenceX = oDataProvider.createDataSequenceByRangeRepresentation(sRange2_X)
If !IsNull(oSequenceX)
oSequenceX.Role = "values-x"
oDataX.setValues(oSequenceX)
EndIf

*-- Значения по оси Y заданы в диапазоне sRange2_Y = 'Лист1.E3:E7'
oDataY = OOoServiceManager_CreateInstance("com.sun.star.chart2.data.LabeledDataSequence") && createUnoService("com.sun.star.chart2.data.LabeledDataSequence")
oSequenceY = oDataProvider.createDataSequenceByRangeRepresentation(sRange2_Y)
If !IsNull(oSequenceY)
oSequenceY.Role = "values-y"
oDataY.setValues(oSequenceY)
EndIf

*-- Установим легенду для серии 2. Возьмем ее текстовое значение из ячейки Лист1.D1
oSequenceLabel = oDataProvider.createDataSequenceByRangeRepresentation(sRange2_Label)
If !IsNull(oSequenceY)
oSequenceLabel.Role = ""
EndIf
oDataY.setLabel(oSequenceLabel)

*-- Имя aData[2] в VFP использовать нельзя, т.к. это имя зарезервировано!
LOCAL laData2[2]
laData2[1] = oDataY
laData2[2] = oDataX

*-- Воспользуемся "мостиком автоматизации" для приведения массивов VFP к типу Sequence, используемому в OOo
*-- service LabeledDataSequence: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/data/LabeledDataSequence.html
*-- interface XLabeledDataSequence: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/data/XLabeledDataSequence.html
ooServiceManager = OOoGetServiceManager()
unoWrap = ooServiceManager.Bridge_GetValueObject()
unoWrap.set("[]com.sun.star.chart2.data.XLabeledDataSequence", @laData2) && Здесь передавать по ссылке ОБЯЗАТЕЛЬНО!

oSeries2 = OOoServiceManager_CreateInstance("com.sun.star.chart2.DataSeries") && createUnoService("com.sun.star.chart2.DataSeries")
*-- Для VFP параметр - предварительно подготовленный объект unoWrap вместо laData, т.к. с последним не работает!
oSeries2.setData(unoWrap)
*----- конец блока 2 CreateDataSeries_XYDiagram

*-- Установка свойств

*-- line colors taken from DefaultColorScheme
oSeries1.Color = oColorScheme.getColorByIndex(0) && 17798
oSeries2.Color = oColorScheme.getColorByIndex(1) && 16728590

*-- symbol description
*-- Вместо CreateUnoStruct("com.sun.star.chart2.Symbol") используем OOoCreateStruct для VFP из библиотеки LibOOo.prg
LOCAL oSymbol && as com.sun.star.chart2.Symbol
oSymbol = OOoCreateStruct( "com.sun.star.chart2.Symbol" )
oSymbol.Style = ooSymbolStyleSTANDARD && com.sun.star.chart2.SymbolStyle.STANDARD
oSize = OOoCreateStruct( "com.sun.star.awt.Size" )
oSize.Width = 250
oSize.Height = 250
oSymbol.Size = oSize && Весь блок равносилен: oSymbol.Size = OOoSize(250, 250)

oSeries1.Symbol = oSymbol
oSeries2.Symbol = oSymbol

*-- Вместо: oNewDataSeriesList = Array(oSeries1, oSeries2)
LOCAL oNewDataSeriesList[2]
oNewDataSeriesList[1] = oSeries1
oNewDataSeriesList[2] = oSeries2

*-- update with new []DataSeries
*-- Воспользуемся "мостиком автоматизации" для приведения массивов VFP к типу Sequence, используемому в OOo
*-- service DataSeries: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/DataSeries.html
*-- interface XDataSeries: http://api.openoffice.org/docs/common/ref/com/sun/star/chart2/XDataSeries.html
ooServiceManager = OOoGetServiceManager()
unoWrap = ooServiceManager.Bridge_GetValueObject()
unoWrap.set("[]com.sun.star.chart2.XDataSeries", @oNewDataSeriesList) && Здесь передавать по ссылке ОБЯЗАТЕЛЬНО!

*-- Для VFP параметр - предварительно подготовленный объект unoWrap вместо oNewDataSeriesList, т.к. с последним не работает!
oChartType.setDataSeries(unoWrap) && Вместо: oChartType.setDataSeries(oNewDataSeriesList)
EndIf

*-- Делаем сформированный отчет видимым
oWindow.SetVisible(.T.)

*-- Получаем заголовок текущего окна, но только после его сохранения
lsWinTitle = oFrame.getPropertyValue("Title")

*-- Выносим приложение Calc на передний план.
*-- Работа со скриптами отсутствует на Windows 98, поэтому используем TRY!
TRY
loShell = CreateObject("WScript.Shell")
loShell.AppActivate(lsWinTitle)
CATCH
ENDTRY

* Очистим Public-переменные во избежание появления ошибки RPC при повторном запуске
RELEASE goOOoDesktop, goOOoConfigProvider
ENDPROC

 
Сделайте оценку этого решения Плохо Удовлетворительно Так себе Хорошо Отлично Текущая оценка: (1.75) Вложение [5.25]kb
Дополнения пользователей
Создание диаграмм для отчетов OpenOffice.org Calc 2.4+ средствами VFP
[+][?]
[Дополнить]



© 2000-2017 Fox Club 
При размещении любых материалов с сайта на других ресурсах- прямая ссылка на www.foxclub.ru обязательна
Яндекс.Метрика