Bladeren bron

commit inicial

acampillo 11 maanden geleden
commit
457f00e792
100 gewijzigde bestanden met toevoegingen van 10460 en 0 verwijderingen
  1. 41 0
      .gitignore
  2. 29 0
      LICENSE.md
  3. 1 0
      README.md
  4. 92 0
      Vagrantfile
  5. 31 0
      assets/AppAsset.php
  6. 27 0
      codeception.yml
  7. 24 0
      commands/BdController.php
  8. 87 0
      composer.json
  9. 33 0
      config/__autocomplete.php
  10. 63 0
      config/console.php
  11. 5 0
      config/params.php
  12. 42 0
      config/test.php
  13. 6 0
      config/test_db.php
  14. 127 0
      config/web.php
  15. 18 0
      controllers/MailController.php
  16. 128 0
      controllers/SiteController.php
  17. 9 0
      docker-compose.yml
  18. 22 0
      mail/layouts/html.php
  19. 430 0
      mail/layouts/informatica/oficio-t2.php
  20. 434 0
      mail/layouts/informatica/oficio-t3.php
  21. 59 0
      migrations/m220310_234138_inicio.php
  22. 89 0
      migrations/m231028_182350_permisos.php
  23. 31 0
      migrations/m240507_043647_productos.php
  24. 96 0
      migrations/m240508_222050_condicionantes.php
  25. 64 0
      models/ColeccionPermiso.php
  26. 61 0
      models/ColeccionPermisoPermiso.php
  27. 76 0
      models/Media.php
  28. 26 0
      models/ModeloBase.php
  29. 55 0
      models/Modulo.php
  30. 77 0
      models/Permiso.php
  31. 63 0
      models/PermisoUsuario.php
  32. 73 0
      models/Producto.php
  33. 253 0
      models/Usuario.php
  34. 45 0
      models/UsuarioMedia.php
  35. 38 0
      modules/common/Module.php
  36. 159 0
      modules/common/data/Respuesta.php
  37. 72 0
      modules/common/models/Usuario.php
  38. 36 0
      modules/common/rest/AuthController.php
  39. 84 0
      modules/common/rest/JsonController.php
  40. 19 0
      modules/common/rest/Serializer.php
  41. 18 0
      modules/common/rest/UrlRule.php
  42. 38 0
      modules/excel/Module.php
  43. 25 0
      modules/excel/controllers/AcuseSolicitudController.php
  44. 69 0
      modules/excel/controllers/IncompetenciaSujetoController.php
  45. 24 0
      modules/excel/controllers/RecursoRevisionController.php
  46. 22 0
      modules/excel/controllers/RequerimientoAclaracionController.php
  47. 104 0
      modules/excel/controllers/SolicitudController.php
  48. 338 0
      modules/excel/views/acuse-solicitud/formato.php
  49. 50 0
      modules/excel/views/acuse-solicitud/oficio.php
  50. 26 0
      modules/excel/views/header/formato.php
  51. 23 0
      modules/excel/views/header/oficio-landing.php
  52. 25 0
      modules/excel/views/header/oficio.php
  53. 263 0
      modules/excel/views/incompetencia-sujeto/formato.php
  54. 40 0
      modules/excel/views/incompetencia-sujeto/oficio.php
  55. 264 0
      modules/excel/views/recurso-revision/formato.php
  56. 263 0
      modules/excel/views/requerimiento-aclaracion/formato.php
  57. 63 0
      modules/excel/views/requerimiento-aclaracion/oficio.php
  58. 156 0
      modules/excel/views/requerimiento-aclaracion/respuesta.php
  59. 562 0
      modules/excel/web/Controller.php
  60. 49 0
      modules/mail/Module.php
  61. 73 0
      modules/mail/README.md
  62. 96 0
      modules/mail/commands/CronController.php
  63. 46 0
      modules/mail/commands/NotificacionController.php
  64. 52 0
      modules/mail/controllers/NotificacionController.php
  65. 36 0
      modules/mail/controllers/VistaPreviaController.php
  66. 47 0
      modules/mail/migrations/m231004_195249_mailer.php
  67. 234 0
      modules/mail/models/NotificacionCorreo.php
  68. 56 0
      modules/mail/models/NotificacionCorreoAdjunto.php
  69. 0 0
      modules/mail/views/ejemplo/html.php
  70. 388 0
      modules/mail/views/layouts/cuerpo.php
  71. 22 0
      modules/mail/views/layouts/html.php
  72. 38 0
      modules/pdf/Module.php
  73. 115 0
      modules/pdf/controllers/AcuseSolicitudController.php
  74. 136 0
      modules/pdf/controllers/IncompetenciaSujetoController.php
  75. 109 0
      modules/pdf/controllers/RecursoRevisionController.php
  76. 141 0
      modules/pdf/controllers/RequerimientoAclaracionController.php
  77. 346 0
      modules/pdf/views/acuse-solicitud/formato.php
  78. 50 0
      modules/pdf/views/acuse-solicitud/oficio.php
  79. 26 0
      modules/pdf/views/header/formato.php
  80. 23 0
      modules/pdf/views/header/oficio-landing.php
  81. 25 0
      modules/pdf/views/header/oficio.php
  82. 263 0
      modules/pdf/views/incompetencia-sujeto/formato.php
  83. 40 0
      modules/pdf/views/incompetencia-sujeto/oficio.php
  84. 266 0
      modules/pdf/views/recurso-revision/formato.php
  85. 294 0
      modules/pdf/views/recurso-revision/manifiesto.php
  86. 263 0
      modules/pdf/views/requerimiento-aclaracion/formato.php
  87. 63 0
      modules/pdf/views/requerimiento-aclaracion/oficio.php
  88. 156 0
      modules/pdf/views/requerimiento-aclaracion/respuesta.php
  89. 540 0
      modules/pdf/web/Controller.php
  90. 37 0
      modules/publico/Module.php
  91. 170 0
      modules/publico/controllers/RecuperarContrasenaController.php
  92. 59 0
      modules/publico/controllers/SolicitudAcuseController.php
  93. 72 0
      modules/publico/views/recuperar-contrasena/correo.php
  94. 38 0
      modules/v1/Module.php
  95. 194 0
      modules/v1/controllers/AclaracionController.php
  96. 95 0
      modules/v1/controllers/AclaracionMediaController.php
  97. 96 0
      modules/v1/controllers/BitacoraEstatusController.php
  98. 115 0
      modules/v1/controllers/CalendarioController.php
  99. 93 0
      modules/v1/controllers/CategoriaController.php
  100. 0 0
      modules/v1/controllers/ColeccionPermisoController.php

+ 41 - 0
.gitignore

@@ -0,0 +1,41 @@
+# phpstorm project files
+.idea
+.vscode
+
+# netbeans project files
+nbproject
+
+# zend studio for eclipse project files
+.buildpath
+.project
+.settings
+
+# windows thumbnail cache
+Thumbs.db
+
+# composer vendor dir
+/vendor
+
+# composer itself is not needed
+composer.phar
+composer.lock
+
+# Mac DS_Store Files
+.DS_Store
+
+# phpunit itself is not needed
+phpunit.phar
+# local phpunit config
+/phpunit.xml
+
+tests/_output/*
+tests/_support/_generated
+
+#vagrant folder
+/.vagrant
+
+#base de datos
+config/db.php
+
+#composer.lock
+composer.lock

+ 29 - 0
LICENSE.md

@@ -0,0 +1,29 @@
+Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+ * Neither the name of Yii Software LLC nor the names of its
+   contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 1 - 0
README.md

@@ -0,0 +1 @@
+### Aquí se puede guardar detalles del proyecto

+ 92 - 0
Vagrantfile

@@ -0,0 +1,92 @@
+require 'yaml'
+require 'fileutils'
+
+required_plugins_installed = nil
+required_plugins = %w( vagrant-hostmanager vagrant-vbguest )
+required_plugins.each do |plugin|
+  unless Vagrant.has_plugin? plugin
+    system "vagrant plugin install #{plugin}"
+    required_plugins_installed = true
+  end
+end
+
+# IF plugin[s] was just installed - restart required
+if required_plugins_installed
+  # Get CLI command[s] and call again
+  system 'vagrant' + ARGV.to_s.gsub(/\[\"|\", \"|\"\]/, ' ')
+  exit
+end
+
+domains = {
+  app: 'yii2basic.test'
+}
+
+vagrantfile_dir_path = File.dirname(__FILE__)
+
+config = {
+  local: vagrantfile_dir_path + '/vagrant/config/vagrant-local.yml',
+  example: vagrantfile_dir_path + '/vagrant/config/vagrant-local.example.yml'
+}
+
+# copy config from example if local config not exists
+FileUtils.cp config[:example], config[:local] unless File.exist?(config[:local])
+# read config
+options = YAML.load_file config[:local]
+
+# check github token
+if options['github_token'].nil? || options['github_token'].to_s.length != 40
+  puts "You must place REAL GitHub token into configuration:\n/yii2-app-basic/vagrant/config/vagrant-local.yml"
+  exit
+end
+
+# vagrant configurate
+Vagrant.configure(2) do |config|
+  # select the box
+  config.vm.box = 'bento/ubuntu-18.04'
+
+  # should we ask about box updates?
+  config.vm.box_check_update = options['box_check_update']
+
+  config.vm.provider 'virtualbox' do |vb|
+    # machine cpus count
+    vb.cpus = options['cpus']
+    # machine memory size
+    vb.memory = options['memory']
+    # machine name (for VirtualBox UI)
+    vb.name = options['machine_name']
+  end
+
+  # machine name (for vagrant console)
+  config.vm.define options['machine_name']
+
+  # machine name (for guest machine console)
+  config.vm.hostname = options['machine_name']
+
+  # network settings
+  config.vm.network 'private_network', ip: options['ip']
+
+  # sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine)
+  config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant'
+
+  # disable folder '/vagrant' (guest machine)
+  config.vm.synced_folder '.', '/vagrant', disabled: true
+
+  # hosts settings (host machine)
+  config.vm.provision :hostmanager
+  config.hostmanager.enabled            = true
+  config.hostmanager.manage_host        = true
+  config.hostmanager.ignore_private_ip  = false
+  config.hostmanager.include_offline    = true
+  config.hostmanager.aliases            = domains.values
+
+  # quick fix for failed guest additions installations
+  # config.vbguest.auto_update = false
+
+  # provisioners
+  config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone'], options['ip']]
+  config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false
+  config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always'
+
+  # post-install message (vagrant console)
+  config.vm.post_up_message = "App URL: http://#{domains[:app]}"
+end

+ 31 - 0
assets/AppAsset.php

@@ -0,0 +1,31 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace app\assets;
+
+use yii\web\AssetBundle;
+
+/**
+ * Main application asset bundle.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class AppAsset extends AssetBundle
+{
+    public $basePath = '@webroot';
+    public $baseUrl = '@web';
+    public $css = [
+        'css/site.css',
+    ];
+    public $js = [
+    ];
+    public $depends = [
+        'yii\web\YiiAsset',
+        'yii\bootstrap4\BootstrapAsset',
+    ];
+}

+ 27 - 0
codeception.yml

@@ -0,0 +1,27 @@
+actor: Tester
+bootstrap: _bootstrap.php
+paths:
+    tests: tests
+    log: tests/_output
+    data: tests/_data
+    helpers: tests/_support
+settings:
+    memory_limit: 1024M
+    colors: true
+modules:
+    config:
+        Yii2:
+            configFile: 'config/test.php'
+
+# To enable code coverage:
+#coverage:
+#    #c3_url: http://localhost:8080/index-test.php/
+#    enabled: true
+#    #remote: true
+#    #remote_config: '../codeception.yml'
+#    whitelist:
+#        include:
+#            - models/*
+#            - controllers/*
+#            - commands/*
+#            - mail/*

+ 24 - 0
commands/BdController.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace app\commands;
+
+use yii\console\Controller;
+use yii\console\ExitCode;
+
+class BdController extends Controller {
+
+  public function actionInsertarUsuario() {
+    $usuario = new \app\models\Usuario();
+    $usuario->uuid();
+    $usuario->correo = "soporte@edesarrollos.com";
+    $usuario->agregarClave("Edes@rrollos2024");
+    $usuario->nombre = "Soporte Técnico";
+    $usuario->rol = "admin";
+    $usuario->telefono = "1234567890";
+    if(!$usuario->save()) {
+      $this->stdout(json_encode($usuario->getFirstErrors()));
+    }
+    return ExitCode::OK;
+  }
+
+}

+ 87 - 0
composer.json

@@ -0,0 +1,87 @@
+{
+    "name": "yiisoft/yii2-app-basic",
+    "description": "Yii 2 Basic Project Template",
+    "keywords": ["yii2", "framework", "basic", "project template"],
+    "homepage": "http://www.yiiframework.com/",
+    "type": "project",
+    "license": "BSD-3-Clause",
+    "support": {
+        "issues": "https://github.com/yiisoft/yii2/issues?state=open",
+        "forum": "http://www.yiiframework.com/forum/",
+        "wiki": "http://www.yiiframework.com/wiki/",
+        "irc": "irc://irc.freenode.net/yii",
+        "source": "https://github.com/yiisoft/yii2"
+    },
+    "minimum-stability": "stable",
+    "require": {
+        "php": ">=5.6.0",
+        "yiisoft/yii2": "~2.0.14",
+        "yiisoft/yii2-bootstrap4": "~2.0.0",
+        "yiisoft/yii2-swiftmailer": "~2.0.0 || ~2.1.0",
+        "firebase/php-jwt": "^5.4",
+        "ramsey/uuid": "^4.2",
+        "phpoffice/phpspreadsheet": "^1.18",
+        "mpdf/mpdf": "^8.0",
+        "phpoffice/phpword": "^1.1"
+    },
+    "require-dev": {
+        "yiisoft/yii2-debug": "~2.1.0",
+        "yiisoft/yii2-gii": "~2.2.0",
+        "yiisoft/yii2-faker": "~2.0.0",
+        "codeception/codeception": "^4.0",
+        "codeception/verify": "~0.5.0 || ~1.1.0",
+        "codeception/specify": "~0.4.6",
+        "symfony/browser-kit": ">=2.7 <=4.2.4",
+        "codeception/module-filesystem": "^1.0.0",
+        "codeception/module-yii2": "^1.0.0",
+        "codeception/module-asserts": "^1.0.0"
+    },
+    "config": {
+        "process-timeout": 1800,
+        "fxp-asset": {
+            "enabled": false
+        },
+        "allow-plugins": {
+            "yiisoft/yii2-composer": true
+        }
+    },
+    "scripts": {
+        "post-install-cmd": [
+            "yii\\composer\\Installer::postInstall"
+        ],
+        "post-create-project-cmd": [
+            "yii\\composer\\Installer::postCreateProject",
+            "yii\\composer\\Installer::postInstall"
+        ]
+    },
+    "extra": {
+        "yii\\composer\\Installer::postCreateProject": {
+            "setPermission": [
+                {
+                    "runtime": "0777",
+                    "web/assets": "0777",
+                    "yii": "0755"
+                }
+            ]
+        },
+        "yii\\composer\\Installer::postInstall": {
+            "generateCookieValidationKey": [
+                "config/web.php"
+            ]
+        }
+    },
+    "repositories": [
+        {
+            "type": "composer",
+            "url": "https://asset-packagist.org"
+        }
+    ],
+    "autoload": {
+        "psr-4": {
+            "common\\": "modules/common",
+            "v1\\": "modules/v1",
+            "pdf\\": "modules/pdf",
+            "excel\\": "modules/excel"
+        }
+    }
+}

+ 33 - 0
config/__autocomplete.php

@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * This class only exists here for IDE (PHPStorm/Netbeans/...) autocompletion.
+ * This file is never included anywhere.
+ * Adjust this file to match classes configured in your application config, to enable IDE autocompletion for custom components.
+ * Example: A property phpdoc can be added in `__Application` class as `@property \vendor\package\Rollbar|__Rollbar $rollbar` and adding a class in this file
+ * ```php
+ * // @property of \vendor\package\Rollbar goes here
+ * class __Rollbar {
+ * }
+ * ```
+ */
+class Yii {
+    /**
+     * @var \yii\web\Application|\yii\console\Application|__Application
+     */
+    public static $app;
+}
+
+/**
+ * @property yii\rbac\DbManager $authManager 
+ * @property \yii\web\User|__WebUser $user
+ * 
+ */
+class __Application {
+}
+
+/**
+ * @property app\models\User $identity
+ */
+class __WebUser {
+}

+ 63 - 0
config/console.php

@@ -0,0 +1,63 @@
+<?php
+
+$params = require __DIR__ . '/params.php';
+$db = require __DIR__ . '/db.php';
+
+$config = [
+  'id' => 'basic-console',
+  'basePath' => dirname(__DIR__),
+  'bootstrap' => ['log', 'mail'],
+  'controllerNamespace' => 'app\commands',
+  'aliases' => [
+    '@bower' => '@vendor/bower-asset',
+    '@npm'   => '@vendor/npm-asset',
+    '@tests' => '@app/tests',
+  ],
+  'components' => [
+    'cache' => [
+      'class' => 'yii\caching\FileCache',
+    ],
+    'log' => [
+      'targets' => [
+        [
+          'class' => 'yii\log\FileTarget',
+          'levels' => ['error', 'warning'],
+        ],
+      ],
+    ],
+    'mailer' => [
+      'class' => 'yii\swiftmailer\Mailer',
+      'useFileTransport' => false,
+      'transport' => [
+        'class' => 'Swift_SmtpTransport',
+        'host' => 'smtp.gmail.com',
+        'username' => 'transparenciasonora22@gmail.com',
+        'password' => 'kzzeysxfeknljkbi',
+        'port' => '587',
+        'encryption' => 'tls',
+      ],
+    ],
+    'db' => $db,
+  ],
+  'params' => $params,
+  'modules' => [
+    'mail' => ['class' => 'app\modules\mail\Module']
+  ],
+  /*
+  'controllerMap' => [
+      'fixture' => [ // Fixture generation command line.
+          'class' => 'yii\faker\FixtureController',
+      ],
+  ],
+  */
+];
+
+if (YII_ENV_DEV) {
+  // configuration adjustments for 'dev' environment
+  $config['bootstrap'][] = 'gii';
+  $config['modules']['gii'] = [
+    'class' => 'yii\gii\Module',
+  ];
+}
+
+return $config;

+ 5 - 0
config/params.php

@@ -0,0 +1,5 @@
+<?php
+
+return [
+  'jwt.key' => 'G3zZ9uE4WrysxcNr53lS',
+];

+ 42 - 0
config/test.php

@@ -0,0 +1,42 @@
+<?php
+$params = require __DIR__ . '/params.php';
+$db = require __DIR__ . '/test_db.php';
+
+/**
+ * Application configuration shared by all test types
+ */
+return [
+    'id' => 'basic-tests',
+    'basePath' => dirname(__DIR__),
+    'aliases' => [
+        '@bower' => '@vendor/bower-asset',
+        '@npm'   => '@vendor/npm-asset',
+    ],
+    'language' => 'en-US',
+    'components' => [
+        'db' => $db,
+        'mailer' => [
+            'useFileTransport' => true,
+        ],
+        'assetManager' => [
+            'basePath' => __DIR__ . '/../web/assets',
+        ],
+        'urlManager' => [
+            'showScriptName' => true,
+        ],
+        'user' => [
+            'identityClass' => 'app\models\User',
+        ],
+        'request' => [
+            'cookieValidationKey' => 'test',
+            'enableCsrfValidation' => false,
+            // but if you absolutely need it set cookie domain to localhost
+            /*
+            'csrfCookie' => [
+                'domain' => 'localhost',
+            ],
+            */
+        ],
+    ],
+    'params' => $params,
+];

+ 6 - 0
config/test_db.php

@@ -0,0 +1,6 @@
+<?php
+$db = require __DIR__ . '/db.php';
+// test database! Important not to run tests on production or development databases
+$db['dsn'] = 'mysql:host=localhost;dbname=yii2basic_test';
+
+return $db;

+ 127 - 0
config/web.php

@@ -0,0 +1,127 @@
+<?php
+
+use function PHPSTORM_META\map;
+
+$params = require __DIR__ . '/params.php';
+$db = require __DIR__ . '/db.php';
+
+$config = [
+  'id' => 'basic',
+  'basePath' => dirname(__DIR__),
+  'language' => 'es',
+  'bootstrap' => ['log'],
+  'aliases' => [
+    '@bower' => '@vendor/bower-asset',
+    '@npm'   => '@vendor/npm-asset',
+  ],
+  'components' => [
+    'request' => [
+      'cookieValidationKey' => 'MwDzyBzzBQMMA8d_tiCkX-h5oMhAg8jK',
+      'parsers' => [
+        'application/json' => 'yii\web\JsonParser',
+      ],
+    ],
+    'cache' => [
+      'class' => 'yii\caching\FileCache',
+    ],
+    'user' => [
+      'identityClass' => 'app\models\User',
+      'enableAutoLogin' => true,
+    ],
+    'errorHandler' => [
+      'errorAction' => 'site/error',
+    ],
+    'mailer' => [
+      'class' => 'yii\swiftmailer\Mailer',
+      'useFileTransport' => false,
+      'transport' => [
+        'class' => 'Swift_SmtpTransport',
+        'host' => 'smtp.gmail.com',
+        'username' => 'correo@gmail.com',
+        'password' => 'contraseña',
+        'port' => '587',
+        'encryption' => 'tls',
+      ],
+    ],
+    'log' => [
+      'traceLevel' => YII_DEBUG ? 3 : 0,
+      'targets' => [
+        [
+          'class' => 'yii\log\FileTarget',
+          'levels' => ['error', 'warning'],
+        ],
+      ],
+    ],
+    'db' => $db,
+    'urlManager' => [
+      'enablePrettyUrl' => true,
+      'showScriptName' => false,
+      'rules' => [
+        [
+          'class' => 'common\rest\UrlRule',
+          'controller' => [
+            'v1/perfil',
+            'v1/usuario',
+            'v1/media',
+            'v1/pais',
+            'v1/tema-solicitud',
+            'v1/subtema-solicitud',
+            'v1/ModalidadEntrega',
+            'v1/estado',
+            'v1/municipio',
+            'v1/ocupacion',
+            'v1/particular',
+            'v1/estatus',
+            'v1/sujeto-obligado',
+            'v1/solicitud',
+            'v1/modulo',
+            'v1/permiso',
+            'v1/coleccion-permiso',
+            'v1/solicitud',
+            'v1/solicitud-expediente',
+            'v1/bitacora-estatus',
+            'v1/categoria',
+            'v1/subir-archivo',
+            'v1/temas',
+            'v1/calendario',
+            'v1/aclaracion',
+            'v1/motivos-inconformidad',
+            'v1/recurso-revision',
+            'v1/mensaje',
+            'v1/estatus-solicitud',
+            'v1/calendario',
+            'v1/solicitud-acuse',
+            'v1/ponencia',
+          ],
+        ]
+      ],
+    ],
+  ],
+  'params' => $params,
+  'modules' => [
+    'v1' => ['class' => 'v1\Module'],
+    'pdf' => ['class' => 'pdf\Module'],
+    'excel' => ['class' => 'excel\Module'],
+    'mail' => ['class' => 'app\modules\mail\Module'],
+    'publico' => ['class' => 'app\modules\publico\Module'],
+  ]
+];
+
+if (YII_ENV_DEV) {
+  // configuration adjustments for 'dev' environment
+  $config['bootstrap'][] = 'debug';
+  $config['modules']['debug'] = [
+    'class' => 'yii\debug\Module',
+    // uncomment the following to add your IP if you are not connecting from localhost.
+    //'allowedIPs' => ['127.0.0.1', '::1'],
+  ];
+
+  $config['bootstrap'][] = 'gii';
+  $config['modules']['gii'] = [
+    'class' => 'yii\gii\Module',
+    // uncomment the following to add your IP if you are not connecting from localhost.
+    //'allowedIPs' => ['127.0.0.1', '::1'],
+  ];
+}
+
+return $config;

+ 18 - 0
controllers/MailController.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace app\controllers;
+
+class MailController extends \yii\web\Controller {
+
+  public function actionOficioInformatica() {
+    return $this->actionOficioInformaticaT2();
+  }
+
+  public function actionOficioInformaticaT2() {
+    return $this->renderPartial("@app/mail/informatica/oficio-t2");
+  }
+
+  public function actionOficioInformaticaT3() {
+    return $this->renderPartial("@app/mail/informatica/oficio-t3");
+  }
+}

+ 128 - 0
controllers/SiteController.php

@@ -0,0 +1,128 @@
+<?php
+
+namespace app\controllers;
+
+use Yii;
+use yii\filters\AccessControl;
+use yii\web\Controller;
+use yii\web\Response;
+use yii\filters\VerbFilter;
+use app\models\LoginForm;
+use app\models\ContactForm;
+
+class SiteController extends Controller
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function behaviors()
+    {
+        return [
+            'access' => [
+                'class' => AccessControl::className(),
+                'only' => ['logout'],
+                'rules' => [
+                    [
+                        'actions' => ['logout'],
+                        'allow' => true,
+                        'roles' => ['@'],
+                    ],
+                ],
+            ],
+            'verbs' => [
+                'class' => VerbFilter::className(),
+                'actions' => [
+                    'logout' => ['post'],
+                ],
+            ],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function actions()
+    {
+        return [
+            'error' => [
+                'class' => 'yii\web\ErrorAction',
+            ],
+            'captcha' => [
+                'class' => 'yii\captcha\CaptchaAction',
+                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
+            ],
+        ];
+    }
+
+    /**
+     * Displays homepage.
+     *
+     * @return string
+     */
+    public function actionIndex()
+    {
+        return $this->render('index');
+    }
+
+    /**
+     * Login action.
+     *
+     * @return Response|string
+     */
+    public function actionLogin()
+    {
+        if (!Yii::$app->user->isGuest) {
+            return $this->goHome();
+        }
+
+        $model = new LoginForm();
+        if ($model->load(Yii::$app->request->post()) && $model->login()) {
+            return $this->goBack();
+        }
+
+        $model->password = '';
+        return $this->render('login', [
+            'model' => $model,
+        ]);
+    }
+
+    /**
+     * Logout action.
+     *
+     * @return Response
+     */
+    public function actionLogout()
+    {
+        Yii::$app->user->logout();
+
+        return $this->goHome();
+    }
+
+    /**
+     * Displays contact page.
+     *
+     * @return Response|string
+     */
+    public function actionContact()
+    {
+        $model = new ContactForm();
+        if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
+            Yii::$app->session->setFlash('contactFormSubmitted');
+
+            return $this->refresh();
+        }
+        return $this->render('contact', [
+            'model' => $model,
+        ]);
+    }
+
+    /**
+     * Displays about page.
+     *
+     * @return string
+     */
+    public function actionAbout()
+    {
+        return $this->render('about');
+    }
+}

+ 9 - 0
docker-compose.yml

@@ -0,0 +1,9 @@
+version: '2'
+services:
+  php:
+    image: yiisoftware/yii2-php:7.4-apache
+    volumes:
+      - ~/.composer-docker/cache:/root/.composer/cache:delegated
+      - ./:/app:delegated
+    ports:
+      - '8000:80'

+ 22 - 0
mail/layouts/html.php

@@ -0,0 +1,22 @@
+<?php
+use yii\helpers\Html;
+
+/** @var \yii\web\View $this view component instance */
+/** @var \yii\mail\MessageInterface $message the message being composed */
+/** @var string $content main view render result */
+?>
+<?php $this->beginPage() ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
+    <title><?= Html::encode($this->title) ?></title>
+    <?php $this->head() ?>
+</head>
+<body>
+    <?php $this->beginBody() ?>
+    <?= $content ?>
+    <?php $this->endBody() ?>
+</body>
+</html>
+<?php $this->endPage() ?>

File diff suppressed because it is too large
+ 430 - 0
mail/layouts/informatica/oficio-t2.php


File diff suppressed because it is too large
+ 434 - 0
mail/layouts/informatica/oficio-t3.php


+ 59 - 0
migrations/m220310_234138_inicio.php

@@ -0,0 +1,59 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m220310_234138_inicio
+ */
+class m220310_234138_inicio extends Migration {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function safeUp() {
+
+    $this->createTable('Usuario', [
+      "id" => $this->string(36),
+      "correo" => $this->string(100)->notNull(),
+      "clave" => $this->string(100)->notNull(),
+      "nombre" => $this->string(100)->notNull(),
+      "estatus" => $this->smallInteger()->comment("0:inactivo, 1:activo"),
+      "telefono" => $this->string(100)->notNull(),
+      "alias" => $this->string(100),
+      "foto" => $this->string(300),
+      "rol" => $this->string(100)->notNull(),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("UsuarioPK", "Usuario", "id");
+
+    $this->createTable('Media', [
+      "id" => $this->string(36),
+      "idUsuario" => $this->string(36),
+      "nombre" => $this->string(100)->notNull(),
+      "extension" => $this->string(5),
+      "size" => $this->string(100),
+      "mimetype" => $this->string(100),
+      "ruta" => $this->string(100),
+      "descripcion" => $this->string(500),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("MediaPK", "Media", "id");
+    $this->addForeignKey("MediaIdUsuarioFK", "Media", "idUsuario", "Usuario", "id");
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function safeDown() {
+    $this->dropTable('Media');
+    $this->dropTable('Usuario');
+  }
+
+}

+ 89 - 0
migrations/m231028_182350_permisos.php

@@ -0,0 +1,89 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m231028_182350_permisos
+ */
+class m231028_182350_permisos extends Migration {
+
+  public function safeUp() {
+    $this->createTable('Modulo', [
+      'id' => $this->string(50),
+      'nombre' => $this->string(100),
+      'creado' => $this->timestamp() . ' with time zone',
+      'modificado' => $this->timestamp() . ' with time zone',
+      'eliminado' => $this->timestamp() . ' with time zone'
+    ]);
+
+    $this->addPrimaryKey('pk_Modulo', 'Modulo', 'id');
+
+    $this->createTable('Permiso', [
+      'id' => $this->string(50),
+      'idModulo' => $this->string(50),
+      'nombre' => $this->string(100),
+      'descripcion' => $this->string(100),
+      'creado' => $this->timestamp() . ' with time zone',
+      'modificado' => $this->timestamp() . ' with time zone',
+      'eliminado' => $this->timestamp() . ' with time zone'
+    ]);
+
+    $this->addPrimaryKey('pk_Permiso', 'Permiso', 'id');
+    $this->addForeignKey('fk_Permiso_Modulo', 'Permiso', 'idModulo', 'Modulo', 'id');
+
+    $this->createTable('PermisoUsuario', [
+      'id' => $this->string(50),
+      'idUsuario' => $this->string(50),
+      'idPermiso' => $this->string(50),
+      'asignado' => $this->timestamp() . ' with time zone',
+      'modificado' => $this->timestamp() . ' with time zone',
+      'eliminado' => $this->timestamp() . ' with time zone'
+    ]);
+
+    $this->addPrimaryKey('pk_PermisoUsuario', 'PermisoUsuario', 'id');
+    $this->addForeignKey('fk_PermisoUsuario_Usuario', 'PermisoUsuario', 'idUsuario', 'Usuario', 'id');
+    $this->addForeignKey('fk_PermisoUsuario_Permiso', 'PermisoUsuario', 'idPermiso', 'Permiso', 'id');
+
+    $this->createTable('ColeccionPermiso', [
+      'id' => $this->string(50),
+      'clave' => $this->string(50),
+      "nombre" => $this->string(100),
+      "descripcion" => $this->string(100),
+      "creado" => $this->timestamp() . ' with time zone',
+      "modificado" => $this->timestamp() . ' with time zone',
+      "eliminado" => $this->timestamp() . ' with time zone'
+    ]);
+
+    $this->addPrimaryKey('pk_coleccion_permiso', 'ColeccionPermiso', 'id');
+
+    $this->createTable('ColeccionPermisoPermiso', [
+      'idColeccion' => $this->string(50),
+      'idPermiso' => $this->string(50),
+      "creado" => $this->timestamp() . ' with time zone',
+      "modificado" => $this->timestamp() . ' with time zone',
+      "eliminado" => $this->timestamp() . ' with time zone'
+    ]);
+    $this->addPrimaryKey('pk_coleccion_permiso_permiso', 'ColeccionPermisoPermiso', ['idColeccion', 'idPermiso']);
+
+    $this->addForeignKey('fk_coleccion_permiso_permiso_coleccion', 'ColeccionPermisoPermiso', 'idColeccion', 'ColeccionPermiso', 'id');
+    $this->addForeignKey('fk_coleccion_permiso_permiso_permiso', 'ColeccionPermisoPermiso', 'idPermiso', 'Permiso', 'id');
+  }
+
+  public function safeDown() {
+
+    $this->dropForeignKey("fk_coleccion_permiso_permiso_coleccion", "ColeccionPermisoPermiso");
+    $this->dropForeignKey("fk_coleccion_permiso_permiso_permiso", "ColeccionPermisoPermiso");
+    $this->dropPrimaryKey("pk_coleccion_permiso_permiso", "ColeccionPermisoPermiso");
+    $this->dropTable("ColeccionPermisoPermiso");
+
+    $this->dropPrimaryKey("pk_coleccion_permiso", "ColeccionPermiso");
+    $this->dropTable("ColeccionPermiso");
+
+    $this->dropForeignKey('fk_PermisoUsuario_Permiso', 'PermisoUsuario');
+    $this->dropForeignKey('fk_PermisoUsuario_Usuario', 'PermisoUsuario');
+    $this->dropForeignKey('fk_Permiso_Modulo', 'Permiso');
+    $this->dropTable('PermisoUsuario');
+    $this->dropTable('Permiso');
+    $this->dropTable('Modulo');
+  }
+}

+ 31 - 0
migrations/m240507_043647_productos.php

@@ -0,0 +1,31 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m240507_043647_productos
+ */
+class m240507_043647_productos extends Migration {
+  /**
+   * {@inheritdoc}
+   */
+  public function safeUp() {
+    $this->createTable("Producto", [
+      "id" => $this->string(36),
+      "nombre" => $this->string(100),
+      "idPadre" => $this->string(36),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("ProductoPK", "Producto", "id");
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function safeDown() {
+    $this->dropTable("Producto");
+  }
+}

+ 96 - 0
migrations/m240508_222050_condicionantes.php

@@ -0,0 +1,96 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m240508_222050_condicionantes
+ */
+class m240508_222050_condicionantes extends Migration {
+  /**
+   * {@inheritdoc}
+   */
+  public function safeUp() {
+    $this->createTable("Condicionante", [
+      "id" => $this->string(36),
+      "titulo" => $this->string(100),
+      "descripcion" => $this->text(),
+      "activa" => $this->boolean(),
+      "amplitud" => $this->string(50),
+      "idProducto" => $this->string(36),
+      "subproductos" => $this->text(),
+      "idCreador" => $this->string(36),
+      "idModificador" => $this->string(36),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("CondicionantePK", "Condicionante", "id");
+    $this->addForeignKey("CondicionanteProductoFK", "Condicionante", "idProducto", "Producto", "id");
+    $this->addForeignKey("CondicionanteCreadorFK", "Condicionante", "idCreador", "Usuario", "id");
+    $this->addForeignKey("CondicionanteModificadorFK", "Condicionante", "idModificador", "Usuario", "id");
+
+    $this->createTable("CondicionanteFin", [
+      "idFin" => $this->string(36),
+      "idCondicionante" => $this->string(36),
+      "creado" => $this->dateTime(),
+      "modificado" => $this->dateTime(),
+      "eliminado" => $this->dateTime(),
+    ]);
+
+    $this->addPrimaryKey("CondicionanteFinPK", "CondicionanteFin", ["idFin", "idCondicionante"]);
+
+    $this->createTable("CondicionanteTipo", [
+      "idTipo" => $this->string(36),
+      "idCondicionante" => $this->string(36),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("CondicionanteTipoPK", "CondicionanteTipo", ["idTipo", "idCondicionante"]);
+
+    $this->createTable("CondicionanteOrigen", [
+      "idOrigen" => $this->string(36),
+      "idCondicionante" => $this->string(36),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("CondicionanteOrigenPK", "CondicionanteOrigen", ["idOrigen", "idCondicionante"]);
+
+    $this->createTable("CondicionanteDestino", [
+      "idDestino" => $this->string(36),
+      "idCondicionante" => $this->string(36),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("CondicionanteDestinoPK", "CondicionanteDestino", ["idDestino", "idCondicionante"]);
+
+    $this->createTable("CondicionanteCondicionante", [
+      "id" => $this->string(36),
+      "idCondicionante" => $this->string(36),
+      "nombre" => $this->string(100),
+      "creado" => $this->timestamp(). " with time zone",
+      "modificado" => $this->timestamp(). " with time zone",
+      "eliminado" => $this->timestamp(). " with time zone",
+    ]);
+
+    $this->addPrimaryKey("CondicionanteCondicionantePK", "CondicionanteCondicionante", "id");
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function safeDown() {
+    $this->dropTable("CondicionanteCondicionante");
+    $this->dropTable("CondicionanteDestino");
+    $this->dropTable("CondicionanteOrigen");
+    $this->dropTable("CondicionanteTipo");
+    $this->dropTable("CondicionanteFin");
+    $this->dropTable("Condicionante");
+  }
+}

+ 64 - 0
models/ColeccionPermiso.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "ColeccionPermiso".
+ *
+ * @property string $id
+ * @property string|null $clave
+ * @property string|null $nombre
+ * @property string|null $descripcion
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property ColeccionPermisoPermiso[] $coleccionPermisoPermisos
+ * @property Permiso[] $idPermisos
+ */
+class ColeccionPermiso extends ModeloBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'ColeccionPermiso';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['id'], 'required'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['id', 'clave'], 'string', 'max' => 50],
+      [['nombre', 'descripcion'], 'string', 'max' => 100],
+      [['id'], 'unique'],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'clave' => 'Clave',
+      'nombre' => 'Nombre',
+      'descripcion' => 'Descripcion',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  public function getColecciones() {
+    return $this->hasMany(ColeccionPermisoPermiso::class, ['idColeccion' => 'id']);
+  }
+
+  public function getPermisos() {
+    return $this->hasMany(Permiso::class, ['id' => 'idPermiso'])->viaTable('ColeccionPermisoPermiso', ['idColeccion' => 'id']);
+  }
+}

+ 61 - 0
models/ColeccionPermisoPermiso.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "ColeccionPermisoPermiso".
+ *
+ * @property string $idColeccion
+ * @property string $idPermiso
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property ColeccionPermiso $idColeccion0
+ * @property Permiso $idPermiso0
+ */
+class ColeccionPermisoPermiso extends ModeloBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'ColeccionPermisoPermiso';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['idColeccion', 'idPermiso'], 'required'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['idColeccion', 'idPermiso'], 'string', 'max' => 50],
+      [['idColeccion', 'idPermiso'], 'unique', 'targetAttribute' => ['idColeccion', 'idPermiso']],
+      [['idColeccion'], 'exist', 'skipOnError' => true, 'targetClass' => ColeccionPermiso::class, 'targetAttribute' => ['idColeccion' => 'id']],
+      [['idPermiso'], 'exist', 'skipOnError' => true, 'targetClass' => Permiso::class, 'targetAttribute' => ['idPermiso' => 'id']],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'idColeccion' => 'Id Coleccion',
+      'idPermiso' => 'Id Permiso',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  public function getColeccion() {
+    return $this->hasOne(ColeccionPermiso::class, ['id' => 'idColeccion']);
+  }
+
+  public function getPermiso() {
+    return $this->hasOne(Permiso::class, ['id' => 'idPermiso']);
+  }
+}

+ 76 - 0
models/Media.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "Media".
+ *
+ * @property string $id
+ * @property string|null $idUsuario
+ * @property string $nombre
+ * @property string|null $uuid
+ * @property string|null $size
+ * @property string|null $mimetype
+ * @property string|null $ruta
+ * @property string|null $descripcion
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ * @property string|null $extension
+ * 
+ * @property Usuario $usuario
+ */
+class Media extends ModeloBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'Media';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['nombre'], 'required'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['idUsuario', 'extension'], 'string', 'max' => 50],
+      [['uuid', 'size', 'mimetype'], 'string', 'max' => 100],
+      [['nombre'], 'string', 'max' => 255],
+      [['descripcion', 'ruta'], 'string', 'max' => 500],
+      [['idUsuario'], 'exist', 'skipOnError' => true, 'targetClass' => Usuario::class, 'targetAttribute' => ['idUsuario' => 'id']],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'idUsuario' => 'Id Usuario',
+      'nombre' => 'Nombre',
+      'uuid' => 'Uuid',
+      'size' => 'Size',
+      'mimetype' => 'Mimetype',
+      'ruta' => 'Ruta',
+      'descripcion' => 'Descripcion',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+      'extension' => 'Extension',
+    ];
+  }
+
+  /**
+   * Gets query for [[usuario]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getUsuario() {
+    return $this->hasOne(Usuario::class, ['id' => 'idUsuario']);
+  }
+}

+ 26 - 0
models/ModeloBase.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace app\models;
+
+use Ramsey\Uuid\Uuid;
+
+class ModeloBase extends \yii\db\ActiveRecord {
+
+  public function uuid() {
+    $pk = static::primaryKey();
+    if (is_array($pk) && count($pk) > 1) {
+      return null;
+    }
+    $pk = $pk[0];
+    do {
+      $uuid = (Uuid::uuid4())
+        ->toString();
+
+      $modelo = static::find()
+        ->andWhere([$pk => $uuid]);
+    } while ($modelo->exists());
+    $this->{$pk} = $uuid;
+    return $uuid;
+  }
+
+}

+ 55 - 0
models/Modulo.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "Modulo".
+ *
+ * @property string $id
+ * @property string|null $nombre
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property Permiso[] $permisos
+ */
+class Modulo extends ModeloBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'Modulo';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['id'], 'required'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['id'], 'string', 'max' => 50],
+      [['nombre'], 'string', 'max' => 100],
+      [['id'], 'unique'],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'nombre' => 'Nombre',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  public function getPermisos() {
+    return $this->hasMany(Permiso::class, ['idModulo' => 'id']);
+  }
+}

+ 77 - 0
models/Permiso.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "Permiso".
+ *
+ * @property string $id
+ * @property string|null $idModulo
+ * @property string|null $nombre
+ * @property string|null $descripcion
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property Modulo $idModulo0
+ * @property PermisoUsuario[] $permisoUsuarios
+ */
+class Permiso extends ModeloBase {
+
+  const PERFIL_ISTAI = 'PERUSUARIO-ASESORISTAI';
+  const PERFIL_COMISIONADO = 'PERUSUARIO-COMISIONADO';
+  const PERFIL_PROYECTISTA = 'PERUSUARIO-PROYECTISTA';
+  const PERFIL_PONENCIA = 'PERUSUARIO-PONENCIA';
+  const PERFIL_SOLICITANTE = 'PERUSUARIO-SOLICITANTE';
+  const PERFIL_UNIDAD_TRANSPARENCIA = 'PERUSUARIO-UNIDADTRANSP';
+
+  const SOLICITUD_VER_TODO = 'SOLICITUD-VERTODO';
+  const SUJETO_OBLIGADO_VER_TODO = 'SOLICITUD-VER-TODO-SUJETOS';
+  const RR_TURNAR_PONENCIA = 'RECURSO-TURNAR-PONENCIA';
+  const RR_VER_DETALLE = 'RECURSO-VERDETALLE';
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'Permiso';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['id'], 'required'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['id', 'idModulo'], 'string', 'max' => 50],
+      [['nombre', 'descripcion'], 'string', 'max' => 100],
+      [['id'], 'unique'],
+      [['idModulo'], 'exist', 'skipOnError' => true, 'targetClass' => Modulo::class, 'targetAttribute' => ['idModulo' => 'id']],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'idModulo' => 'Id Modulo',
+      'nombre' => 'Nombre',
+      'descripcion' => 'Descripcion',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  public function getModulo() {
+    return $this->hasOne(Modulo::class, ['id' => 'idModulo']);
+  }
+
+  public function getPermisoUsuarios() {
+    return $this->hasMany(PermisoUsuario::class, ['idPermiso' => 'id']);
+  }
+}

+ 63 - 0
models/PermisoUsuario.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "PermisoUsuario".
+ *
+ * @property string $id
+ * @property string|null $idUsuario
+ * @property string|null $idPermiso
+ * @property string|null $asignado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property Permiso $idPermiso0
+ * @property Usuario $idUsuario0
+ */
+class PermisoUsuario extends ModeloBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'PermisoUsuario';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['id'], 'required'],
+      [['asignado', 'modificado', 'eliminado'], 'safe'],
+      [['id', 'idUsuario', 'idPermiso'], 'string', 'max' => 50],
+      [['id'], 'unique'],
+      [['idPermiso'], 'exist', 'skipOnError' => true, 'targetClass' => Permiso::class, 'targetAttribute' => ['idPermiso' => 'id']],
+      [['idUsuario'], 'exist', 'skipOnError' => true, 'targetClass' => Usuario::class, 'targetAttribute' => ['idUsuario' => 'id']],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'idUsuario' => 'Id Usuario',
+      'idPermiso' => 'Id Permiso',
+      'asignado' => 'Asignado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  public function getPermiso() {
+    return $this->hasOne(Permiso::class, ['id' => 'idPermiso']);
+  }
+
+  public function getUsuario() {
+    return $this->hasOne(Usuario::class, ['id' => 'idUsuario']);
+  }
+}

+ 73 - 0
models/Producto.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "Producto".
+ *
+ * @property string $id
+ * @property string|null $nombre
+ * @property string|null $idPadre
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property Producto $idPadre
+ * @property Producto[] $productos
+ */
+class Producto extends ModeloBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'Producto';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['id'], 'required'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['id', 'idPadre'], 'string', 'max' => 36],
+      [['nombre'], 'string', 'max' => 100],
+      [['id'], 'unique'],
+      [['idPadre'], 'exist', 'skipOnError' => true, 'targetClass' => Producto::class, 'targetAttribute' => ['idPadre' => 'id']],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'nombre' => 'Nombre',
+      'idPadre' => 'Id Padre',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  /**
+   * Gets query for [[IdPadre]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getIdPadre() {
+    return $this->hasOne(Producto::class, ['id' => 'idPadre']);
+  }
+
+  /**
+   * Gets query for [[Productos]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getProductos() {
+    return $this->hasMany(Producto::class, ['idPadre' => 'id']);
+  }
+}

+ 253 - 0
models/Usuario.php

@@ -0,0 +1,253 @@
+<?php
+
+namespace app\models;
+
+use common\data\Respuesta;
+use Yii;
+use yii\db\Expression;
+use yii\db\Query;
+
+/**
+ * This is the model class for table "Usuario".
+ *
+ * @property string $id
+ * @property string $correo
+ * @property string $clave
+ * @property string $nombre
+ * @property int|null $estatus 0:inactivo, 1:activo
+ * @property string $telefono
+ * @property string|null $alias
+ * @property string|null $foto
+ * @property string $rol
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ */
+class Usuario extends ModeloBase {
+
+  public const ACTIVO = 1;
+  public const INACTIVO = 0;
+
+  const ROL_ADMIN = "admin";
+  const ROL_USUARIO = "usuario";
+  const ROL_UNIDAD_ADMINISTRATIVA = "unidadAdministrativa";
+
+  private $_permisos = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'Usuario';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['id', 'correo', 'clave', 'rol'], 'required'],
+      [['estatus', 'nombre', 'telefono'], 'default', 'value' => null],
+      [['estatus'], 'integer'],
+      [['creado', 'modificado', 'eliminado'], 'safe'],
+      [['id'], 'string', 'max' => 36],
+      [['correo', 'clave', 'nombre', 'telefono', 'alias', 'rol'], 'string', 'max' => 100],
+      [['foto'], 'string', 'max' => 300],
+      [['id'], 'unique'],
+    ];
+  }
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'correo' => 'Correo',
+      'clave' => 'Clave',
+      'nombre' => 'Nombre',
+      'estatus' => 'Estatus',
+      'telefono' => 'Telefono',
+      'alias' => 'Alias',
+      'foto' => 'Foto',
+      'rol' => 'Rol',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+      'idSujetoObligado' => 'Id Sujeto Obligado',
+      'idPonencia' => 'Id Ponencia',
+      'verificarCorreo' => 'Verificar Correo',
+    ];
+  }
+
+  /**
+   * Gets query for [[media]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getMedia() {
+    return $this->hasMany(Media::class, ['idUsuario' => 'id']);
+  }
+
+  public function getPermisos() {
+    return $this->hasMany(PermisoUsuario::class, ['idUsuario' => 'id'])
+      ->select("idPermiso")
+      ->column();
+  }
+
+  /**
+   * Gets query for [[recursosRevision]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getRecursosRevision() {
+    return $this->hasMany(RecursoRevision::class, ['idPonencia' => 'id']);
+  }
+
+  public function getEstatusPermiso() {
+    return $this->hasMany(EstatusPermisoUsuario::class, ['idUsuario' => 'id']);
+  }
+
+  /**
+   * Gets query for [[IdPonencia0]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getPonencia() {
+    return $this->hasOne(Ponencia::class, ['id' => 'idPonencia']);
+  }
+
+
+  /**
+   * Gets query for [[sujetoObligado]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getSujetoObligado() {
+    return $this->hasOne(SujetoObligado::class, ['id' => 'idSujetoObligado']);
+  }
+
+  public function agregarClave($pwd) {
+    $this->clave = Yii::$app->getSecurity()->generatePasswordHash($pwd);
+  }
+
+  public function validarClave($pwd) {
+    return Yii::$app->getSecurity()->validatePassword($pwd, $this->clave);
+  }
+
+  /**
+   * Consulta si el usuario cuenta con el permiso proporcionado.
+   * @param string $permiso Valor del permiso a evaluar.
+   * @return bool Regresa cierto si cuenta con permiso, de lo contrario, regresa falso.
+   */
+  public function tienePermiso($permiso = null) {
+    if ($permiso === null) {
+      return false;
+    }
+
+    $permisos = $this->cargarPermisos($permiso);
+    return isset($permisos[$permiso]) && $permisos[$permiso];
+  }
+
+  /**
+   * Consulta en la base de datos si el usuario cuenta con los permisos proporcionados.
+   * @param string|array $permiso Valor del permiso a evaluar | lista de permisos a evaluar.
+   * @return array Regresa un arreglo donde especifica, por permiso, si el usuario cuenta con algún permiso.
+   * 
+   * [
+   *  'clave_permiso_encontrado' => true,
+   *  'clave_permiso_no_encontrado' => false
+   * ]
+   */
+  public function cargarPermisos($permiso = null) {
+
+    $noEstan = [];
+    $permisosVacios = empty($this->_permisos);
+    if (!$permisosVacios) {
+      if (is_array($permiso)) {
+        foreach ($permiso as $p) {
+          if (!isset($this->_permisos[$p])) {
+            $noEstan[] = $p;
+          }
+        }
+      } else if (!isset($this->_permisos[$permiso])) {
+        $noEstan[] = $permiso;
+      }
+    }
+
+    if ($permisosVacios || !empty($noEstan)) {
+      $permisosQuery = (new Query())
+        ->select([
+          'case when ({{PermisoUsuario}}.[[idUsuario]] is not null) then true else false end as [[idUsuario]]',
+          '{{Permiso}}.id',
+        ])
+        ->from('Permiso')
+        ->leftJoin(
+          'PermisoUsuario',
+          '{{PermisoUsuario}}.[[idPermiso]] = {{Permiso}}.id and {{PermisoUsuario}}.[[idUsuario]] = :idUsuario',
+          [':idUsuario' => $this->id]
+        )
+        ->andWhere([
+          // 'idPermiso' => $permiso,
+          '{{PermisoUsuario}}.eliminado' => null
+        ]);
+
+      if ($permisosVacios) {
+        $permisosQuery->andWhere(['{{Permiso}}.id' => $permiso]);
+      } else {
+        $permisosQuery->andWhere(['{{Permiso}}.id' => $noEstan]);
+      }
+
+      $aux = $permisosQuery->indexBy('id')
+        ->column();
+
+      if ($permisosVacios) {
+        $this->_permisos = $aux;
+      } else {
+        foreach ($aux as $indice => $valor) {
+          $this->_permisos[$indice] = $valor;
+        }
+      }
+    }
+
+    return $this->_permisos;
+  }
+
+  public function validarPerfilPermisos() {
+    $permisos = $this->cargarPermisos();
+
+    $flag = false;
+
+    foreach ($permisos as $permiso) {
+      if ($permiso) {
+        $flag = true;
+        break;
+      }
+    }
+
+    return $flag;
+  }
+
+  public static function crearSolicitante($rol = null, $idUsuario = "") {
+    $permisoSolicitante = (new Query())
+      ->select('{{ColeccionPermisoPermiso}}.[[idPermiso]]')
+      ->from('ColeccionPermisoPermiso')
+      ->innerJoin('ColeccionPermiso', '{{ColeccionPermisoPermiso}}.[[idColeccion]] = {{ColeccionPermiso}}.id')
+      ->andWhere(['{{ColeccionPermiso}}.clave' => "SOLICITANTE"])
+      ->column();
+
+    if ($rol === Usuario::ROL_USUARIO &&  $permisoSolicitante !== null) {
+      foreach ($permisoSolicitante as $permiso) {
+        $permisos = new PermisoUsuario();
+        $permisos->idUsuario = $idUsuario;
+        $permisos->idPermiso = $permiso;
+        $permisos->asignado = new Expression('now()');
+        $permisos->uuid();
+        if (!$permisos->save()) {
+          return (new Respuesta($permiso))
+            ->mensaje("Ocurrió un problema al asignar permisos");
+        }
+      }
+    }
+  }
+}

+ 45 - 0
models/UsuarioMedia.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace app\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "UsuarioMedia".
+ *
+ * @property string $idUsuario
+ * @property string $idMedia
+ */
+class UsuarioMedia extends ModeloBase
+{
+    /**
+     * {@inheritdoc}
+     */
+    public static function tableName()
+    {
+        return 'UsuarioMedia';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rules()
+    {
+        return [
+            [['idUsuario', 'idMedia'], 'required'],
+            [['idUsuario', 'idMedia'], 'string', 'max' => 36],
+            [['idUsuario', 'idMedia'], 'unique', 'targetAttribute' => ['idUsuario', 'idMedia']],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attributeLabels()
+    {
+        return [
+            'idUsuario' => 'Id Usuario',
+            'idMedia' => 'Id Media',
+        ];
+    }
+}

+ 38 - 0
modules/common/Module.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace common;
+
+use Yii;
+
+/**
+ * v1 module definition class
+ */
+class Module extends \yii\base\Module {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $controllerNamespace = 'common\controllers';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    parent::init();
+    $response = Yii::$app->getResponse();
+    $headers = $response->getHeaders();
+
+    $headers->set('Access-Control-Allow-Methods', 'POST, GET, DELETE, OPTIONS');
+    $headers->set('Access-Control-Allow-Headers', 'Content-Type,Accept,Authorization');
+    $headers->set('Access-Control-Allow-Origin', '*');
+    $headers->set('Access-Control-Request-Method', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Credentials', 'true');
+    $headers->set('Access-Control-Max-Age', 86400);
+    if (Yii::$app->getRequest()->isOptions) {
+      Yii::$app->end();
+    }
+    Yii::$app->getUser()->enableSession = false;
+    Yii::$app->getUser()->identityClass = 'common\models\Usuario';
+  }
+
+}

+ 159 - 0
modules/common/data/Respuesta.php

@@ -0,0 +1,159 @@
+<?php
+
+namespace common\data;
+
+use common\rest\Serializer;
+use yii\data\ActiveDataProvider;
+
+class Respuesta {
+
+  public $cuerpo = [];
+  protected $atributosPermitidos = [
+    'resultado',
+    'mensaje',
+    'errores',
+    'detalle',
+    'paginacion'
+  ];
+
+  protected $parametros = [
+    "total" => 0,
+    "pagina" => 0,
+    "limite" => 0,
+    "ordenar" => false
+  ];
+
+  public function __set($nombre, $valor) {
+    if(!in_array($nombre, $this->atributosPermitidos)) {
+      return;
+    }
+    $this->cuerpo[$nombre] = $valor;
+  }
+
+  public function __get($nombre) {
+    if(isset($this->cuerpo[$nombre])) {
+      return $this->cuerpo[$nombre];
+    }
+
+    return null;
+  }
+
+  public function __construct($modelo = null, $limite = 20, $pagina = 1, $ordenar = false) {
+    $this->parametros['limite'] = $limite;
+    $this->parametros['pagina'] = $pagina;
+    $this->parametros['ordenar'] = $ordenar;
+    if($modelo !== null) {
+      $this->modelo($modelo);
+    }
+  }
+
+  public function modelo($modelo) {
+    $this->esExitoso();
+    if ($modelo instanceof \yii\db\ActiveRecord) {
+      if ($modelo->hasErrors()) {
+        $this->esError();
+        $this->errores = $modelo->getFirstErrors();
+      } else {
+        $this->detalle($modelo->toArray());
+      }
+    } elseif ($modelo instanceof \yii\db\ActiveQuery || $modelo instanceof \yii\db\Query) {
+      \Yii::$app->getResponse()->setStatusCode(200);
+      $req = \Yii::$app->getRequest();
+      $sql = intval($req->get("sql", "")) === 1;
+      if ($sql) {
+        \Yii::$app->getResponse()->format = \yii\web\Response::FORMAT_RAW;
+        echo $modelo->createCommand()->getRawSql();
+        exit(0);
+      }
+      $limite = intval($this->parametros['limite']);
+      $pagina = intval($this->parametros['pagina']);
+      $ordenar = $this->parametros['ordenar'];
+      $total = $modelo->count();
+
+      if($pagina <= 0) {
+        $pagina = 1;
+      }
+
+      $offset = 0;
+      if (($pagina - 1) >= 0) {
+        $offset = $limite * ($pagina - 1);
+      }
+
+      if($offset > 0) {
+        $modelo->offset($offset);
+      }
+
+      $modelo->limit($limite);
+
+      if ($ordenar !== false && ($campo = trim($ordenar)) !== "") {
+        $separar = explode(",", $ordenar);
+        $ordenamiento = [];
+        foreach ($separar as $segmento) {
+          $exp = explode("-", trim($segmento));
+          $desc = false;
+          if (count($exp) > 1) {
+            $campo = $exp[0];
+            $desc = $exp[1] === 'desc';
+          }
+          $ordenamiento[$campo] = $desc ? SORT_DESC : SORT_ASC;
+        }
+        if (!empty($ordenamiento)) {
+          $modelo->orderBy($ordenamiento);
+        }
+      }
+
+      if ($limite > $total || $limite <= 0) {
+        $limite = $total;
+      }
+
+      $this->paginacion = [
+        "total" => (int)$total, # Total de elementos
+        "pagina" => $pagina, # Página actual
+        "limite" => $limite # Elementos por página
+      ];
+
+      $s = new Serializer();
+      $this->resultado = $s->serialize(new ActiveDataProvider(["query" => $modelo, "pagination" => false]));
+    } elseif(is_array($modelo) && isset($modelo[0])) {
+      $total = count($modelo);
+      $this->paginacion = [
+        "total" => $total,
+        "pagina" => 1,
+        "limite" => $total
+      ];
+      $this->resultado = $modelo;
+    } else {
+      $this->paginacion = [
+        "total" => 1,
+        "pagina" => 1,
+        "limite" => 1
+      ];
+      $this->resultado = [$modelo];
+    }
+    return $this;
+  }
+
+  public function esExitoso($codigo = 200) {
+    \Yii::$app->getResponse()->setStatusCode($codigo);
+    return $this;
+  }
+  
+  public function esError($codigo = 400) {
+    \Yii::$app->getResponse()->setStatusCode($codigo);
+    return $this;
+  }
+
+  public function detalle($detalle) {
+    $this->detalle = $detalle;
+    return $this;
+  }
+
+  public function mensaje($mensaje) {
+    $this->mensaje = $mensaje;
+    return $this;
+  }
+
+  public function getParametros() {
+    return $this->parametros;
+  }
+}

+ 72 - 0
modules/common/models/Usuario.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace common\models;
+
+use Yii;
+use yii\web\IdentityInterface;
+use Firebase\JWT\JWT;
+
+class Usuario extends \app\models\Usuario implements IdentityInterface {
+
+  /**
+   * Finds an identity by the given id.
+   *
+   * @param string|int $id the id to be looked for
+   * @return IdentityInterface|null the identity object that matches the given id.
+   */
+  public static function findIdentity($id) {
+    return static::findOne($id);
+  }
+
+  /**
+   * Finds an identity by the given token.
+   *
+   * @param string $token the token to be looked for
+   * @return IdentityInterface|null the identity object that matches the given token.
+   */
+  public static function findIdentityByAccessToken($token, $type = null) {
+    $key = Yii::$app->params['jwt.key'];
+    $jwt = JWT::decode($token, $key, ['HS256']);
+    if(!isset($jwt->id)) {
+      return null;
+    }
+
+    return static::findOne($jwt->id);
+  }
+
+  /**
+   * @return int|string current user ID
+   */
+  public function getId() {
+    return $this->id;
+  }
+
+  /**
+   * @return string current user auth key
+   */
+  public function getAuthKey() {
+    $key = Yii::$app->params['jwt.key'];
+    $token = [
+      "id" => $this->id,
+      "pass" => $this->clave
+    ];
+
+    $jwt = JWT::encode($token, $key);
+    return $jwt;
+  }
+
+  /**
+   * @param string $authKey
+   * @return bool if auth key is valid for current user
+   */
+  public function validateAuthKey($authKey) {
+    $key = Yii::$app->params['jwt.key'];
+    $jwt = JWT::decode($authKey, $key);
+    if(!isset($jwt["id"])) {
+      return false;
+    }
+
+    return $jwt["id"] == $this->id;
+  }
+
+}

+ 36 - 0
modules/common/rest/AuthController.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace common\rest;
+
+use yii\filters\auth\HttpBearerAuth;
+use yii\filters\auth\QueryParamAuth;
+
+class AuthController extends JsonController {
+
+  /**
+   * @var \common\models\Usuario $usuario
+   */
+  public $usuario;
+  public $permisos = [];
+
+  public function behaviors() {
+    $behavior = parent::behaviors();
+    $behavior["authenticator"]["authMethods"] = [
+      QueryParamAuth::className(),
+      HttpBearerAuth::className()
+    ];
+    return $behavior;
+  }
+
+  public function beforeAction($action) {
+    parent::beforeAction($action);
+
+    $this->usuario = \Yii::$app->getUser()->getIdentity();
+
+    if (!empty($this->permisos)) {
+      $this->usuario->cargarPermisos($this->permisos);
+    }
+
+    return true;
+  }
+}

+ 84 - 0
modules/common/rest/JsonController.php

@@ -0,0 +1,84 @@
+<?php
+
+namespace common\rest;
+
+use yii\filters\ContentNegotiator;
+use yii\filters\Cors;
+use yii\rest\Controller;
+use yii\web\Response;
+use Yii;
+
+
+/**
+ * @property \yii\web\Application $app
+ * @property \yii\web\Request $req
+ * @property \yii\web\Response $res
+ * @property \yii\db\ActiveQuery $queryInicial
+ * @property int $limite
+ * @property int $pagina
+ * @property string $ordenar
+ * @property string $modelClass
+ */
+class JsonController extends Controller {
+
+  public $app = null;
+  public $req = null;
+  public $res = null;
+
+  public $queryInicial = null;
+  public $modelClass = null;
+
+  public $limite = null;
+  public $pagina = null;
+  public $ordenar = null;
+
+  public $serializer = 'common\rest\Serializer';
+
+  public function behaviors() {
+    $behavior = parent::behaviors();
+    $behavior['contentNegotiator'] =  [
+      'class' => ContentNegotiator::className(),
+      'formats' => [
+        'application/json' => Response::FORMAT_JSON,
+        'application/xml' => Response::FORMAT_XML,
+      ],
+    ];
+    $behavior['corsFilter'] = [
+      'class' => Cors::className(),
+      'cors' => [
+        'Origin' => ['*'],
+        'Access-Control-Request-Method' => [
+          'GET', 'POST', 'PUT', 'PATCH', 
+          'DELETE', 'HEAD', 'OPTIONS'
+        ],
+        'Access-Control-Request-Headers' => ['*'],
+      ],
+    ];
+    $behavior["authenticator"]["except"] = ['options'];
+    return $behavior;
+  }
+
+  public function beforeAction($action) {
+    parent::beforeAction($action);
+    Yii::$app->getResponse()->format = Response::FORMAT_JSON;
+    $this->app = Yii::$app;
+    $this->req = $this->app->getRequest();
+    $this->res = $this->app->getResponse();
+    if ($this->req->isGet) {
+      $this->limite = $this->req->get("limite", 20);
+      $this->pagina = $this->req->get("pagina", 0);
+      $this->ordenar = $this->req->get("ordenar", "");
+    }
+    if ($this->modelClass !== null) {
+      $model = new $this->modelClass;
+      $tableName = $this->modelClass::tableName();
+      $this->queryInicial = $this->modelClass::find();
+      if ($model->hasProperty('eliminado')) {
+        $this->queryInicial
+          ->where(["{{{$tableName}}}.[[eliminado]]" => null]);
+      }
+    }
+    return true;
+  }
+
+}

+ 19 - 0
modules/common/rest/Serializer.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace common\rest;
+
+use yii\rest\Serializer as YiiSerializer;
+use common\data\Respuesta;
+
+class Serializer extends YiiSerializer {
+
+  public function serialize($data) {
+    $data = parent::serialize($data);
+    if ($data instanceof Respuesta) {
+      return $data->cuerpo;
+    }
+
+    return $data;
+  }
+
+}

+ 18 - 0
modules/common/rest/UrlRule.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace common\rest;
+
+class UrlRule extends \yii\rest\UrlRule {
+
+  public $pluralize = false;
+
+  public $patterns = [
+    'PUT' => 'guardar',
+    'DELETE' => 'eliminar',
+    'GET,HEAD' => 'index',
+    'POST' => 'guardar',
+    'GET,HEAD' => 'index',
+    '' => 'options',
+  ];
+
+}

+ 38 - 0
modules/excel/Module.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace excel;
+
+use Yii;
+
+/**
+ * v1 module definition class
+ */
+class Module extends \yii\base\Module {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $controllerNamespace = 'excel\controllers';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    parent::init();
+    $response = Yii::$app->getResponse();
+    $headers = $response->getHeaders();
+    
+    $headers->set('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Headers', 'Content-Type,Accept,Authorization');
+    $headers->set('Access-Control-Allow-Origin', '*');
+    $headers->set('Access-Control-Request-Method', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Credentials', 'true');
+    $headers->set('Access-Control-Max-Age', 86400);
+    if (Yii::$app->getRequest()->isOptions) {
+      Yii::$app->end();
+    } // */
+    Yii::$app->getUser()->enableSession = false;
+    Yii::$app->getUser()->identityClass = 'v1\models\Usuario';
+  }
+
+}

+ 25 - 0
modules/excel/controllers/AcuseSolicitudController.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace excel\controllers;
+
+use app\models\OficialiaPartesManifiestoDocumento;
+use Exception;
+use pdf\web\Controller;
+use v1\controllers\EntregaInformacionController;
+use v1\models\FondoLegislativo;
+use v1\models\OficialiaPartesDocumento;
+use v1\models\OficialiaPartesDocumentoResponsable;
+use v1\models\OficialiaPartesManifiesto;
+use v1\models\RecursoRevision;
+use v1\models\RequisicionResponsable;
+use v1\models\Solicitud;
+use v1\models\Usuario;
+use yii\web\HttpException;
+use yii\web\NotFoundHttpException;
+
+class AcuseSolicitudController extends Controller {
+
+  public function actionIndex() {
+  }
+
+}

+ 69 - 0
modules/excel/controllers/IncompetenciaSujetoController.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace excel\controllers;
+
+use app\models\OficialiaPartesManifiestoDocumento;
+use pdf\web\Controller;
+use v1\models\FondoLegislativo;
+use v1\models\OficialiaPartesDocumento;
+use v1\models\OficialiaPartesDocumentoResponsable;
+use v1\models\OficialiaPartesManifiesto;
+use v1\models\RequisicionResponsable;
+use v1\models\Solicitud;
+use v1\models\Usuario;
+
+class IncompetenciaSujetoController extends Controller {
+
+  public function actionIndex() {
+
+    $idSolicitud = trim($this->req->get('solicitud', ''));
+    $solicitud = null;
+
+    if ($idSolicitud === '') {
+      throw new \yii\web\NotFoundHttpException("No se encontró la solicitud");
+    }
+
+    $solicitud = Solicitud::find()
+      ->andWhere(['id' => $idSolicitud])
+      ->andWhere(['eliminado' => null])
+      ->one();
+
+    if ($solicitud === null) {
+      throw new \yii\web\NotFoundHttpException("No se encontró la solicitud");
+    }
+
+    $request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '50',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/incompetencia-sujeto/oficio", [
+      "solicitud" => $solicitud,
+      "recursoRevision" => [],
+    ]);
+
+    if ($request->get('header'))
+      $header = $view->render("@app/modules/pdf/views/header/oficio-landing", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+    else
+      $header = $view->render("@app/modules/pdf/views/header/oficio", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+
+    $pdf = self::crearPDF(
+      'AS-ISTAIAS-03',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+}

+ 24 - 0
modules/excel/controllers/RecursoRevisionController.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace excel\controllers;
+
+use excel\web\Controller;
+use v1\models\RecursoRevision;
+
+class RecursoRevisionController extends Controller {
+
+  public function actionIndex() {
+    $id = $this->req->get("id", "");
+
+    $recurso = RecursoRevision::findOne($id);
+
+    if (!$recurso) {
+      throw new \yii\web\NotFoundHttpException("No se encontró el recurso de revisión");
+    }
+
+    return $this->renderPartial("formato", [
+      "recurso" => $recurso,
+    ]);
+  }
+
+}

+ 22 - 0
modules/excel/controllers/RequerimientoAclaracionController.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace excel\controllers;
+
+use app\models\OficialiaPartesManifiestoDocumento;
+use pdf\web\Controller;
+use v1\models\Aclaracion;
+use v1\models\FondoLegislativo;
+use v1\models\OficialiaPartesDocumento;
+use v1\models\OficialiaPartesDocumentoResponsable;
+use v1\models\OficialiaPartesManifiesto;
+use v1\models\RequisicionResponsable;
+use v1\models\Usuario;
+use yii\web\HttpException;
+use yii\web\NotFoundHttpException;
+
+class RequerimientoAclaracionController extends Controller {
+
+  public function actionIndex() {
+
+  }
+}

+ 104 - 0
modules/excel/controllers/SolicitudController.php

@@ -0,0 +1,104 @@
+<?php
+
+namespace excel\controllers;
+
+use app\models\Solicitud;
+use excel\web\Controller;
+use v1\models\RecursoRevision;
+
+class SolicitudController extends Controller {
+
+  public function actionIndex() {
+    $inicio = $this->req->get("inicio", "");
+    $fin = $this->req->get("fin", "");
+
+    $query = Solicitud::find();
+
+    if (!empty($inicio) & !empty($fin)) {
+      $query->andWhere([
+        "AND",
+        [">=", "recepcion", $inicio],
+        ["<=", "recepcion", $fin],
+      ]);
+    }
+
+    $query->andWhere(['eliminado' => null]);
+
+    if ($query->count() === 0)
+      throw new \yii\web\NotFoundHttpException("No se encontrarón Solicitudes");
+
+    $array = [];
+
+    $titulos = [
+      'Tipo de Acceso',
+      'Folio de la Solicitud',
+      'Sujeto Obligado',
+      'Estado Actual',
+      'Fecha de Recepción',
+      'DT',
+      'DR',
+      'DA',
+      'DT-RR',
+      'DR-RR',
+    ];
+
+    $campos = [
+      'tipoAcceso',
+      'folioSolicitud',
+      'sujetoObligado',
+      'estadoActual',
+      'fechaRecepción',
+      'dt',
+      'dr',
+      'da',
+      'dtrr',
+      'drrr',
+    ];
+
+    foreach ($query->each() as $solicitud) {
+
+      $recursosRevision = RecursoRevision::find()
+        ->andWhere(['eliminado' => null, 'idSolicitud' => $solicitud->id])
+        ->orderBy('creado');
+
+      array_push($array, [
+        'tipoAcceso' => $recursosRevision->count() > 0 ? 'RR' : 'S',
+        'folioSolicitud' => "{$solicitud->folio} ",
+        'sujetoObligado' => $solicitud->sujetoObligado->nombre,
+        'estadoActual' => $solicitud->estatus->nombre,
+        'fechaRecepción' => date("d/m/Y", strtotime($solicitud->recepcion)),
+        'dt' => $solicitud->diasTranscurridos,
+        'dr' => $solicitud->diasRestantes,
+        'da' => $solicitud->diasAtencion,
+        'dtrr' => $recursosRevision->one() ? $recursosRevision->one()->diasTranscurridos : "-",
+        'drrr' => $recursosRevision->one() ? $recursosRevision->one()->diasRestantes : "-",
+      ]);
+
+      foreach ($recursosRevision->each() as $recurso){
+        array_push($array, [
+          'tipoAcceso' => "",
+          'folioSolicitud' => "{$solicitud->folio} ",
+          'sujetoObligado' => $solicitud->sujetoObligado->nombre,
+          'estadoActual' => $solicitud->estatus->nombre,
+          'fechaRecepción' => date("d/m/Y", strtotime($solicitud->recepcion)),
+          'dt' => $solicitud->diasTranscurridos,
+          'dr' => $solicitud->diasRestantes,
+          'da' => $solicitud->diasAtencion,
+          'dtrr' => $recursosRevision->one() ? $recursosRevision->one()->diasTranscurridos : "-",
+          'drrr' => $recursosRevision->one() ? $recursosRevision->one()->diasRestantes : "-",
+        ]);
+      }
+
+    }
+
+    $fecha = date("d/m/Y");
+    $nombre = "Solicitud " . $fecha;
+    $titulo = "Solicitud " . $fecha;
+    $pestania = "Solicitudes";
+
+    return $this->Excel($titulo, $pestania, $nombre, $titulos, $campos, $array, false, $isArray = true);
+
+
+  }
+
+}

+ 338 - 0
modules/excel/views/acuse-solicitud/formato.php

@@ -0,0 +1,338 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\Solicitud $solicitud
+ * @var \v1\models\RecursoRevision $recurso
+ * @var \v1\models\Usuario $usuario
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$media = $solicitud->media;
+$solicitante = $solicitud->usuario;
+
+$modalidadEntrega = [
+  ["id" => 1, "label" => "Portal ISTAI"],
+  ["id" => 2, "label" => "CD-ROM (con costo)"],
+  ["id" => 3, "label" => "Consulta Directa"],
+  ["id" => 4, "label" => "Copias Certificadas (con costo)"],
+  ["id" => 5, "label" => "Copias Simples (con costo)"],
+  ["id" => 6, "label" => "Copia Digitalizada"],
+  ["id" => 7, "label" => "Otro"],
+];
+
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          SUJETO OBLIGADO
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          <?= $solicitud->sujetoObligado->nombre ?>
+          <!-- Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales -->
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4">
+        <strong>Fecha de Recepción:</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        <?php
+        $date = '';
+        if ($solicitud->recepcion) {
+          $tz = new DateTimeZone('America/Hermosillo');
+          $date = new DateTime($solicitud->recepcion);
+          $date->setTimezone($tz);
+          $date = $date->format('d-m-Y');
+        }
+        echo $date;
+        ?>
+      </td>
+      <td class="col-table-2">
+        <strong>Hora:</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        <?php
+        $hora = '';
+        if ($solicitud->recepcion) {
+          $tz = new DateTimeZone('America/Hermosillo');
+          $hora = new DateTime($solicitud->recepcion);
+          $hora->setTimezone($tz);
+          $hora = $hora->format('H:i:s');
+        }
+        echo $hora
+        ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DATOS DEL SOLICITANTE
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          <?= $solicitud->nombre ? $solicitud->nombre : 'No Proporcionado' ?>
+          <!-- Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales -->
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-3 td-border td-center">
+        <strong>
+          DOMICILIO
+        </strong>
+      </td>
+      <td class="col-table-9 td-center">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          PAíS:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+        No Proporcionado
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          DOMICILIO:
+        </strong>
+      </td>
+      <td colspan="3" class="col-table-9 td-subrayado ">
+        No Proporcionado
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          CORREO ELECTRÓNICO:
+        </strong>
+      </td>
+      <td class="col-table-3 td-subrayado ">
+        <?= $solicitud->correo ?>
+      </td>
+      <td class="col-table-3">
+        <strong>
+          TELÉFONO (Opcional):
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado">
+        <?= $solicitud->telefono ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse ">
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de la Solicitud:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red"><?= $solicitud->folio ?></strong>
+      </td>
+    </tr>
+    <?php if ($recurso !== null) : ?>
+      <tr>
+        <td class="col-table-7">
+          <strong style="color:red">Número de Folio de Recurso de Revisión:</strong>
+        </td>
+        <td class="col-table-5">
+          <strong style="color:red">
+            <?= $recurso->folio ?>
+          </strong>
+        </td>
+      </tr>
+    <?php endif ?>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12" style="border: 1px solid black">
+        Solicitud de información del instituto de transparencia
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12">
+        <?= $solicitud->descripcion ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-8 td-border" colspan="2">
+        <strong>MODALIDAD DE ENTREGA</strong>
+      </td>
+      <td class="col-table-4"></td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <?php
+    $contador = 0;
+    foreach ($modalidadEntrega as $entrega) {
+      if ($contador % 3 == 0) {
+        echo '<tr>';
+      }
+
+      if ($entrega['id'] === $solicitud->idModalidadEntrega) {
+        echo '<td class="col-table-4"><span style="background-color: #D9B5E5"><input type="checkbox" value="'.$entrega['id'].'" checked="checked" />' . $entrega['label'] . '</span></td>';
+      } else {
+        echo '<td class="col-table-4"><span><input type="checkbox" value="'.$entrega['id'].'" />' . $entrega['label'] . '</td>';
+      }
+
+      if ($contador % 3 == 2) {
+        echo '</tr>';
+      }
+
+      $contador++;
+    }
+
+    if ($contador % 3 != 0) {
+      echo '</tr>';
+    }
+
+    if ($solicitud->idModalidadEntrega === 6) {
+    ?>
+      <tr>
+        <td class="col-table-4">
+          Especificar:
+        </td>
+        <td class="col-table-4">
+          <?= $solicitud->otroModalidadEntrega ?>
+        </td>
+      </tr>
+    <?php
+    }
+    ?>
+    <!-- <tr>
+      <td class="col-table-4">
+        A través del SAIMEX
+      </td>
+      <td class="col-table-4">
+        Copias Simples(con costo)
+      </td>
+      <td class="col-table-4">
+        Consulta Directa(sin costo)
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4">
+        CD-ROM(con costo)
+      </td>
+      <td class="col-table-4">
+        Copias Certificadas(con costo)
+      </td>
+      <td class="col-table-4">
+        Disquete 3.5(con costo)
+      </td>
+    </tr>
+    <tr>
+      <td>
+        OTRO TIPO DE MEDIO (Especificar):
+      </td>
+      <td colspan="2">
+      </td>
+    </tr> -->
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DOCUMENTOS ANEXOS
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border">
+    <tr>
+      <td class="col-table-12">
+        <ul>
+          <?php
+          foreach ($media as $archivo) :
+          ?>
+            <li>
+              <a href="<?= $archivo->ruta ?>" target="_blank">
+                <?= $archivo->nombre ?>
+              </a>
+            </li>
+          <?php
+          endforeach
+          ?>
+        </ul>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          PLAZO DE RESPUESTA
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-8">
+        <strong>Incompetencia y Parcialmente Incompetencia :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        3 días hábiles
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>En Caso de Negativa a la Información: Improcedente, Inexistente, Confidencial y Reservada :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>En Caso de Reproducción con Costo :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles
+      </td>
+    </tr>
+  </table>
+</div>

+ 50 - 0
modules/excel/views/acuse-solicitud/oficio.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-oficio">
+    <tr>
+      <td class="col-table-4 td-center">
+      </td>
+      <td class="col-table-8" style="text-align:right">
+        nse de Transparencia, Acceso a la Información Pública y Datos Personales,<br/>
+        México a 03 de Octubre de 2023<br/>
+        Nombre del solicitante: C. Solicitante<br/>
+        Folio de la solicitud: 00004/INFOSONORA/IP/2023<br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        En respuesta a la solicitud recibida, nos permitimos hacer de su conocimiento que con fundamento en el artículo
+        53, Fracciones: II, V y VI de la Ley de Transparencia y Acceso a la Información Pública del Estado de México y
+        Municipios, le contestamos que:
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Respuesta del servidor público habilitado
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-oficio center " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        ATENTAMENTE<br/>
+        Unidad de Transparencia - Sujeto Obligado<br/>
+        <strong>Unidad de Transparencia</strong><br/>
+        Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+      </td>
+    </tr>
+  </table>
+</div>

+ 26 - 0
modules/excel/views/header/formato.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ * @var  $titulo
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table width="100%" style="margin-bottom: 10px">
+    <tr>
+      <td width="220px" style="text-align: left">
+        <img src="/img/logo_istai_lg.png" width="25%" height="15%">
+      </td>
+      <td width="480px" style="text-align: center">
+        <h3>
+          <?= $titulo ?>
+        </h3>
+      </td>
+      <td width="220px" style="text-align: right">
+      </td>
+    </tr>
+  </table>
+</div>

+ 23 - 0
modules/excel/views/header/oficio-landing.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse center" style="margin-bottom: 10px">
+    <tr>
+      <td class="col-table-3">
+        <img src="/img/logo_istai_lg.png" width="30%" height="18%">
+      </td>
+      <td class="col-table-9" style="text-align: center">
+        <h3>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </h3>
+      </td>
+    </tr>
+  </table>
+</div>

+ 25 - 0
modules/excel/views/header/oficio.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse center" style="margin-bottom: 10px">
+    <tr>
+      <td width="48px">
+        <img src="/img/logo_istai_lg.png" width="30%" height="18%">
+      </td>
+    </tr>
+    <tr>
+      <td width="1200px" style="text-align: center">
+        <h3>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </h3>
+      </td>
+    </tr>
+  </table>
+</div>

+ 263 - 0
modules/excel/views/incompetencia-sujeto/formato.php

@@ -0,0 +1,263 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          SUJETO OBLIGADO
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4">
+        <strong>Fecha de Recepción(dd-mm-aaaa):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10/01/2023
+      </td>
+      <td class="col-table-2">
+        <strong>Hora(hh:mm):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10:25:00
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DATOS DEL SOLICITANTE
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-3 td-border td-center">
+        <strong>
+          DOMICILIO
+        </strong>
+      </td>
+      <td class="col-table-9 td-center">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          PAÍS:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          DOMICILIO:
+        </strong>
+      </td>
+      <td colspan="3" class="col-table-9 td-center td-subrayado ">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          CORREO ELECTRÓNICO:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3">
+        <strong>
+          TELÉFONO (Opcional):
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse ">
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de la Solicitud:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">00004/INFOSONORA/IP/2023</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de Recurso de Revisión:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">
+          00001/INFOEM/IP/RR/2023
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-10 td-border td-center">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border">
+    <tr>
+      <td class="col-table-12">
+        Solicitud de información del instituto de transparencia
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-8 td-border" colspan="2">
+        <strong>MODALIDAD DE ENTREGA</strong>
+      </td>
+      <td class="col-table-4"></td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-4">
+        A través del SAIMEX
+      </td>
+      <td class="col-table-4">
+        Copias Simples(con costo)
+      </td>
+      <td class="col-table-4">
+        Consulta Directa(sin costo)
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4">
+        CD-ROM(con costo)
+      </td>
+      <td class="col-table-4">
+        Copias Certificadas(con costo)
+      </td>
+      <td class="col-table-4">
+        Disquete 3.5(con costo)
+      </td>
+    </tr>
+    <tr>
+      <td>
+        OTRO TIPO DE MEDIO (Especificar):
+      </td>
+      <td colspan="2">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DOCUMENTOS ANEXOS
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          PLAZO DE RESPUESTA
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha límite de respuesta:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        15 días hábiles 31/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha de posible requerimiento de aclaración de la información :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles 17/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Notificación de ampliación de plazo(prórroga) :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        14 a 15 días hábiles 30/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Respuesta a la solicitud en caso de ampliación de plazo :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        22 días hábiles 10/02/2023
+      </td>
+    </tr>
+  </table>
+</div>

+ 40 - 0
modules/excel/views/incompetencia-sujeto/oficio.php

@@ -0,0 +1,40 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\Solicitud $solicitud
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-oficio">
+    <tr>
+      <td class="col-table-4 td-center">
+      </td>
+      <td class="col-table-8" style="text-align:right">
+        nse de Transparencia, Acceso a la Información Pública y Datos Personales,<br/>
+        México a 03 de Octubre de 2023<br/>
+        Nombre del solicitante: C. Solicitante<br/>
+        Folio de la solicitud: <?= $solicitud->folio ?><br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        fue una incompetencia total
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-oficio center " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        ATTE<br/>
+        Unidad de Transparencia - Sujeto Obligado<br/>
+      </td>
+    </tr>
+  </table>
+</div>

+ 264 - 0
modules/excel/views/recurso-revision/formato.php

@@ -0,0 +1,264 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\RecursoRevision $recurso
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+date_default_timezone_set('America/Hermosillo');
+$fecha = date("d/m/Y");
+$hora = date("h:i A");
+$nombreCompleto = explode(" ", $recurso->solicitud->usuario->nombre);
+?>
+<div class="container">
+  <table class="table-acuse border">
+    <tr>
+      <td class="col-table-12 border" colspan="4" style="text-align:center;background:#cccccc; padding:9px 0">
+        <strong>RECEPCIÓN</strong>
+      </td>
+    </tr>
+    <tr>
+    <tr>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Fecha (dd/mm/aaaa):</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $fecha ?>
+      </td>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Hora (hh:mm:ss):</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $hora ?>
+      </td>
+    </tr>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td colspan="2" class="col-table-12 td-border" style="text-align: center; background:#cccccc; padding:9px 0">
+        <strong>DATOS DEL SOLICITANTE</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4 td-border" style=" background:#cccccc; padding:9px 0">
+        <strong>PERSONA FÍSICA</strong>
+      </td>
+      <td class="col-table-8 td-no-borde">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border acuse td-padding">
+    <tr>
+      <td class="col-table-4" style="text-align: center;padding-top:40px">
+        <strong><?= count($nombreCompleto) === 3 ? $nombreCompleto[2] : "____________________________" ?></strong><br>
+        <strong>APELLIDO PATERNO</strong>
+      </td>
+      <td class="col-table-4" style="text-align: center;padding-top:40px">
+        <strong><?= $nombreCompleto[1] ? $nombreCompleto[1] : "____________________________" ?></strong><br>
+        <strong>APELLIDO MATERNO</strong>
+      </td>
+      <td class="col-table-4" style="text-align: center;padding-top:40px">
+        <strong><?= $nombreCompleto[0] ?></strong><br>
+        <strong>NOMBRE(S)</strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4 td-border" style="background:#cccccc; padding:9px 0">
+        <strong>PERSONA MORAL</strong>
+      </td>
+      <td class="col-table-8" style="text-align: center; border:none">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border">
+    <tr>
+      <td class="col-table-4" style="text-align: center;">
+        <strong>RAZÓN O DENOMINACIÓN SOCIAL:</strong>
+      </td>
+      <td colspan="col-table-8" style="text-align: center;">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border">
+    <tr>
+      <td class="col-table-3">
+        <strong>NOMBRE DEL REPRESENTANTE:</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>APELLIDO PATERNO</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>APELLIDO MATERNO</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>NOMBRE(S)</strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-12" style="background:#cccccc;">
+        <strong>
+          DATOS DEL ACTO DE IMPUGNACIÓN
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#cccccc;">
+        <strong>SUJETO OBLIGADO QUE LO EMITIÓ</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= $recurso->solicitud->sujetoObligado->nombre ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#cccccc;">
+        <strong>ACTO IMPUGNADO</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= $recurso->actoImpugnado ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#cccccc;">
+        <strong>LUGAR Y FECHA DE LA EMISIÓN DEL ACTO</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= date('d/m/Y', strtotime($recurso->creado)) ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#cccccc;">
+        <strong>FECHA EN QUE SE TUVO CONOCIMIENTO DEL ACTO IMPUGNADO (dd /mm /aaaa) </strong>
+      </td>
+      <td class="col-table-4" style="">
+        <?= date('d/m/Y', strtotime($recurso->creado)) ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#cccccc;">
+        <strong>NÚMERO DE FOLIO O EXPEDIENTE DE LA SOLICITUD </strong>
+      </td>
+      <td class="col-table-4">
+        <?= $recurso->solicitud->folio ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#cccccc;">
+        <strong>RAZONES O MOTIVOS DE LA INCONFORMIDAD</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td colspan="12" style="text-align: justify;">
+        <?= $recurso->razonesMotivos ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-6 td-border" style=" background:#cccccc;">
+        <strong>DOCUMENTOS ANEXOS</strong>
+      </td>
+      <td class="col-table-6">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding ">
+    <tr>
+      <td class="col-table-5">
+        Poder
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Poder" ? "X" : "" ?>
+      </td>
+      <td class="col-table-5">
+        Copia de constancia de Notificación
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Copia de la Constancia de Notificación" ? "X" : "" ?>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-5">
+        Copia de la resolución
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Copia de Resolución" ? "X" : "" ?>
+      </td>
+      <td class="col-table-5">
+        Otros
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Otros" ? "X" : "" ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>Folio del recurso de</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <strong>00001/INFOEM/IP/RR/2023</strong>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>Clave de entrega del recurso de</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <strong>000042023001102500004205</strong>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+</div>

+ 263 - 0
modules/excel/views/requerimiento-aclaracion/formato.php

@@ -0,0 +1,263 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          SUJETO OBLIGADO
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4">
+        <strong>Fecha de Recepción(dd-mm-aaaa):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10/01/2023
+      </td>
+      <td class="col-table-2">
+        <strong>Hora(hh:mm):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10:25:00
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DATOS DEL SOLICITANTE
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-3 td-border td-center">
+        <strong>
+          DOMICILIO
+        </strong>
+      </td>
+      <td class="col-table-9 td-center">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          PAÍS:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          DOMICILIO:
+        </strong>
+      </td>
+      <td colspan="3" class="col-table-9 td-center td-subrayado ">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          CORREO ELECTRÓNICO:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3">
+        <strong>
+          TELÉFONO (Opcional):
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse ">
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de la Solicitud:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">00004/INFOSONORA/IP/2023</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de Recurso de Revisión:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">
+          00001/INFOEM/IP/RR/2023
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-10 td-border td-center">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border">
+    <tr>
+      <td class="col-table-12">
+        Solicitud de información del instituto de transparencia
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-8 td-border" colspan="2">
+        <strong>MODALIDAD DE ENTREGA</strong>
+      </td>
+      <td class="col-table-4"></td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-4">
+        A través del SAIMEX
+      </td>
+      <td class="col-table-4">
+        Copias Simples(con costo)
+      </td>
+      <td class="col-table-4">
+        Consulta Directa(sin costo)
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4">
+        CD-ROM(con costo)
+      </td>
+      <td class="col-table-4">
+        Copias Certificadas(con costo)
+      </td>
+      <td class="col-table-4">
+        Disquete 3.5(con costo)
+      </td>
+    </tr>
+    <tr>
+      <td>
+        OTRO TIPO DE MEDIO (Especificar):
+      </td>
+      <td colspan="2">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DOCUMENTOS ANEXOS
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          PLAZO DE RESPUESTA
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha límite de respuesta:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        15 días hábiles 31/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha de posible requerimiento de aclaración de la información :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles 17/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Notificación de ampliación de plazo(prórroga) :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        14 a 15 días hábiles 30/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Respuesta a la solicitud en caso de ampliación de plazo :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        22 días hábiles 10/02/2023
+      </td>
+    </tr>
+  </table>
+</div>

+ 63 - 0
modules/excel/views/requerimiento-aclaracion/oficio.php

@@ -0,0 +1,63 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-oficio">
+    <tr>
+      <td class="col-table-4 td-center">
+      </td>
+      <td class="col-table-8" style="text-align:right">
+        nse de Transparencia, Acceso a la Información Pública y Datos Personales,<br/>
+        México a 03 de Octubre de 2023<br/>
+        Nombre del solicitante: C. Solicitante<br/>
+        Folio de la solicitud: 00005/1NFOSONORA/1P/2023<br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Con fundamento en el artículo 159 de la Ley de Transparencia y Acceso a la Información Pública del Estado de
+        México y Municipios, se le requiere para que dentro del plazo de diez días hábiles realice lo siguiente:
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Solicito me aclare la información
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        En caso de que no se desahogue el requerimiento señalado dentro del plazo citado se tendrá por no presentada la
+        solicitud de información, quedando a salvo sus derechos para volver a presentar la solicitud, lo anterior con
+        fundamento en el artículo 159 de la Ley invocada.
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Respuesta del servidor público habilitado
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-oficio center " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        ATENTAMENTE<br/>
+        Unidad de Transparencia - Sujeto Obligado<br/>
+      </td>
+    </tr>
+  </table>
+</div>

+ 156 - 0
modules/excel/views/requerimiento-aclaracion/respuesta.php

@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\Aclaracion $aclaracion
+ * @var \v1\models\Usuario $solicitante
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$primerApellido = '';
+$segundoApellido = '';
+$nombre = '';
+
+$nombreCompleto = $solicitante->nombre;
+
+$nombreCompleto = explode(' ', $nombreCompleto);
+
+if (count($nombreCompleto) === 2) {
+  $primerApellido = $nombreCompleto[1];
+  $nombre = $nombreCompleto[0];
+} else if (count($nombreCompleto) === 3) {
+  $primerApellido = $nombreCompleto[2];
+  $segundoApellido = $nombreCompleto[1];
+  $nombre = $nombreCompleto[0];
+} else if (count($nombreCompleto) === 4) {
+  $primerApellido = $nombreCompleto[3];
+  $segundoApellido = $nombreCompleto[2];
+  $nombre = $nombreCompleto[0] . ' ' . $nombreCompleto[1];
+}
+
+?>
+<div class="container">
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td colspan="2" class="col-table-12 td-border" style="text-align: center; background:#cccccc; padding:9px 0">
+        <strong>DATOS DEL SOLICITANTE</strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border acuse td-padding">
+    <tr>
+      <td class="col-table-4" style="text-align: center;padding-top: 40px;border-bottom: 1px solid black">
+        <?= $primerApellido ?><br>
+      </td>
+      <td class="col-table-4" style="text-align: center;padding-top: 40px;border-bottom: 1px solid black">
+        <?= $segundoApellido ?><br>
+      </td>
+      <td class="col-table-4" style="text-align: center;padding-top: 40px;border-bottom: 1px solid black">
+        <?= $nombre ?><br>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4" style="text-align: center">
+        <strong>APELLIDO PATERNO</strong>
+      </td>
+      <td class="col-table-4" style="text-align: center">
+        <strong>APELLIDO MATERNO</strong>
+      </td>
+      <td class="col-table-4" style="text-align: center">
+        <strong>NOMBRE(S)</strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#cccccc;">
+        <strong>FECHA EN QUE SE TUVO CONOCIMIENTO DEL ACTO IMPUGNADO (dd /mm /aaaa) </strong>
+      </td>
+      <td class="col-table-4">
+        <?php
+        $date = new DateTime($aclaracion->creado);
+        echo $date->format('d-m-Y')
+        ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#cccccc;">
+        <strong>NÚMERO DE FOLIO O EXPEDIENTE DE LA SOLICITUD </strong>
+      </td>
+      <td class="col-table-4">
+        <?= $aclaracion->solicitud->folio ?>
+        <!-- 00004/INFOSONORA/IP/2023 -->
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#cccccc;">
+        <strong>DATOS A COMPLETAR, CORREGIR, AMPLIAR O ACLARAR</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+    <tr>
+      <td colspan="12" style="text-align: justify;">
+        <?= $aclaracion->solicitudAclaracion ?>
+        <!-- no fue claro la orientación a un trámite -->
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="td-border" style=" background:#cccccc;">
+        <strong>DOCUMENTOS ANEXOS</strong>
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <ul>
+          <?php foreach ($aclaracion->media as $media) : ?>
+            <li>
+              <a href="<?= $media->ruta ?>" target="_blank">
+                <?= $media->nombre ?>
+              </a>
+            </li>
+          <?php endforeach ?>
+        </ul>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>NÚMERO DE ACLARACIÓN</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <?= $aclaracion->solicitud->folio ?>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>CLAVE DE ENTREGA DE LA ACLARACIÓN</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        000042023001102500004205
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+</div>

+ 562 - 0
modules/excel/web/Controller.php

@@ -0,0 +1,562 @@
+<?php
+
+namespace excel\web;
+
+use Mpdf\Mpdf;
+use yii\filters\auth\CompositeAuth;
+use yii\filters\auth\QueryParamAuth;
+use yii\filters\Cors;
+use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Controller extends \yii\web\Controller {
+
+  /**
+   * Si es verdadero imprime el contenido en el web
+   * @var boolean $html
+   */
+  public $html = false;
+
+  /**
+   * Mostrar vista previa del pdf o descargar
+   * true = descargar
+   * @var boolean $descargar
+   */
+  public $descargar = false;
+
+  /**
+   * Configuración para la librería mpdf
+   * @var array $configuracion
+   */
+  public $configuracion = [
+    "format" => "letter",
+    "default_font" => "Roboto",
+  ];
+
+  /**
+   * Texto para la marca de agua
+   * @var string $marcaDeAguaTexto
+   */
+  public $marcaDeAguaTexto = "";
+
+  /**
+   * Habilitar la marca de agua
+   * @var boolean $html
+   */
+  public $marcaDeAgua = false;
+
+  /**
+   * Encoger las tablas para que quepan
+   * @var int $encogerTablas
+   */
+  public $encogerTablas = 0;
+
+  /**
+   * Mantener proporciones de tabla
+   * @var boolean $mantenerProporcionTabla
+   */
+  public $mantenerProporcionTabla = true;
+
+  /**
+   * Nombre del archivo al descargar
+   * @var string $nombreArchivo
+   */
+  public $nombreArchivo = "";
+
+  /**
+   * Estilos para el pdf
+   * @var string $hojaDeEstilo
+   */
+  public $hojaDeEstilo = "";
+
+  /**
+   * header para el pdf
+   * @var string $header
+   */
+  public $header;
+
+  /**
+   * @var \yii\web\Request $req
+   */
+  public $req;
+
+  /**
+   * @var \yii\web\Response $res
+   */
+  public $res;
+
+  //*
+  /* public function behaviors() {
+    $behavior = parent::behaviors();
+    $behavior["authenticator"] = [
+      "class" => CompositeAuth::className(),
+      "authMethods" => [
+        QueryParamAuth::className(),
+      ]
+    ];
+    return $behavior;
+  }  */// */
+
+  public function beforeAction($action) {
+    parent::beforeAction($action);
+
+    $basePath = \Yii::getAlias("@app");
+    $this->req = \Yii::$app->getRequest();
+    $this->res = \Yii::$app->getResponse();
+    $this->html = intval($this->req->get("html", 0)) === 1;
+
+    if ($this->html) {
+      $this->res->format = \yii\web\Response::FORMAT_HTML;
+    }
+
+    $this->descargar = intval($this->req->get("descargar", "")) === 1;
+    $this->marcaDeAgua = false; // intval($this->req->get("wm", 0)) === 1;
+    $this->hojaDeEstilo = file_get_contents("{$basePath}/web/css/pdf.css");
+
+    return true;
+  }
+
+  public static function crearPDF($nombreArchivo, $contenido, $configuracion, $descargar = true, $header = "", $footer = "", $marcaAgua = "") {
+    $basePath = \Yii::getAlias("@app");
+
+    $mpdf = new Mpdf($configuracion);
+    //$mpdf->WriteHTML($hojaDeEstilo, \Mpdf\HTMLParserMode::HEADER_CSS);
+    $mpdf->showWatermarkText = false;
+    $mpdf->watermarkTextAlpha = 0.30;
+    $mpdf->shrink_tables_to_fit = 0;
+    $mpdf->keep_table_proportions = true;
+    $mpdf->SetTitle($nombreArchivo);
+    $mpdf->SetDisplayMode('default');
+
+    $mpdf->SetHTMLHeader($header); //SetHTMLFooter($footer);
+
+    if ($footer !== "") {
+      $mpdf->SetHTMLFooter($footer); //SetHTMLFooter($footer);
+    } else {
+      $mpdf->SetFooter('Pag. {PAGENO} de {nbpg}');
+    }
+    if ($marcaAgua !== "") {
+      $mpdf->SetWatermarkText($marcaAgua);
+      $mpdf->showWatermarkText = true;
+    }
+    $mpdf->keep_table_proportions = TRUE;
+    $stylesheet = file_get_contents("{$basePath}/web/css/pdf/pdf.css");
+    $mpdf->WriteHTML($stylesheet, \Mpdf\HTMLParserMode::HEADER_CSS);
+
+    $mpdf->showImageErrors = false;
+    $mpdf->useSubstitutions = false;
+    $mpdf->simpleTables = false;
+    $mpdf->WriteHTML($contenido, \Mpdf\HTMLParserMode::HTML_BODY, ini_set("pcre.backtrack_limit", (strlen($contenido) * 5000)));
+    if ($descargar == true) {
+      return $mpdf->Output($nombreArchivo . ".pdf", \Mpdf\Output\Destination::STRING_RETURN);
+    } else {
+      $mpdf->Output($nombreArchivo . ".pdf", "I");
+      \Yii::$app->end();
+    }
+  }
+
+  public function exportarPdf($contenido) {
+    try {
+      // $config = array_merge($this->configuracion, ['format' => 'A4']);
+      $mpdf = new Mpdf($this->configuracion);
+      if (!empty($this->header)) {
+        $mpdf->SetHTMLHeader($this->header);
+      }
+      $mpdf->WriteHTML($this->hojaDeEstilo, \Mpdf\HTMLParserMode::HEADER_CSS);
+      $mpdf->SetWatermarkText($this->marcaDeAguaTexto);
+      $mpdf->watermark_font = 'DejaVuSansCondensed';
+      $mpdf->showWatermarkText = $this->marcaDeAgua;
+      $mpdf->watermarkTextAlpha = 0.30;
+      $mpdf->shrink_tables_to_fit = $this->encogerTablas;
+      $mpdf->keep_table_proportions = $this->mantenerProporcionTabla;
+      $mpdf->SetTitle($this->nombreArchivo);
+      $mpdf->SetDisplayMode('default');
+      $mpdf->SetFooter('Pag. {PAGENO} de {nbpg}');
+      $mpdf->showImageErrors = false;
+      $mpdf->useSubstitutions = false;
+      $mpdf->simpleTables = false;
+      $mpdf->WriteHTML($contenido, \Mpdf\HTMLParserMode::HTML_BODY);
+      $dest = $this->descargar ? "D" : "I";
+      if (strpos($this->nombreArchivo, '.pdf') === false) {
+        $this->nombreArchivo .= ".pdf";
+      }
+      header('Access-Control-Allow-Origin: *');
+      header('Access-Control-Expose-Headers: *');
+      $mpdf->Output($this->nombreArchivo, $dest);
+    } catch (\Exception $exception) {
+      throw $exception;
+    }
+    \Yii::$app->end();
+  }
+
+  public function afterAction($action, $result) {
+    if (!$this->html) {
+      $result = str_replace('disabled="disabled"', '', $result);
+      return $this->exportarPdf($result);
+    }
+    $this->marcaDeAgua = intval($this->req->get("wm", 1)) === 1;
+    $watermark = "background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' " .
+      "height='100px' width='100px'><text transform='translate(20, 100) rotate(-45)' fill='rgb(210,210,210)' " .
+      "font-size='18'>{$this->marcaDeAguaTexto}</text></svg>\");";
+    if (!$this->marcaDeAgua) {
+      $watermark = "";
+    }
+    $fondo = ".fondo-privado { background-color: rgb(141,216,169,0.7) !important; }";
+    $result = str_replace("<pagebreak>", "<br>", $result);
+    $result = "<style type=\"text/css\">{$this->hojaDeEstilo}\nbody{{$watermark}}\n{$fondo}</style>{$result}";
+    return $result;
+  }
+
+  /**
+   * funcion para generar cualquier Excel
+   */
+  public static function Excel(
+    $titulo = "Reporte",
+    $pestania = "Reporte", $nombre = "Reporte", $etiquetas = [], $campos = [], $datos, $usarApuntador = true, $isArray = false, $respaldo = false) {
+    // Create new Spreadsheet object
+    $basePath = \Yii::getAlias("@app");
+
+    $spreadsheet = new Spreadsheet();
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getSecurity()->setLockWindows(false);
+    $spreadsheet->getSecurity()->setLockStructure(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSheet(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSort(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setInsertRows(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setFormatCells(false);
+
+    // Set document properties
+    $spreadsheet->getProperties()->setCreator('ISTAI')->setLastModifiedBy('ISTAI')->setTitle($titulo)
+      ->setDescription($titulo);
+
+    $spreadsheet->getActiveSheet()->setTitle($pestania);
+    $style_titulo = [
+      'font' => [
+        'bold' => true,
+        'size' => 13,
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+      ]
+    ];
+
+    $style_titulo_etiquestas = [
+      'font' => [
+        'bold' => true,
+        'size' => 11,
+        'color' => ['rgb' => '000000'],
+        'background' => ['rgb' => '4c5966'],
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+      ],
+      'fill' => [
+        'type' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_GRADIENT_LINEAR,
+        'rotation' => 90,
+        'startcolor' => ['argb' => '000000',],
+        'endcolor' => ['argb' => '000000',],
+      ],
+    ];
+
+    $spreadsheet->setActiveSheetIndex(0);
+    $i = $respaldo ? 1 : 7;
+    $ltrs = [];
+    $ltr2 = 65;
+    $ltr = 65;
+    foreach ($etiquetas as $v) {
+      if ($ltr > 90) {
+        $letra = "A" . chr($ltr2);
+      } else {
+        $letra = chr($ltr);
+      }
+      $ltrs[] = $letra;
+
+      $spreadsheet->getActiveSheet()->setCellValue($letra . $i, $v);
+      if ($ltr > 90) {
+        $ltr2++;
+      }
+      $ltr++;
+    }
+
+    // Add some data
+    $i++; //Es el renglón inicial
+    if ($usarApuntador) {
+      /* @var $datos ActiveQuery */
+      if (!$isArray) {
+        foreach ($datos->each() as $v) {
+          $l = 0;
+          foreach ($campos as $k => $a) {
+            $spreadsheet
+              ->getActiveSheet()
+              ->setCellValue($ltrs[$l] . $i, isset($v[$a]) ? $v[$a] : "")
+              ->getColumnDimension($ltrs[$l])
+              ->setAutoSize(true);
+            $l++;
+          }
+          $i++;
+        }
+      } else {
+        foreach ($datos as $v) {
+          $l = 0;
+          foreach ($campos as $k => $a) {
+            $spreadsheet
+              ->getActiveSheet()
+              ->setCellValue($ltrs[$l] . $i, isset($v[$a]) ? $v[$a] : "")
+              ->getColumnDimension($ltrs[$l])
+              ->setAutoSize(true);
+            $l++;
+          }
+          $i++;
+        }
+      }
+
+    } else {
+      foreach ($datos as $v) {
+        $l = 0;
+        foreach ($campos as $k => $a) {
+          $spreadsheet
+            ->getActiveSheet()
+            ->setCellValue($ltrs[$l] . $i, isset($v[$a]) ? $v[$a] : "")
+            ->getColumnDimension($ltrs[$l])
+            ->setAutoSize(true);
+          $l++;
+        }
+        $i++;
+      }
+
+    }
+    // Rename worksheet
+
+    // Set active sheet index to the first sheet, so Excel opens this as the first sheet
+    $spreadsheet->setActiveSheetIndex(0);
+
+    $ultima_letra = array_pop($ltrs);
+
+    $style_titulo_etiquestas = [
+      'font' => [
+        'bold' => true,
+        'size' => 14,
+        'color' => ['rgb' => 'ffffff'],
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+      ],
+      'fill' => [
+        'fillType' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID,
+        'startColor' => [
+          'argb' => '00736c',
+        ],
+      ],
+    ];
+    $spreadsheet->getActiveSheet()->getStyle("A7:" . $ultima_letra . "7")->applyFromArray($style_titulo_etiquestas);
+
+
+
+    if (!$respaldo) {
+      $spreadsheet->getActiveSheet()->mergeCells('A3:' . $ultima_letra . "3");
+      $spreadsheet->getActiveSheet()->getStyle("A3:" . $ultima_letra . "3")->applyFromArray($style_titulo);
+      $spreadsheet->getActiveSheet()->setCellValue('A3', $titulo);
+      //$spreadsheet->getActiveSheet()->setCellValue('A2', '=HIPERVINCULO("http://www.google.com/","Google")');
+
+      $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+      $objDrawing->setName('Logo');
+      $objDrawing->setDescription('Logo');
+      $objDrawing->setPath($basePath . '/web/img/logo_istai_lg.png');
+      $objDrawing->setWidth(200);
+      $objDrawing->setCoordinates('A1');
+      $objDrawing->setWorksheet($spreadsheet->getActiveSheet());
+
+      /*$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+      $objDrawing->setName('Logo');
+      $objDrawing->setDescription('Logo');
+      $objDrawing->setPath($basePath . '/web/img/logo_istai_lg.png');
+      $objDrawing->setWidth(150);
+      $objDrawing->setCoordinates('M1');
+      $objDrawing->setWorksheet($spreadsheet->getActiveSheet());
+      $spreadsheet->getActiveSheet()->setCellValue('N3', " ");
+      $spreadsheet->getActiveSheet()->setCellValue('O3', " ");*/
+
+      //$spreadsheet->getActiveSheet()->getStyle("A7:" . $ultima_letra . "7")->applyFromArray($style_titulo_etiquestas);
+    }
+
+
+    // Redirect output to a client's web browser (Xlsx)
+    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+    header('Content-Disposition: attachment;filename="' . $nombre . '.xlsx"');
+    header('Cache-Control: max-age=0');
+
+    $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
+    $writer->save('php://output');
+    exit;
+  }
+
+  public function excelMir($datos = []) {
+    // Create new Spreadsheet object
+    $nombre = "excels.xlsx";
+    $titulo = "Matriz de Indicadores para Resultados (2022)";
+    $pestania = "pestania";
+
+    $spreadsheet = new Spreadsheet();
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getSecurity()->setLockWindows(false);
+    $spreadsheet->getSecurity()->setLockStructure(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSheet(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSort(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setInsertRows(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setFormatCells(false);
+
+    // Set document properties
+    $spreadsheet->getProperties()->setCreator('pbr')->setLastModifiedBy('pbr')->setTitle($titulo)->setDescription($titulo);
+
+    $spreadsheet->getActiveSheet()->setTitle($pestania);
+    $style_titulo = [
+      'font' => [
+        'bold' => true,
+        'size' => 13,
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+      ]
+    ];
+
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getActiveSheet()->setCellValue("A8", "Dependencia y/o Entidad:");
+    $spreadsheet->getActiveSheet()->setCellValue("A9", "Programa Presupuestario:");//Eje De PED:
+    $spreadsheet->getActiveSheet()->setCellValue("A10", "Eje De PED:");//:
+    $spreadsheet->getActiveSheet()->setCellValue("A11", "Objetivo del PED:");
+    $spreadsheet->getActiveSheet()->setCellValue("A12", "Beneficiarios:");
+
+    // Rename worksheet
+    // Set active sheet index to the first sheet, so Excel opens this as the first sheet
+    $spreadsheet->setActiveSheetIndex(0);
+
+    $spreadsheet->getActiveSheet()->mergeCells('F4:K4');
+    $spreadsheet->getActiveSheet()->getStyle('F4:K4')->applyFromArray($style_titulo);
+    $spreadsheet->getActiveSheet()->setCellValue('F4', $titulo);
+    $losbenificiarios = "";
+    if (count($datos["benificiarios"]) > 0) {
+      foreach ($datos["benificiarios"] as $ben) {
+        $losbenificiarios .= "{$ben->beneficiario->nombre}, ";
+      }
+    }
+
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getActiveSheet()->setCellValue("C8", $datos["dependencia"]->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C9", $datos["programa"]->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C10", $datos["eje"]->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C11", $datos["mir"]->objetivo->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C12", $losbenificiarios);
+
+    $spreadsheet->getActiveSheet()->setCellValue("A14", "");
+    $spreadsheet->getActiveSheet()->setCellValue("B14", "Resumen Narrativo\n(Objetivos)");
+    $spreadsheet->getActiveSheet()->mergeCells('B14:B15');
+    $spreadsheet->getActiveSheet()->setCellValue("C14", "Indicadores");
+    $spreadsheet->getActiveSheet()->mergeCells('C14:E14');
+
+    $spreadsheet->getActiveSheet()->setCellValue("F14", "Programación");
+    $spreadsheet->getActiveSheet()->mergeCells('F14:J14');
+    $spreadsheet->getActiveSheet()->setCellValue("K14", "Meta % de Anua");
+    $spreadsheet->getActiveSheet()->setCellValue("L14", "% de Avance");
+    $spreadsheet->getActiveSheet()->setCellValue("M14", "Linea Base (año base)");
+    $spreadsheet->getActiveSheet()->setCellValue("N14", "Sentido");
+    $spreadsheet->getActiveSheet()->setCellValue("O14", "Frecuencia");
+    $spreadsheet->getActiveSheet()->setCellValue("P14", "Medios de Verificación\n(Fuentes)");
+    $spreadsheet->getActiveSheet()->setCellValue("Q14", "Supuestos");
+    $spreadsheet->getActiveSheet()->mergeCells('K14:K15');
+    $spreadsheet->getActiveSheet()->mergeCells('L14:L15');
+    $spreadsheet->getActiveSheet()->mergeCells('M14:M15');
+    $spreadsheet->getActiveSheet()->mergeCells('N14:N15');
+    $spreadsheet->getActiveSheet()->mergeCells('O14:O15');
+    $spreadsheet->getActiveSheet()->mergeCells('P14:P15');
+    $spreadsheet->getActiveSheet()->mergeCells('Q14:Q15');
+
+    $spreadsheet->getActiveSheet()->getStyle('A14:Q14')->applyFromArray($style_titulo);
+    $spreadsheet->getActiveSheet()->getStyle('A15:Q15')->applyFromArray($style_titulo);
+    $spreadsheet->getActiveSheet()->setCellValue("C15", "Nombre");
+    $spreadsheet->getActiveSheet()->setCellValue("D15", "Unidad de\nMedida");
+    $spreadsheet->getActiveSheet()->setCellValue("E15", "Fórmula");
+    $spreadsheet->getActiveSheet()->setCellValue("F15", "I");
+    $spreadsheet->getActiveSheet()->setCellValue("G15", "II");
+    $spreadsheet->getActiveSheet()->setCellValue("H15", "III");
+    $spreadsheet->getActiveSheet()->setCellValue("I15", "IV");
+    $spreadsheet->getActiveSheet()->setCellValue("J15", "Avance\nAcumulado");
+
+    $objDrawing = $this->cargarImagen($spreadsheet, '/web/img/logo-salud.png', 300, "A1");
+    $objDrawing = $this->cargarImagen($spreadsheet, '/web/img/sa.png', 150, "Q5");
+    $spreadsheet->getActiveSheet()->setCellValue('R3', " ");
+
+    $i = 16;
+    if ($datos["mir"] != null) {
+      $nuevoNiveles = [];
+      foreach ($datos["niveles"] as $nivel) {
+        if (!isset($nuevoNiveles[$nivel->nivel])) {
+          $nuevoNiveles[$nivel->nivel] = [];
+        }
+        $nuevoNiveles[$nivel->nivel][] = $nivel;
+      }
+
+      $ordenNivel = [
+        "FIN",
+        "PROPÓSITO",
+        "COMPONENTE",
+        "ACTIVIDAD"
+      ];
+
+      foreach ($ordenNivel as $k => $v) {
+        $inicio = $i;
+        $spreadsheet->getActiveSheet()->setCellValue("A" . $i, $v);
+        $fin = 0;
+        foreach ($nuevoNiveles[$v] as $nivel) {
+          $spreadsheet->getActiveSheet()->setCellValue("B" . $i, $nivel->resumen);
+          foreach ($nivel->matrizMIRIndicadores as $indicador) {
+            $spreadsheet->getActiveSheet()->setCellValue("C" . $i, $indicador->nombre);
+            $spreadsheet->getActiveSheet()->setCellValue("D" . $i, $indicador->unidadMedida->nombre);
+            $spreadsheet->getActiveSheet()->setCellValue("E" . $i, $indicador->numerador);
+            $spreadsheet->getActiveSheet()->setCellValue("E" . ($i + 1), $indicador->denominador);
+            $spreadsheet->getActiveSheet()->setCellValue("F" . $i, $indicador->numeradorT1);
+            $spreadsheet->getActiveSheet()->setCellValue("G" . $i, $indicador->numeradorT2);
+            $spreadsheet->getActiveSheet()->setCellValue("H" . $i, $indicador->numeradorT3);
+            $spreadsheet->getActiveSheet()->setCellValue("I" . $i, $indicador->numeradorT4);
+
+            $spreadsheet->getActiveSheet()->setCellValue("F" . ($i + 1), $indicador->denominadorT1);
+            $spreadsheet->getActiveSheet()->setCellValue("G" . ($i + 1), $indicador->denominadorT2);
+            $spreadsheet->getActiveSheet()->setCellValue("H" . ($i + 1), $indicador->denominadorT3);
+            $spreadsheet->getActiveSheet()->setCellValue("I" . ($i + 1), $indicador->denominadorT4);
+
+            $spreadsheet->getActiveSheet()->setCellValue("J" . $i, $indicador->avanceAcumulado);
+            $spreadsheet->getActiveSheet()->setCellValue("K" . $i, $indicador->metaAnual);
+            $spreadsheet->getActiveSheet()->setCellValue("L" . $i, $indicador->porcentajeAvance);
+            $spreadsheet->getActiveSheet()->setCellValue("M" . $i, $indicador->lineaBase);
+            $spreadsheet->getActiveSheet()->setCellValue("N" . $i, $indicador->sentido->valor);
+            $spreadsheet->getActiveSheet()->setCellValue("O" . $i, $indicador->frecuencia->nombre);
+            $spreadsheet->getActiveSheet()->setCellValue("P" . $i, $indicador->metodoVerificacion);
+            $spreadsheet->getActiveSheet()->setCellValue("Q" . $i, $indicador->supuestos);
+            $spreadsheet->getActiveSheet()->setCellValue("R" . $i, " ");
+            $i += 2;
+          }
+          //$spreadsheet->getActiveSheet()->mergeCells("B{$inicio}:B{$f}");
+        }
+        //$fin = $i+1;
+        $spreadsheet->getActiveSheet()->mergeCells("A{$inicio}:A17");
+      }
+    }
+    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+    header('Content-Disposition: attachment;filename="' . $nombre . '"');
+    header('Cache-Control: max-age=0');
+
+    $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
+    $writer->save('php://output');
+    exit;
+  }
+
+  function cargarImagen($spreadsheet, $imagen = "/web/img/logo-salud.png", $width = 300, $ubicacion = "A1") {
+    $basePath = \Yii::getAlias("@app");
+    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+    $objDrawing->setName('Logo');
+    $objDrawing->setDescription('Logo');
+    $objDrawing->setPath($basePath . $imagen);
+    $objDrawing->setWidth($width);
+    $objDrawing->setCoordinates($ubicacion);
+    $objDrawing->setWorksheet($spreadsheet->getActiveSheet());
+  }
+}

+ 49 - 0
modules/mail/Module.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace app\modules\mail;
+
+use Yii;
+
+/**
+ * v1 module definition class
+ */
+class Module extends \yii\base\Module implements \yii\base\BootstrapInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $controllerNamespace = 'app\modules\mail\controllers';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    parent::init();
+
+    $app = Yii::$app;
+    if(!($app instanceof \yii\console\Application)){
+      $response = $app->getResponse();
+      $headers = $response->getHeaders();
+      $params = \Yii::$app->params;
+
+      $headers->set('Access-Control-Allow-Methods', '*');
+      $headers->set('Access-Control-Allow-Headers', '*');
+      $headers->set('Access-Control-Allow-Origin', '*');
+      $headers->set('Access-Control-Request-Method', 'POST, GET, DELETE, PUT, OPTIONS');
+      $headers->set('Access-Control-Allow-Credentials', 'true');
+      $headers->set('Access-Control-Max-Age', 86400);
+      if (Yii::$app->getRequest()->isOptions) {
+        Yii::$app->end();
+      }
+      \Yii::$app->getUser()->enableSession = false;
+      \Yii::$app->getUser()->identityClass = 'app\models\Usuario';
+    }
+  }
+
+  public function bootstrap($app) {
+    if ($app instanceof \yii\console\Application) {
+      $this->controllerNamespace = 'app\modules\mail\commands';
+    }
+  }
+
+}

+ 73 - 0
modules/mail/README.md

@@ -0,0 +1,73 @@
+#### Ejecutar la migración
+
+```
+php yii migrate --migrationPath=@app/modules/mail/migrations
+```
+
+### Revisar que la configuración para envío de correo este correcto
+
+```
+$config = [
+  // ..
+  'components' => [
+    // ...
+    'mailer' => [
+      'class' => 'yii\swiftmailer\Mailer',
+      'useFileTransport' => false,
+      'transport' => [
+        'class' => 'Swift_SmtpTransport',
+        'host' => 'smtp.gmail.com',
+        'username' => 'correo@gmail.com',
+        'password' => 'contraseña',
+        'port' => '587',
+        'encryption' => 'tls',
+      ],
+    ],
+  ]
+  // ...
+];
+```
+
+#### Correr el comando de migración
+
+Agregar al archivo config/console.php las siguientes líneas
+
+```
+$config['bootstrap'][] = 'mail';
+$config['modules']['mail'] = ['class' => 'app\modules\mail\Controller'];
+```
+
+#### Ejecutar el comando para enviar el correo
+```
+php yii mail/cron
+```
+
+#### Guardar notificaciones
+
+##### Guardado manual de notificación
+```
+$modelo = NotificacionCorreo::crear($prioridad = 3)
+  ->setReceptor(["mail@gmail.com" => "Nombre del Receptor"])
+  ->setAsunto("Asunto del correo")
+  ->setCuerpo("Cuerpo del corre puede ser <strong>HTML</strong>")
+
+if($modelo->save()) {
+  echo "Guardado correcto";
+}
+```
+
+##### Guardado desde un arreglo
+```
+$parametros = [
+  "prioridad" => NotificacionCorreo::PRIORIDAD_3,
+  "asunto" => "Asunto del correo",
+  "cuerpo" => "Este es el cuerpo del correo, puede ser <strong>HTML</strong>",
+  "receptores" => [
+    "hquijada@edesarrollos.com"
+  ],
+  "adjuntos" => [# Debe contener la(s) ruta(s) al(los) archivo(s)
+  ]
+];
+
+$resultado = NotificacionCorreo::enviarMultiple($parametros);
+```

+ 96 - 0
modules/mail/commands/CronController.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace app\modules\mail\commands;
+
+use app\modules\mail\models\NotificacionCorreo;
+use app\modules\mail\models\NotificacionCorreoAdjunto;
+use yii\console\Controller;
+use yii\db\Expression;
+
+class CronController extends Controller {
+
+  // ¿Cuántos correos se enviarán por minuto?
+  # TODO: Se debe enviar uno cada 3 minutos para 
+  private $limite = 1;
+  # Este debe venir de un archivo de configuración o de alguna tabla
+  private $correoDesde = ["siistai@transparenciasonora.org" => "Notificación SIISTAI"];
+  private $correoAResponder = "transparenciasonora22@gmail.com";
+
+  public function actionCheck() {
+    $this->stdout("checked \n");
+  }
+
+  public function actionIndex() {
+
+    $notificaciones = NotificacionCorreo::find()
+      ->andWhere([
+        "estatus" => NotificacionCorreo::ESTATUS_NUEVO,
+        "enviado" => null
+      ])
+      ->orderBy(["prioridad" => SORT_ASC])
+      ->limit($this->limite);
+
+    foreach ($notificaciones->each() as $notif) {
+      /** @var NotificacionCorreo $notif */
+      # Guardar el que se este procesando
+      $notif->estatus = NotificacionCorreo::ESTATUS_PROCESO;
+      $notif->modificado = new Expression('now()');
+      $notif->save();
+
+      $view = new \yii\web\View();
+      $contenido = $view->render("@app/modules/mail/views/layouts/cuerpo", [
+        "cuerpo" => $notif->cuerpo
+      ]);
+
+      $destinos = $notif->receptor;
+      if (!is_array($destinos)) {
+        $notif->estatus = NotificacionCorreo::ESTATUS_ERROR;
+        $notif->detalle = "No hay destinos para el correo";
+        $notif->save();
+        continue;
+      }
+      foreach ($destinos as $indice => $valor) {
+        $destino = [$indice => $valor];
+        if (is_numeric($indice)) {
+          $destino = $valor;
+        }
+        try {
+
+          $correo = \Yii::$app->mailer->compose()
+            ->setFrom($this->correoDesde)
+            // ->setReplyTo($this->correoAResponder)
+            ->setTo($destino)
+            ->setSubject($notif->asunto)
+            ->setHtmlBody($contenido);
+
+          foreach ($notif->adjuntos as $adjunto) {
+            $ruta = str_replace("https://sistai.web.app/", \Yii::getAlias("@app"), $adjunto->ruta);
+            if (is_file($ruta)) {
+              $correo->attach($ruta);
+            }
+          }
+
+          $resultado = $correo->send();
+
+          if ($resultado) {
+            $notif->enviado = new Expression('now()');
+            $notif->estatus = NotificacionCorreo::ESTATUS_ENVIADO;
+          } else {
+            $notif->estatus = NotificacionCorreo::ESTATUS_ERROR;
+            $notif->detalle = $correo->toString(); # Buscar la manera de obtener el error
+          }
+          $notif->save();
+
+          $this->stdout("\n");
+        } catch (\Exception $e) {
+
+          $notif->estatus = $notif::ESTATUS_ERROR;
+          $notif->detalle = $e->getMessage();
+          $notif->save();
+
+          $this->stdout(" Ocurrió un error al guardar {$e->getMessage()}\n");
+        }
+      }
+    }
+  }
+}

+ 46 - 0
modules/mail/commands/NotificacionController.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace app\modules\mail\commands;
+
+use app\modules\mail\models\NotificacionCorreo;
+use yii\console\Controller;
+
+class NotificacionController extends Controller {
+
+  public function stdout($msg) {
+    return parent::stdout("{$msg}\n");
+  }
+
+  public function actionGuardar() {
+    $modelo = NotificacionCorreo::crear()
+      ->setReceptor([
+        "hquijada@edesarrollos.com" => "Hugo Quijada",
+        "rsotobernal@edesarrollos.com" => "Rafael Soto"
+      ])
+      ->setAsunto("Correo con header y footer")
+      ->setCuerpo("<h1>Título del Mensaje</h1><br><p>Este es un ejemplo de cómo enviamos un mensaje alerta desde el sistema</p>");
+
+    if(!$modelo->save()) {
+      $this->stdout(json_encode($modelo->getFirstErrors()));
+    }
+
+    $this->stdout("Proceso terminado");
+  }
+
+  public function actionGuardarMultiple() {
+    $parametros = [
+      "asunto" => "Asunto del correo",
+      "cuerpo" => "Este es el cuerpo del correo, puede ser <strong>HTML</strong>",
+      "receptores" => [
+        "hquijada@edesarrollos.com"
+      ],
+      "adjuntos" => [# Debe contener la(s) ruta(s) al(los) archivo(s)
+      ]
+    ];
+
+    $resultado = NotificacionCorreo::enviarMultiple($parametros);
+
+    var_dump($resultado);
+  }
+
+} 

+ 52 - 0
modules/mail/controllers/NotificacionController.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace app\modules\mail\controllers;
+
+use app\modules\mail\models\NotificacionCorreo;
+use Yii;
+use yii\rest\Controller;
+use yii\web\BadRequestHttpException;
+use yii\web\ForbiddenHttpException;
+
+# TODO: Cambiar por un controlador con seguridad
+class NotificacionController extends Controller {
+  
+  const TIPO_UNO = "uno";
+  const TIPO_MULTIPLE = "multiple";
+
+  public function actionIndex() {
+    $req = Yii::$app->getRequest();
+    if(!$req->isPost) {
+      # TODO: Cambiar por respuestas
+      throw new ForbiddenHttpException("El método debe enviarse por POST");
+    }
+    # Para indicar si se envía un correo por receptor o un correo con multiple receptores
+    # valores: uno o multiple
+    $tipo = $req->getBodyParam("tipo", "");
+
+    $tran = Yii::$app->getDb()->beginTransaction();
+    try {
+      if($tipo === self::TIPO_UNO) {
+        $modelo = NotificacionCorreo::enviar($req->getBodyParams());
+      } elseif($tipo === self::TIPO_MULTIPLE) {
+        $modelo = NotificacionCorreo::enviarMultiple($req->getBodyParams());
+      }
+      if($modelo->hasErrors()) {
+        foreach($modelo->getFirstErrors() as $error) {
+          throw new BadRequestHttpException($error);
+        }
+      }
+      $tran->commit();
+    } catch(BadRequestHttpException $e) {
+      $tran->rollBack();
+
+      throw $e;
+    } catch(\Exception $e) {
+      $tran->rollBack();
+
+      throw new BadRequestHttpException("Ocurrió un error en el servidor: {$e->getMessage()}");
+    }
+
+  }
+
+}

+ 36 - 0
modules/mail/controllers/VistaPreviaController.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace app\modules\mail\controllers;
+
+use v1\models\RecursoRevision;
+use v1\models\Solicitud;
+use v1\models\SolicitudAcuse;
+
+class VistaPreviaController extends \yii\web\Controller {
+
+  public function actionIndex() {
+    $req = \Yii::$app->getRequest();
+    $archivo = $req->get("archivo", "ejemplo/html");
+
+    return $this->render("@app/modules/mail/views/{$archivo}");
+  }
+
+  public function actionCuerpo() {
+    return $this->render("@app/modules/mail/views/layouts/cuerpo", ["cuerpo" => "<h1>Hola mundo</h1>"]);
+  }
+
+  public function actionPruebaCorreo() {
+    $modelo = Solicitud::findOne('e03b89a0-09a1-4f9e-9d78-827f5004b048');
+    return $this->render("@app/modules/mail/views/layouts/cuerpo", ["cuerpo" => $this->renderPartial("@app/modules/v1/views/solicitud/acuse", ["solicitud" => $modelo, "usuario" => $modelo->usuario])]);
+  }
+
+  public function actionPruebaCorreoRr() {
+    $modelo = RecursoRevision::findOne('1fc99a78-23e6-4a92-8a97-0ca81a171f02');
+    return $this->render("@app/modules/mail/views/layouts/cuerpo", ["cuerpo" => $this->renderPartial("@app/modules/v1/views/recurso-revision/acuse", ["recurso" => $modelo/* , "usuario" => $modelo->usuario */])]);
+  }
+
+  public function actionPruebaAcuse() {
+    $modelo = SolicitudAcuse::findOne('e1a66644-81b7-4824-838b-85ef34f83f75');
+    return $this->render("@app/modules/mail/views/layouts/cuerpo", ["cuerpo" => $this->renderPartial("@app/modules/v1/views/solicitud-acuse/correo", ["modelo" => $modelo])]);
+  }
+}

+ 47 - 0
modules/mail/migrations/m231004_195249_mailer.php

@@ -0,0 +1,47 @@
+<?php
+
+use yii\db\Migration;
+
+/**
+ * Class m231004_195249_mailer
+ */
+class m231004_195249_mailer extends Migration {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function safeUp() {
+
+    $this->createTable("NotificacionCorreo", [
+      "id" => $this->primaryKey(),
+      "receptor" => $this->json()->notNull(),
+      "asunto" => $this->string()->notNull(),
+      "cuerpo" => $this->text()->notNull(),
+      "estatus" => $this->string(20),
+      "detalle" => $this->text(),
+      "prioridad" => $this->smallInteger()->comment("1, 2 o 3"),
+      "enviado" => $this->timestamp() . ' with time zone',
+      "creado" => $this->timestamp() . ' with time zone',
+      "modificado" => $this->timestamp() . ' with time zone',
+      "eliminado" => $this->timestamp() . ' with time zone',
+    ]);
+
+    $this->createTable("NotificacionCorreoAdjunto", [
+      "id" => $this->primaryKey(),
+      "idNotificacionCorreo" => $this->integer(),
+      "ruta" => $this->string()
+    ]);
+
+    $this->addForeignKey("NCAidNotificacionCorreoFK", "NotificacionCorreoAdjunto", "idNotificacionCorreo", "NotificacionCorreo", "id");
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function safeDown() {
+    $this->dropForeignKey("NCAidNotificacionCorreoFK", "NotificacionCorreoAdjunto");
+    $this->dropTable("NotificacionCorreoAdjunto");
+    $this->dropTable("NotificacionCorreo");
+  }
+}

+ 234 - 0
modules/mail/models/NotificacionCorreo.php

@@ -0,0 +1,234 @@
+<?php
+
+namespace app\modules\mail\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "NotificacionCorreo".
+ *
+ * @property int $id
+ * @property string $receptor
+ * @property string $asunto
+ * @property string $cuerpo
+ * @property string|null $estatus
+ * @property string|null $detalle
+ * @property int $prioridad
+ * @property string|null $enviado
+ * @property string|null $creado
+ * @property string|null $modificado
+ * @property string|null $eliminado
+ *
+ * @property NotificacionCorreoAdjunto[] $adjuntos
+ */
+class NotificacionCorreo extends \yii\db\ActiveRecord {
+
+  const ESTATUS_NUEVO = "Nuevo";
+  const ESTATUS_PROCESO = "Proceso";
+  const ESTATUS_ENVIADO = "Enviado";
+  const ESTATUS_ERROR = "Error";
+
+  const PRIORIDAD_1 = 1;
+  const PRIORIDAD_2 = 2;
+  const PRIORIDAD_3 = 3;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'NotificacionCorreo';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['receptor', 'asunto', 'cuerpo'], 'required'],
+      [['receptor', 'enviado', 'creado', 'modificado', 'eliminado'], 'safe'],
+      [['prioridad'], 'integer'],
+      [['cuerpo', 'detalle'], 'string'],
+      [['asunto'], 'string', 'max' => 255],
+      [['estatus'], 'string', 'max' => 20],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'receptor' => 'Receptor',
+      'asunto' => 'Asunto',
+      'cuerpo' => 'Cuerpo',
+      'estatus' => 'Estatus',
+      'detalle' => 'Detalle',
+      'enviado' => 'Enviado',
+      'creado' => 'Creado',
+      'modificado' => 'Modificado',
+      'eliminado' => 'Eliminado',
+    ];
+  }
+
+  /**
+   * Gets query for [[NotificacionCorreoAdjuntos]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getAdjuntos() {
+    return $this->hasMany(NotificacionCorreoAdjunto::class, ['idNotificacionCorreo' => 'id']);
+  }
+
+  private function validarCampos($params) {
+    if(isset($params["prioridad"]) && !in_array($params["prioridad"], [self::PRIORIDAD_1, self::PRIORIDAD_2, self::PRIORIDAD_3])) {
+      $this->addError("prioridad", "La prioridad no es válida");
+      return false;
+    } elseif(!isset($params["asunto"])) {
+      $this->addError("estatus", "El Asunto es obligatorio");
+      return false;
+    } elseif(trim($params["asunto"]) === "") {
+      $this->addError("estatus", "El Asunto no puede estar vacío");
+      return false;
+    }
+    if(!isset($params["cuerpo"])) {
+      $this->addError("estatus", "El Cuerpo es obligatorio");
+      return false;
+    } elseif(trim($params["cuerpo"]) === "") {
+      $this->addError("estatus", "El Cuerpo no puede estar vacío");
+      return false;
+    }
+    if(!isset($params["receptores"])) {
+      $this->addError("estatus", "El Receptor es obligatorio");
+      return false;
+    } elseif(!is_array($params["receptores"]) || empty($params["receptores"])) {
+      $this->addError("estatus", "El Receptor debe ser un arreglo");
+      return false;
+    }
+
+    if(isset($params["adjuntos"]) && !is_array($params["adjuntos"])) {
+      $this->addError("estatus", "Los Adjuntos debe ser un arreglo");
+    }
+
+    return true;
+  }
+
+  /**
+   * Enviar un correo con muchos receptores
+   */
+  public static function enviar($params) {
+    $modelo = self::crear();
+    if(!$modelo->validarCampos($params)) {
+      return $modelo;
+    }
+
+    $modelo->setAsunto($params["asunto"])
+      ->setCuerpo($params["cuerpo"])
+      ->setReceptor($params["receptor"]);
+
+    if(!$modelo->save()) {
+      return $modelo;
+    }
+
+    if(isset($params["adjuntos"])) {
+      $modelo->setAdjuntos($params["adjuntos"]);
+    }
+
+    $modelo->refresh();
+    return $modelo;
+  }
+
+  /**
+   * Por cada receptor genera un registro de notificación
+   * 
+   * @param $params Arreglo con los parámetros para enviar un correo
+   * @return null|static Regresa una instancia del modelo o null en caso de error
+   */
+  public static function enviarMultiple($params) {
+    $modelo = self::crear();
+    if(!$modelo->validarCampos($params)) {
+      return $modelo;
+    }
+    $prioridad = self::PRIORIDAD_3;
+    if(isset($params["prioridad"])) {
+      $prioridad = $params["prioridad"];
+    }
+
+    try {
+      $modelo = null;
+      foreach($params["receptores"] as $indice => $valor) {
+        $receptor = [];
+        if(is_numeric($indice)) {
+          # No tiene nombre de la persona a la que se le envía
+          $receptor[] = $valor;
+        } else {
+          # $indice es equivalente al correo y $valor al nombre
+          $receptor[$indice] = $valor;
+        }
+  
+        $modelo = (self::crear($prioridad))
+          ->setAsunto($params["asunto"])
+          ->setCuerpo($params["cuerpo"])
+          ->setReceptor($receptor);
+  
+        if(!$modelo->save()) {
+          return $modelo;
+        }
+  
+        if(isset($params["adjuntos"])) {
+          $modelo->setAdjuntos($params["adjuntos"]);
+        }
+      }
+  
+      return $modelo;
+    } catch(\Exception $e) {
+      # Revisar el contenido de la variable $e si el resultado es nulo
+      return null;
+    }
+  }
+
+  public static function crear($prioridad = self::PRIORIDAD_3) {
+    $modelo = new self();
+    $modelo->creado = new \yii\db\Expression('now()');
+    $modelo->estatus = self::ESTATUS_NUEVO;
+    $modelo->prioridad = $prioridad;
+    return $modelo;
+  }
+
+  public function setAsunto($asunto = "") {
+    $this->asunto = $asunto;
+    return $this;
+  }
+
+  public function setCuerpo($cuerpo = "") {
+    $this->cuerpo = $cuerpo;
+    return $this;
+  }
+
+  public function setReceptor($receptor = []) {
+    $this->receptor = $receptor;
+    return $this;
+  }
+
+  public function setAdjuntos($adjuntos = []) {
+    if($this->hasErrors()) {
+      return;
+    }
+    if(!is_array($adjuntos) || empty($adjuntos)) {
+      return;
+    }
+
+    $instancias = [];
+    foreach($adjuntos as $adjunto) {
+      $n = new NotificacionCorreoAdjunto();
+      $n->idNotificacionCorreo = $this->id;
+      $n->ruta = $adjunto;
+      if(!$n->save()) {
+        // Agregar el error a un log
+      }
+      $instancias[] = $n;
+    }
+
+    return $instancias;
+  }
+}

+ 56 - 0
modules/mail/models/NotificacionCorreoAdjunto.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace app\modules\mail\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "NotificacionCorreoAdjunto".
+ *
+ * @property int $id
+ * @property int|null $idNotificacionCorreo
+ * @property string|null $ruta
+ *
+ * @property NotificacionCorreo $notificacionCorreo
+ */
+class NotificacionCorreoAdjunto extends \yii\db\ActiveRecord {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function tableName() {
+    return 'NotificacionCorreoAdjunto';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rules() {
+    return [
+      [['idNotificacionCorreo'], 'default', 'value' => null],
+      [['idNotificacionCorreo'], 'integer'],
+      [['ruta'], 'string', 'max' => 255],
+      [['idNotificacionCorreo'], 'exist', 'skipOnError' => true, 'targetClass' => NotificacionCorreo::class, 'targetAttribute' => ['idNotificacionCorreo' => 'id']],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function attributeLabels() {
+    return [
+      'id' => 'ID',
+      'idNotificacionCorreo' => 'Id Notificacion Correo',
+      'ruta' => 'Ruta',
+    ];
+  }
+
+  /**
+   * Gets query for [[IdNotificacionCorreo0]].
+   *
+   * @return \yii\db\ActiveQuery
+   */
+  public function getNotificacionCorreo() {
+    return $this->hasOne(NotificacionCorreo::class, ['id' => 'idNotificacionCorreo']);
+  }
+}

+ 0 - 0
modules/mail/views/ejemplo/html.php


File diff suppressed because it is too large
+ 388 - 0
modules/mail/views/layouts/cuerpo.php


+ 22 - 0
modules/mail/views/layouts/html.php

@@ -0,0 +1,22 @@
+<?php
+use yii\helpers\Html;
+
+/* @var $this \yii\web\View view component instance */
+/* @var $message \yii\mail\MessageInterface the message being composed */
+/* @var $content string main view render result */
+?>
+<?php $this->beginPage() ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
+    <title><?= Html::encode($this->title) ?></title>
+    <?php $this->head() ?>
+</head>
+<body>
+    <?php $this->beginBody() ?>
+    <?= $content ?>
+    <?php $this->endBody() ?>
+</body>
+</html>
+<?php $this->endPage() ?>

+ 38 - 0
modules/pdf/Module.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace pdf;
+
+use Yii;
+
+/**
+ * v1 module definition class
+ */
+class Module extends \yii\base\Module {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $controllerNamespace = 'pdf\controllers';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    parent::init();
+    $response = Yii::$app->getResponse();
+    $headers = $response->getHeaders();
+    
+    $headers->set('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Headers', 'Content-Type,Accept,Authorization');
+    $headers->set('Access-Control-Allow-Origin', '*');
+    $headers->set('Access-Control-Request-Method', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Credentials', 'true');
+    $headers->set('Access-Control-Max-Age', 86400);
+    if (Yii::$app->getRequest()->isOptions) {
+      Yii::$app->end();
+    } // */
+    Yii::$app->getUser()->enableSession = false;
+    Yii::$app->getUser()->identityClass = 'v1\models\Usuario';
+  }
+
+}

+ 115 - 0
modules/pdf/controllers/AcuseSolicitudController.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace pdf\controllers;
+
+use app\models\OficialiaPartesManifiestoDocumento;
+use Exception;
+use pdf\web\Controller;
+use v1\controllers\EntregaInformacionController;
+use v1\models\FondoLegislativo;
+use v1\models\OficialiaPartesDocumento;
+use v1\models\OficialiaPartesDocumentoResponsable;
+use v1\models\OficialiaPartesManifiesto;
+use v1\models\RecursoRevision;
+use v1\models\RequisicionResponsable;
+use v1\models\Solicitud;
+use v1\models\Usuario;
+use yii\web\HttpException;
+use yii\web\NotFoundHttpException;
+
+class AcuseSolicitudController extends Controller {
+
+  public function actionIndex() {
+  }
+
+  public function actionFormato() {
+
+    $idSolicitud = trim($this->req->get("solicitud", ""));
+    $solicitud = null;
+
+    if ($idSolicitud === "") {
+      throw new HttpException(400, "Es necesario proporcionar una solicitud");
+    }
+
+    $solicitud = Solicitud::find()
+      ->andWhere(['id' => $idSolicitud])
+      ->andWhere(['eliminado' => null])
+      ->with('usuario', 'media', 'sujetoObligado')
+      ->one();
+
+    $recursoRevision = RecursoRevision::find()
+      ->innerJoin('Solicitud', '{{Solicitud}}.id = {{RecursoRevision}}.[[idSolicitud]]')
+      ->andWhere(['idSolicitud' => $idSolicitud])
+      ->andWhere(['{{Solicitud}}.eliminado' => null])
+      ->andWhere(['{{RecursoRevision}}.eliminado' => null])
+      ->one();
+
+    if ($solicitud === null) {
+      throw new NotFoundHttpException('No se encontró la solicitud');
+    }
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '36',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/acuse-solicitud/formato", [
+      "solicitud" => $solicitud,
+      "recurso" => $recursoRevision
+    ]);
+
+    $header = $view->render("@app/modules/pdf/views/header/formato", [
+      "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+    ]);
+
+
+    $pdf = self::crearPDF(
+      'AS-ISTAIAS-03',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+
+  public function actionOficio() {
+
+    $request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '50',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/acuse-solicitud/oficio", [
+      "recursoRevision" => [],
+    ]);
+
+    if ($request->get('header'))
+      $header = $view->render("@app/modules/pdf/views/header/oficio-landing", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+    else
+      $header = $view->render("@app/modules/pdf/views/header/oficio", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+
+    $pdf = self::crearPDF(
+      'AS-ISTAIAS-03',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+}

+ 136 - 0
modules/pdf/controllers/IncompetenciaSujetoController.php

@@ -0,0 +1,136 @@
+<?php
+
+namespace pdf\controllers;
+
+use app\models\OficialiaPartesManifiestoDocumento;
+use pdf\web\Controller;
+use v1\models\FondoLegislativo;
+use v1\models\OficialiaPartesDocumento;
+use v1\models\OficialiaPartesDocumentoResponsable;
+use v1\models\OficialiaPartesManifiesto;
+use v1\models\RequisicionResponsable;
+use v1\models\Solicitud;
+use v1\models\Usuario;
+
+class IncompetenciaSujetoController extends Controller {
+
+  public function actionIndex() {
+
+    $idSolicitud = trim($this->req->get('solicitud', ''));
+    $solicitud = null;
+
+    if ($idSolicitud === '') {
+      throw new \yii\web\NotFoundHttpException("No se encontró la solicitud");
+    }
+
+    $solicitud = Solicitud::find()
+      ->andWhere(['id' => $idSolicitud])
+      ->andWhere(['eliminado' => null])
+      ->one();
+
+    if ($solicitud === null) {
+      throw new \yii\web\NotFoundHttpException("No se encontró la solicitud");
+    }
+
+    $request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '50',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/incompetencia-sujeto/oficio", [
+      "solicitud" => $solicitud,
+      "recursoRevision" => [],
+    ]);
+
+    if ($request->get('header'))
+      $header = $view->render("@app/modules/pdf/views/header/oficio-landing", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+    else
+      $header = $view->render("@app/modules/pdf/views/header/oficio", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+
+    $pdf = self::crearPDF(
+      'AS-ISTAIAS-03',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+
+  public function actionFormato() {
+
+    //$request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '36',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/requerimiento-aclaracion/formato", [
+      "recursoRevision" => [],
+    ]);
+
+    $header = $view->render("@app/modules/pdf/views/header/formato", [
+      "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+    ]);
+
+    $pdf = self::crearPDF(
+      'RA-ISTAIAS-02',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+
+  public function actionOficio() {
+
+    $request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '50',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/requerimiento-aclaracion/oficio", [
+      "recursoRevision" => [],
+    ]);
+
+    if ($request->get('header'))
+      $header = $view->render("@app/modules/pdf/views/header/oficio-landing", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+    else
+      $header = $view->render("@app/modules/pdf/views/header/oficio", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+
+    $pdf = self::crearPDF(
+      'AS-ISTAIAS-03',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+}

+ 109 - 0
modules/pdf/controllers/RecursoRevisionController.php

@@ -0,0 +1,109 @@
+<?php
+
+namespace pdf\controllers;
+
+use pdf\web\Controller;
+use v1\models\RecursoManifestacion;
+use v1\models\RecursoRevision;
+
+class RecursoRevisionController extends Controller {
+
+  public function actionIndex() {
+    $id = $this->req->get("id", "");
+
+    $recurso = RecursoRevision::findOne($id);
+
+    if (!$recurso) {
+      throw new \yii\web\NotFoundHttpException("No se encontró el recurso de revisión");
+    }
+
+    return $this->renderPartial("formato", [
+      "recurso" => $recurso,
+    ]);
+  }
+
+  public function actionFormato() {
+
+    //$request = \Yii::$app->request;
+
+    $id = $this->req->get("id", "");
+
+    $recurso = RecursoRevision::findOne($id);
+
+    if (!$recurso) {
+      throw new \yii\web\NotFoundHttpException("No se encontró el recurso de revisión");
+    }
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '36',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/recurso-revision/formato", [
+      "recurso" => $recurso,
+    ]);
+
+    $header = $view->render("@app/modules/pdf/views/header/formato", [
+      "titulo" => "FORMATO DE RECURSO DE REVISIÓN",
+    ]);
+
+    $pdf = self::crearPDF(
+      'RR-ISTAIAS-04',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      "",
+      ''
+    );
+  }
+
+  public function actionManifiesto() {
+
+    //$request = \Yii::$app->request;
+
+    $id = trim($this->req->get("manifiesto", ""));
+
+    $manifiesto = RecursoManifestacion::findOne($id);
+
+    if (!$manifiesto) {
+      throw new \yii\web\NotFoundHttpException("No se encontró el manifiesto de recurso de revisión");
+    }
+
+    $recurso = RecursoRevision::findOne($manifiesto->idRecurso);
+
+    if (!$recurso) {
+      throw new \yii\web\NotFoundHttpException("No se encontró el recurso de revisión");
+    }
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '36',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/recurso-revision/manifiesto", [
+      "recurso" => $recurso,
+      "manifiesto" => $manifiesto,
+    ]);
+
+    $header = $view->render("@app/modules/pdf/views/header/formato", [
+      "titulo" => "FORMATO DE MANIFIESTO DE RECURSO DE REVISIÓN",
+    ]);
+
+    $pdf = self::crearPDF(
+      'RR-ISTAIAS-04',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      "",
+      ''
+    );
+  }
+}

+ 141 - 0
modules/pdf/controllers/RequerimientoAclaracionController.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace pdf\controllers;
+
+use app\models\OficialiaPartesManifiestoDocumento;
+use pdf\web\Controller;
+use v1\models\Aclaracion;
+use v1\models\FondoLegislativo;
+use v1\models\OficialiaPartesDocumento;
+use v1\models\OficialiaPartesDocumentoResponsable;
+use v1\models\OficialiaPartesManifiesto;
+use v1\models\RequisicionResponsable;
+use v1\models\Usuario;
+use yii\web\HttpException;
+use yii\web\NotFoundHttpException;
+
+class RequerimientoAclaracionController extends Controller {
+
+  public function actionIndex() {
+
+  }
+
+  public function actionFormato() {
+
+    //$request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '36',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/requerimiento-aclaracion/formato", [
+      "recursoRevision" => [],
+    ]);
+
+    $header = $view->render("@app/modules/pdf/views/header/formato", [
+      "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+    ]);
+
+    $pdf = self::crearPDF(
+      'RA-ISTAIAS-02',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+
+  public function actionRespuesta() {
+
+    $idAclaracion = trim($this->req->get('aclaracion', ''));
+    $aclaracion = null;
+
+    if ($idAclaracion === '') {
+      throw new HttpException(400, 'Es necesario proporcionar un ID de aclaración.');
+    }
+
+    $aclaracion = Aclaracion::find()
+      ->andWhere(["id" => $idAclaracion])
+      ->andWhere(['eliminado' => null])
+      ->with('solicitud', 'media')
+      ->one();
+
+    if ($aclaracion === null) {
+      throw new NotFoundHttpException('No se encontró la aclaración.');
+    }
+
+    $solicitante = Usuario::find()
+      ->andWhere(['id' => $aclaracion->idSolicitaAclaracion])
+      ->andWhere(['eliminado' => null])
+      ->one();
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '36',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/requerimiento-aclaracion/respuesta", [
+      "aclaracion" => $aclaracion,
+      "solicitante" => $solicitante
+    ]);
+
+    $header = $view->render("@app/modules/pdf/views/header/formato", [
+      "titulo" => "RESPUESTA DE REQUERIMIENTO DE ACLARACIÓN",
+    ]);
+
+    $pdf = self::crearPDF(
+      'RA-ISTAIAS-06',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+
+  public function actionOficio() {
+
+    $request = \Yii::$app->request;
+
+    $configuracion = [
+      'mode' => 'utf-8',
+      'format' => 'letter',
+      'margin_top' => '50',
+    ];
+
+    $view = new \yii\web\View();
+
+    $htmlPDF = $view->render("@app/modules/pdf/views/requerimiento-aclaracion/oficio", [
+      "recursoRevision" => [],
+    ]);
+
+    if ($request->get('header'))
+      $header = $view->render("@app/modules/pdf/views/header/oficio-landing", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+    else
+      $header = $view->render("@app/modules/pdf/views/header/oficio", [
+        "titulo" => "ACUSE DE SOLICITUD DE INFORMACIÓN PÚBLICA",
+      ]);
+
+    $pdf = self::crearPDF(
+      'AS-ISTAIAS-03',
+      $htmlPDF,
+      $configuracion,
+      false,
+      $header,
+      '',
+      ''
+    );
+  }
+}

+ 346 - 0
modules/pdf/views/acuse-solicitud/formato.php

@@ -0,0 +1,346 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\Solicitud $solicitud
+ * @var \v1\models\RecursoRevision $recurso
+ * @var \v1\models\Usuario $usuario
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$media = $solicitud->media;
+$solicitante = $solicitud->usuario;
+
+$modalidadEntrega = [
+  ["id" => 1, "label" => "Portal ISTAI"],
+  ["id" => 2, "label" => "CD-ROM (con costo)"],
+  ["id" => 3, "label" => "Consulta Directa"],
+  ["id" => 4, "label" => "Copias Certificadas (con costo)"],
+  ["id" => 5, "label" => "Copias Simples (con costo)"],
+  ["id" => 6, "label" => "Copia Digitalizada"],
+  ["id" => 7, "label" => "Otro"],
+];
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          SUJETO OBLIGADO
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          <?= $solicitud->sujetoObligado->nombre ?>
+          <!-- Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales -->
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4">
+        <strong>Fecha de Recepción:</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        <?php
+        $date = '';
+        if ($solicitud->recepcion) {
+          $tz = new DateTimeZone('America/Hermosillo');
+          $date = new DateTime($solicitud->recepcion);
+          $date->setTimezone($tz);
+          $date = $date->format('d-m-Y');
+        }
+        echo $date;
+        ?>
+      </td>
+      <td class="col-table-2">
+        <strong>Hora:</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        <?php
+        $hora = '';
+        if ($solicitud->recepcion) {
+          $tz = new DateTimeZone('America/Hermosillo');
+          $hora = new DateTime($solicitud->recepcion);
+          $hora->setTimezone($tz);
+          $hora = $hora->format('H:i:s');
+        }
+        echo $hora
+        ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DATOS DEL SOLICITANTE
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          <?= $solicitud->nombre ? $solicitud->nombre : 'No Proporcionado' ?>
+          <!-- Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales -->
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-3 td-border td-center">
+        <strong>
+          DOMICILIO
+        </strong>
+      </td>
+      <td class="col-table-9 td-center">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          PAíS:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+        No Proporcionado
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          DOMICILIO:
+        </strong>
+      </td>
+      <td colspan="3" class="col-table-9 td-subrayado ">
+        No Proporcionado
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          CORREO ELECTRÓNICO:
+        </strong>
+      </td>
+      <td class="col-table-3 td-subrayado ">
+        <?= $solicitud->correo ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse ">
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de la Solicitud:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red"><?= $solicitud->folio ?></strong>
+      </td>
+    </tr>
+    <?php if ($recurso !== null) : ?>
+      <tr>
+        <td class="col-table-7">
+          <strong style="color:red">Número de Folio de Recurso de Revisión:</strong>
+        </td>
+        <td class="col-table-5">
+          <strong style="color:red">
+            <?= $recurso->folio ?>
+          </strong>
+        </td>
+      </tr>
+    <?php endif ?>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12" style="border: 1px solid black">
+        <strong>Solicitud de información</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12">
+        <?= $solicitud->descripcion ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-8 td-border" colspan="2">
+        <strong>MODALIDAD DE ENTREGA</strong>
+      </td>
+      <td class="col-table-4"></td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <?php
+    $contador = 0;
+    foreach ($modalidadEntrega as $entrega) {
+      if ($contador % 3 == 0) {
+        echo '<tr>';
+      }
+
+      if ($entrega['id'] === $solicitud->idModalidadEntrega) {
+        echo '<td class="col-table-4"><span style="background-color: #D9B5E5"><input type="checkbox" value="'.$entrega['id'].'" checked="checked" />' . $entrega['label'] . '</span></td>';
+      } else {
+        echo '<td class="col-table-4"><span><input type="checkbox" value="'.$entrega['id'].'" />' . $entrega['label'] . '</td>';
+      }
+
+      if ($contador % 3 == 2) {
+        echo '</tr>';
+      }
+
+      $contador++;
+    }
+
+    if ($contador % 3 != 0) {
+      echo '</tr>';
+    }
+
+    if ($solicitud->idModalidadEntrega === 6) {
+    ?>
+      <tr>
+        <td class="col-table-4">
+          Especificar:
+        </td>
+        <td class="col-table-4">
+          <?= $solicitud->otroModalidadEntrega ?>
+        </td>
+      </tr>
+    <?php
+    }
+    ?>
+    <!-- <tr>
+      <td class="col-table-4">
+        A través del SAIMEX
+      </td>
+      <td class="col-table-4">
+        Copias Simples(con costo)
+      </td>
+      <td class="col-table-4">
+        Consulta Directa(sin costo)
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4">
+        CD-ROM(con costo)
+      </td>
+      <td class="col-table-4">
+        Copias Certificadas(con costo)
+      </td>
+      <td class="col-table-4">
+        Disquete 3.5(con costo)
+      </td>
+    </tr>
+    <tr>
+      <td>
+        OTRO TIPO DE MEDIO (Especificar):
+      </td>
+      <td colspan="2">
+      </td>
+    </tr> -->
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DOCUMENTOS ANEXOS
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border">
+    <tr>
+      <td class="col-table-12">
+        <ul>
+          <?php
+          foreach ($media as $archivo) :
+          ?>
+            <li>
+              <a href="<?= $archivo->ruta ?>" target="_blank">
+                <?= $archivo->nombre ?>
+              </a>
+            </li>
+          <?php
+          endforeach
+          ?>
+        </ul>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          PLAZO DE RESPUESTA
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br /></div>
+
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-8">
+        <strong>Incompetencia y Parcialmente Incompetencia:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        3 días hábiles
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha de Posible Requerimiento de Aclaración de la Información:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>En Caso de Negativa a la Información: Improcedente, Inexistente, Confidencial y Reservada:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>En Caso de Reproducción con Costo:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha Límite de Respuesta:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        15 días hábiles
+      </td>
+    </tr>
+  </table>
+</div>

+ 50 - 0
modules/pdf/views/acuse-solicitud/oficio.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-oficio">
+    <tr>
+      <td class="col-table-4 td-center">
+      </td>
+      <td class="col-table-8" style="text-align:right">
+        nse de Transparencia, Acceso a la Información Pública y Datos Personales,<br/>
+        México a 03 de Octubre de 2023<br/>
+        Nombre del solicitante: C. Solicitante<br/>
+        Folio de la solicitud: 00004/INFOSONORA/IP/2023<br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        En respuesta a la solicitud recibida, nos permitimos hacer de su conocimiento que con fundamento en el artículo
+        53, Fracciones: II, V y VI de la Ley de Transparencia y Acceso a la Información Pública del Estado de México y
+        Municipios, le contestamos que:
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Respuesta del servidor público habilitado
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-oficio center " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        ATENTAMENTE<br/>
+        Unidad de Transparencia - Sujeto Obligado<br/>
+        <strong>Unidad de Transparencia</strong><br/>
+        Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+      </td>
+    </tr>
+  </table>
+</div>

+ 26 - 0
modules/pdf/views/header/formato.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ * @var  $titulo
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table width="100%" style="margin-bottom: 10px">
+    <tr>
+      <td width="220px" style="text-align: left">
+        <img src="/img/logo_istai_lg.png" width="25%" height="15%">
+      </td>
+      <td width="480px" style="text-align: center">
+        <h3>
+          <?= $titulo ?>
+        </h3>
+      </td>
+      <td width="220px" style="text-align: right">
+      </td>
+    </tr>
+  </table>
+</div>

+ 23 - 0
modules/pdf/views/header/oficio-landing.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse center" style="margin-bottom: 10px">
+    <tr>
+      <td class="col-table-3">
+        <img src="/img/logo_istai_lg.png" width="30%" height="18%">
+      </td>
+      <td class="col-table-9" style="text-align: center">
+        <h3>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </h3>
+      </td>
+    </tr>
+  </table>
+</div>

+ 25 - 0
modules/pdf/views/header/oficio.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse center" style="margin-bottom: 10px">
+    <tr>
+      <td width="48px">
+        <img src="/img/logo_istai_lg.png" width="30%" height="18%">
+      </td>
+    </tr>
+    <tr>
+      <td width="1200px" style="text-align: center">
+        <h3>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </h3>
+      </td>
+    </tr>
+  </table>
+</div>

+ 263 - 0
modules/pdf/views/incompetencia-sujeto/formato.php

@@ -0,0 +1,263 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          SUJETO OBLIGADO
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4">
+        <strong>Fecha de Recepción(dd-mm-aaaa):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10/01/2023
+      </td>
+      <td class="col-table-2">
+        <strong>Hora(hh:mm):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10:25:00
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DATOS DEL SOLICITANTE
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-3 td-border td-center">
+        <strong>
+          DOMICILIO
+        </strong>
+      </td>
+      <td class="col-table-9 td-center">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          PAÍS:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          DOMICILIO:
+        </strong>
+      </td>
+      <td colspan="3" class="col-table-9 td-center td-subrayado ">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          CORREO ELECTRÓNICO:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3">
+        <strong>
+          TELÉFONO (Opcional):
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse ">
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de la Solicitud:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">00004/INFOSONORA/IP/2023</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de Recurso de Revisión:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">
+          00001/INFOEM/IP/RR/2023
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-10 td-border td-center">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border">
+    <tr>
+      <td class="col-table-12">
+        Solicitud de información del instituto de transparencia
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-8 td-border" colspan="2">
+        <strong>MODALIDAD DE ENTREGA</strong>
+      </td>
+      <td class="col-table-4"></td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-4">
+        A través del SAIMEX
+      </td>
+      <td class="col-table-4">
+        Copias Simples(con costo)
+      </td>
+      <td class="col-table-4">
+        Consulta Directa(sin costo)
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4">
+        CD-ROM(con costo)
+      </td>
+      <td class="col-table-4">
+        Copias Certificadas(con costo)
+      </td>
+      <td class="col-table-4">
+        Disquete 3.5(con costo)
+      </td>
+    </tr>
+    <tr>
+      <td>
+        OTRO TIPO DE MEDIO (Especificar):
+      </td>
+      <td colspan="2">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DOCUMENTOS ANEXOS
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          PLAZO DE RESPUESTA
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha límite de respuesta:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        15 días hábiles 31/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha de posible requerimiento de aclaración de la información :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles 17/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Notificación de ampliación de plazo(prórroga) :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        14 a 15 días hábiles 30/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Respuesta a la solicitud en caso de ampliación de plazo :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        22 días hábiles 10/02/2023
+      </td>
+    </tr>
+  </table>
+</div>

+ 40 - 0
modules/pdf/views/incompetencia-sujeto/oficio.php

@@ -0,0 +1,40 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\Solicitud $solicitud
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-oficio">
+    <tr>
+      <td class="col-table-4 td-center">
+      </td>
+      <td class="col-table-8" style="text-align:right">
+        nse de Transparencia, Acceso a la Información Pública y Datos Personales,<br/>
+        México a 03 de Octubre de 2023<br/>
+        Nombre del solicitante: C. Solicitante<br/>
+        Folio de la solicitud: <?= $solicitud->folio ?><br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        fue una incompetencia total
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-oficio center " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        ATTE<br/>
+        Unidad de Transparencia - Sujeto Obligado<br/>
+      </td>
+    </tr>
+  </table>
+</div>

+ 266 - 0
modules/pdf/views/recurso-revision/formato.php

@@ -0,0 +1,266 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\RecursoRevision $recurso
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+date_default_timezone_set('America/Hermosillo');
+$fecha = date("d/m/Y");
+$hora = date("h:i A");
+$nombreCompleto = explode(" ", $recurso->solicitud->usuario->nombre);
+?>
+<div class="container">
+  <table class="table-acuse border">
+    <tr>
+      <td class="col-table-12 border" colspan="4" style="text-align:center;background:#863695; color:#ffffff; padding:9px 0">
+        <strong>RECEPCIÓN</strong>
+      </td>
+    </tr>
+    <tr>
+    <tr>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Fecha:</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $fecha ?>
+      </td>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Hora:</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $hora ?>
+      </td>
+    </tr>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td colspan="2" class="col-table-12 td-border" style="text-align: center; background:#863695; color:#ffffff; padding:9px 0">
+        <strong>DATOS DEL SOLICITANTE</strong>
+      </td>
+    </tr>
+    <tr>
+      <!-- <td class="col-table-4 td-border" style=" background:#863695; color:#ffffff; padding:9px 0">
+        <strong>PERSONA FÍSICA</strong>
+      </td> -->
+      <td class="col-table-8 td-no-borde">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border acuse td-padding">
+    <tr>
+      <td class="col-table-12" style="text-align: start;padding-top:40px; padding-left:10px; text-align: center">
+        <strong style="font-size: 28px;">
+          <?= $recurso->solicitud->usuario->nombre ?>
+        </strong><br>
+        <strong>NOMBRE</strong>
+      </td>
+    </tr>
+  </table>
+  <!-- <table class="table-acuse">
+    <tr>
+      <td class="col-table-4 td-border" style="background:#863695; color:#ffffff; padding:9px 0">
+        <strong>PERSONA MORAL</strong>
+      </td>
+      <td class="col-table-8" style="text-align: center; border:none">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border">
+    <tr>
+      <td class="col-table-4" style="text-align: center;">
+        <strong>RAZÓN O DENOMINACIÓN SOCIAL:</strong>
+      </td>
+      <td colspan="col-table-8" style="text-align: center;">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border">
+    <tr>
+      <td class="col-table-3">
+        <strong>NOMBRE DEL REPRESENTANTE:</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>APELLIDO PATERNO</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>APELLIDO MATERNO</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>NOMBRE(S)</strong>
+      </td>
+    </tr>
+  </table> -->
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-12" style="background:#863695; color:#ffffff;">
+        <strong>
+          DATOS DEL ACTO DE IMPUGNACIÓN
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>SUJETO OBLIGADO QUE LO EMITIÓ</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= $recurso->solicitud->sujetoObligado->nombre ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>ACTO IMPUGNADO</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= $recurso->actoImpugnado ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>LUGAR Y FECHA DE LA EMISIÓN DEL ACTO</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= date('d/m/Y', strtotime($recurso->creado)) ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#863695; color:#ffffff;">
+        <strong>FECHA EN QUE SE TUVO CONOCIMIENTO DEL ACTO IMPUGNADO (dd /mm /aaaa) </strong>
+      </td>
+      <td class="col-table-4">
+        <?= date('d/m/Y', strtotime($recurso->creado)) ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#863695; color:#ffffff;">
+        <strong>NÚMERO DE FOLIO O EXPEDIENTE DE LA SOLICITUD </strong>
+      </td>
+      <td class="col-table-4">
+        <?= $recurso->folio ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>RAZONES O MOTIVOS DE LA INCONFORMIDAD</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td colspan="12" style="text-align: justify;">
+        <?= $recurso->razonesMotivos ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-6 td-border" style=" background:#863695; color:#ffffff;">
+        <strong>DOCUMENTOS ANEXOS</strong>
+      </td>
+      <td class="col-table-6">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding ">
+    <tr>
+      <td class="col-table-5">
+        Niguno
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Niguno" ? "X" : "" ?>
+      </td>
+      <td class="col-table-5">
+        Poder
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Poder" ? "X" : "" ?>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-5">
+        Copia de constancia de Notificación
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Copia de la Constancia de Notificación" ? "X" : "" ?>
+      </td>
+      <td class="col-table-5">
+        Copia de Resolución
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Copia de Resolución" ? "X" : "" ?>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8" colspan="3">
+        Otros <?= isset($recurso->documentoAnexoOtro) && $recurso->documentoAnexoOtro !== null ? $recurso->documentoAnexoOtro : '' ?>
+      </td>
+      <td class="col-table-1 border" colspan="1" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Otros" ? "X" : "" ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>Folio de la solicitud de información</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <strong><?= $recurso->solicitud->folio ?></strong>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>Folio del recurso de revisión</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <strong><?= $recurso->folio ?></strong>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+</div>

+ 294 - 0
modules/pdf/views/recurso-revision/manifiesto.php

@@ -0,0 +1,294 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\RecursoRevision $recurso
+ * @var \v1\models\RecursoManifestacion $manifiesto
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+date_default_timezone_set('America/Hermosillo');
+$fecha = date("d/m/Y");
+$hora = date("h:i A");
+$nombreCompleto = explode(" ", $recurso->solicitud->usuario->nombre);
+?>
+<div class="container">
+  <table class="table-acuse border">
+    <tr>
+      <td class="col-table-12 border" colspan="4" style="text-align:center;background:#863695; color:#ffffff; padding:9px 0">
+        <strong>RECEPCIÓN</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Fecha:</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $fecha ?>
+      </td>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Hora:</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $hora ?>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse border">
+    <tr>
+      <td class="col-table-12 border" colspan="4" style="text-align:center;background:#863695; color:#ffffff; padding:9px 0">
+        <strong>MANIFIESTO</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Estado Anterior:</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+        <?= $manifiesto->estatusActualRecurso->nombre ?>
+      </td>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Nuevo Estado:</strong>
+      </td>
+      <td class="col-table-2" style=" padding:9px 0">
+      <?= $manifiesto->estatusNuevoRecurso->nombre ?>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4" style=" padding:9px 0">
+        <strong>Mensaje:</strong>
+      </td>
+      <td class="col-table-2" cols="3" style=" padding:9px 0">
+        <?= $manifiesto->mensaje ?>
+      </td>
+    </tr>
+  </table>
+  <!-- <div class="clearfix-table"></div> -->
+  <table class="table-acuse">
+    <tr>
+      <td colspan="2" class="col-table-12 td-border" style="text-align: center; background:#863695; color:#ffffff; padding:9px 0">
+        <strong>DATOS DEL SOLICITANTE</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4 td-border" style=" background:#863695; color:#ffffff; padding:9px 0">
+        <strong>PERSONA FÍSICA</strong>
+      </td>
+      <td class="col-table-8 td-no-borde">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border acuse td-padding">
+    <tr>
+      <td class="col-table-12" style="text-align: start;padding-top:40px; padding-left:10px;">
+        <strong style="font-size: 28px;">
+          <?= $recurso->solicitud->usuario->nombre ?>
+        </strong><br>
+        <strong>NOMBRE</strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4 td-border" style="background:#863695; color:#ffffff; padding:9px 0">
+        <strong>PERSONA MORAL</strong>
+      </td>
+      <td class="col-table-8" style="text-align: center; border:none">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border">
+    <tr>
+      <td class="col-table-4" style="text-align: center;">
+        <strong>RAZÓN O DENOMINACIÓN SOCIAL:</strong>
+      </td>
+      <td colspan="col-table-8" style="text-align: center;">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border">
+    <tr>
+      <td class="col-table-3">
+        <strong>NOMBRE DEL REPRESENTANTE:</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>APELLIDO PATERNO</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>APELLIDO MATERNO</strong>
+      </td>
+      <td class="col-table-3 td-center" style="padding-top:40px">
+        <strong>____________________________</strong><br>
+        <strong>NOMBRE(S)</strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-12" style="background:#863695; color:#ffffff;">
+        <strong>
+          DATOS DEL ACTO DE IMPUGNACIÓN
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>SUJETO OBLIGADO QUE LO EMITIÓ</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= $recurso->solicitud->sujetoObligado->nombre ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>ACTO IMPUGNADO</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= $recurso->actoImpugnado ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>LUGAR Y FECHA DE LA EMISIÓN DEL ACTO</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-12" colspan="2">
+        <?= date('d/m/Y', strtotime($recurso->creado)) ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#863695; color:#ffffff;">
+        <strong>FECHA EN QUE SE TUVO CONOCIMIENTO DEL ACTO IMPUGNADO (dd /mm /aaaa) </strong>
+      </td>
+      <td class="col-table-4">
+        <?= date('d/m/Y', strtotime($recurso->creado)) ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#863695; color:#ffffff;">
+        <strong>NÚMERO DE FOLIO O EXPEDIENTE DE LA SOLICITUD </strong>
+      </td>
+      <td class="col-table-4">
+        <?= $recurso->folio ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#863695; color:#ffffff;">
+        <strong>RAZONES O MOTIVOS DE LA INCONFORMIDAD</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+      <td colspan="12" style="text-align: justify;">
+        <?= $recurso->razonesMotivos ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-6 td-border" style=" background:#863695; color:#ffffff;">
+        <strong>DOCUMENTOS ANEXOS</strong>
+      </td>
+      <td class="col-table-6">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding ">
+    <tr>
+      <td class="col-table-5">
+        Niguno
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Niguno" ? "X" : "" ?>
+      </td>
+      <td class="col-table-5">
+        Poder
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Poder" ? "X" : "" ?>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-5">
+        Copia de constancia de Notificación
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Copia de la Constancia de Notificación" ? "X" : "" ?>
+      </td>
+      <td class="col-table-5">
+        Copia de Resolución
+      </td>
+      <td class="col-table-1 border" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Copia de Resolución" ? "X" : "" ?>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8" colspan="3">
+        Otros <?= isset($recurso->documentoAnexoOtro) && $recurso->documentoAnexoOtro !== null ? $recurso->documentoAnexoOtro : '' ?>
+      </td>
+      <td class="col-table-1 border" colspan="1" style="text-align:center;">
+        <?= $recurso->documentoAnexo === "Otros" ? "X" : "" ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>Folio de la solicitud de información</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <strong><?= $recurso->solicitud->folio ?></strong>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>Folio del recurso de revisión</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <strong><?= $recurso->folio ?></strong>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+</div>

+ 263 - 0
modules/pdf/views/requerimiento-aclaracion/formato.php

@@ -0,0 +1,263 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          SUJETO OBLIGADO
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-4">
+        <strong>Fecha de Recepción(dd-mm-aaaa):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10/01/2023
+      </td>
+      <td class="col-table-2">
+        <strong>Hora(hh:mm):</strong>
+      </td>
+      <td class="col-table-3 td-subrayado td-center">
+        10:25:00
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DATOS DEL SOLICITANTE
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <strong>
+          Instituto Sonorense de Transparencia, Acceso a la Información Pública y Datos Personales
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-3 td-border td-center">
+        <strong>
+          DOMICILIO
+        </strong>
+      </td>
+      <td class="col-table-9 td-center">
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          PAÍS:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+      <td class="col-table-3 td-center">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          DOMICILIO:
+        </strong>
+      </td>
+      <td colspan="3" class="col-table-9 td-center td-subrayado ">
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-3">
+        <strong>
+          CORREO ELECTRÓNICO:
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado ">
+      </td>
+      <td class="col-table-3">
+        <strong>
+          TELÉFONO (Opcional):
+        </strong>
+      </td>
+      <td class="col-table-3 td-center td-subrayado">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse ">
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de la Solicitud:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">00004/INFOSONORA/IP/2023</strong>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-7">
+        <strong style="color:red">Número de Folio de Recurso de Revisión:</strong>
+      </td>
+      <td class="col-table-5">
+        <strong style="color:red">
+          00001/INFOEM/IP/RR/2023
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-center" style="margin-top:12px">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-10 td-border td-center">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border">
+    <tr>
+      <td class="col-table-12">
+        Solicitud de información del instituto de transparencia
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-8 td-border" colspan="2">
+        <strong>MODALIDAD DE ENTREGA</strong>
+      </td>
+      <td class="col-table-4"></td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-4">
+        A través del SAIMEX
+      </td>
+      <td class="col-table-4">
+        Copias Simples(con costo)
+      </td>
+      <td class="col-table-4">
+        Consulta Directa(sin costo)
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4">
+        CD-ROM(con costo)
+      </td>
+      <td class="col-table-4">
+        Copias Certificadas(con costo)
+      </td>
+      <td class="col-table-4">
+        Disquete 3.5(con costo)
+      </td>
+    </tr>
+    <tr>
+      <td>
+        OTRO TIPO DE MEDIO (Especificar):
+      </td>
+      <td colspan="2">
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          DOCUMENTOS ANEXOS
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse td-border td-center">
+    <tr>
+      <td class="col-table-12">
+        <br/>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="col-table-12 td-border td-center">
+        <strong>
+          PLAZO DE RESPUESTA
+        </strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-acuse td-border td-padding">
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha límite de respuesta:</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        15 días hábiles 31/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Fecha de posible requerimiento de aclaración de la información :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        5 días hábiles 17/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Notificación de ampliación de plazo(prórroga) :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        14 a 15 días hábiles 30/01/2023
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-8">
+        <strong>Respuesta a la solicitud en caso de ampliación de plazo :</strong>
+      </td>
+      <td class="col-table-4 td-subrayado">
+        22 días hábiles 10/02/2023
+      </td>
+    </tr>
+  </table>
+</div>

+ 63 - 0
modules/pdf/views/requerimiento-aclaracion/oficio.php

@@ -0,0 +1,63 @@
+<?php
+/**
+ * @var \yii\web\View $this
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$camposTabla = 30;
+?>
+<div class="container">
+  <table class="table-oficio">
+    <tr>
+      <td class="col-table-4 td-center">
+      </td>
+      <td class="col-table-8" style="text-align:right">
+        nse de Transparencia, Acceso a la Información Pública y Datos Personales,<br/>
+        México a 03 de Octubre de 2023<br/>
+        Nombre del solicitante: C. Solicitante<br/>
+        Folio de la solicitud: 00005/1NFOSONORA/1P/2023<br/>
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Con fundamento en el artículo 159 de la Ley de Transparencia y Acceso a la Información Pública del Estado de
+        México y Municipios, se le requiere para que dentro del plazo de diez días hábiles realice lo siguiente:
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Solicito me aclare la información
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        En caso de que no se desahogue el requerimiento señalado dentro del plazo citado se tendrá por no presentada la
+        solicitud de información, quedando a salvo sus derechos para volver a presentar la solicitud, lo anterior con
+        fundamento en el artículo 159 de la Ley invocada.
+      </td>
+    </tr>
+  </table>
+  <table class="table-oficio " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        Respuesta del servidor público habilitado
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix"><br/></div>
+  <table class="table-oficio center " style="margin-top:75px">
+    <tr>
+      <td class="col-table-12">
+        ATENTAMENTE<br/>
+        Unidad de Transparencia - Sujeto Obligado<br/>
+      </td>
+    </tr>
+  </table>
+</div>

+ 156 - 0
modules/pdf/views/requerimiento-aclaracion/respuesta.php

@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @var \yii\web\View $this
+ * @var \v1\models\Aclaracion $aclaracion
+ * @var \v1\models\Usuario $solicitante
+ */
+setlocale(LC_ALL, 'es_ES');
+$basePath = \Yii::getAlias('@app') . "/web/";
+
+$primerApellido = '';
+$segundoApellido = '';
+$nombre = '';
+
+$nombreCompleto = $solicitante->nombre;
+
+$nombreCompleto = explode(' ', $nombreCompleto);
+
+if (count($nombreCompleto) === 2) {
+  $primerApellido = $nombreCompleto[1];
+  $nombre = $nombreCompleto[0];
+} else if (count($nombreCompleto) === 3) {
+  $primerApellido = $nombreCompleto[2];
+  $segundoApellido = $nombreCompleto[1];
+  $nombre = $nombreCompleto[0];
+} else if (count($nombreCompleto) === 4) {
+  $primerApellido = $nombreCompleto[3];
+  $segundoApellido = $nombreCompleto[2];
+  $nombre = $nombreCompleto[0] . ' ' . $nombreCompleto[1];
+}
+
+?>
+<div class="container">
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td colspan="2" class="col-table-12 td-border" style="text-align: center; background:#cccccc; padding:9px 0">
+        <strong>DATOS DEL SOLICITANTE</strong>
+      </td>
+    </tr>
+  </table>
+  <table class="table-acuse  td-border acuse td-padding">
+    <tr>
+      <td class="col-table-4" style="text-align: center;padding-top: 40px;border-bottom: 1px solid black">
+        <?= $primerApellido ?><br>
+      </td>
+      <td class="col-table-4" style="text-align: center;padding-top: 40px;border-bottom: 1px solid black">
+        <?= $segundoApellido ?><br>
+      </td>
+      <td class="col-table-4" style="text-align: center;padding-top: 40px;border-bottom: 1px solid black">
+        <?= $nombre ?><br>
+      </td>
+    </tr>
+    <tr>
+      <td class="col-table-4" style="text-align: center">
+        <strong>APELLIDO PATERNO</strong>
+      </td>
+      <td class="col-table-4" style="text-align: center">
+        <strong>APELLIDO MATERNO</strong>
+      </td>
+      <td class="col-table-4" style="text-align: center">
+        <strong>NOMBRE(S)</strong>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#cccccc;">
+        <strong>FECHA EN QUE SE TUVO CONOCIMIENTO DEL ACTO IMPUGNADO (dd /mm /aaaa) </strong>
+      </td>
+      <td class="col-table-4">
+        <?php
+        $date = new DateTime($aclaracion->creado);
+        echo $date->format('d-m-Y')
+        ?>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-8" style=" background:#cccccc;">
+        <strong>NÚMERO DE FOLIO O EXPEDIENTE DE LA SOLICITUD </strong>
+      </td>
+      <td class="col-table-4">
+        <?= $aclaracion->solicitud->folio ?>
+        <!-- 00004/INFOSONORA/IP/2023 -->
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-solicitud">
+    <tr>
+      <td class="col-table-7" style="background:#cccccc;">
+        <strong>DATOS A COMPLETAR, CORREGIR, AMPLIAR O ACLARAR</strong>
+      </td>
+      <td class="col-table-5">
+      </td>
+    </tr>
+    <tr>
+    <tr>
+      <td colspan="12" style="text-align: justify;">
+        <?= $aclaracion->solicitudAclaracion ?>
+        <!-- no fue claro la orientación a un trámite -->
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse">
+    <tr>
+      <td class="td-border" style=" background:#cccccc;">
+        <strong>DOCUMENTOS ANEXOS</strong>
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <ul>
+          <?php foreach ($aclaracion->media as $media) : ?>
+            <li>
+              <a href="<?= $media->ruta ?>" target="_blank">
+                <?= $media->nombre ?>
+              </a>
+            </li>
+          <?php endforeach ?>
+        </ul>
+      </td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>NÚMERO DE ACLARACIÓN</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        <?= $aclaracion->solicitud->folio ?>
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+  <div class="clearfix-table"></div>
+  <table class="table-acuse td-padding ">
+    <tr>
+      <td class="col-table-2"></td>
+      <td class="col-table-4 td-border">
+        <strong>CLAVE DE ENTREGA DE LA ACLARACIÓN</strong>
+      </td>
+      <td class="col-table-4 td-border">
+        000042023001102500004205
+      </td>
+      <td class="col-table-2"></td>
+    </tr>
+  </table>
+</div>

+ 540 - 0
modules/pdf/web/Controller.php

@@ -0,0 +1,540 @@
+<?php
+
+namespace pdf\web;
+
+use Mpdf\Mpdf;
+use yii\filters\auth\CompositeAuth;
+use yii\filters\auth\QueryParamAuth;
+use yii\filters\Cors;
+use PhpOffice\PhpSpreadsheet\IOFactory;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Controller extends \yii\web\Controller {
+
+  /**
+   * Si es verdadero imprime el contenido en el web
+   * @var boolean $html
+   */
+  public $html = false;
+
+  /**
+   * Mostrar vista previa del pdf o descargar
+   * true = descargar
+   * @var boolean $descargar
+   */
+  public $descargar = false;
+
+  /**
+   * Configuración para la librería mpdf
+   * @var array $configuracion
+   */
+  public $configuracion = [
+    "format" => "letter",
+    "default_font" => "Roboto",
+  ];
+
+  /**
+   * Texto para la marca de agua
+   * @var string $marcaDeAguaTexto
+   */
+  public $marcaDeAguaTexto = "";
+
+  /**
+   * Habilitar la marca de agua
+   * @var boolean $html
+   */
+  public $marcaDeAgua = false;
+
+  /**
+   * Encoger las tablas para que quepan
+   * @var int $encogerTablas
+   */
+  public $encogerTablas = 0;
+
+  /**
+   * Mantener proporciones de tabla
+   * @var boolean $mantenerProporcionTabla
+   */
+  public $mantenerProporcionTabla = true;
+
+  /**
+   * Nombre del archivo al descargar
+   * @var string $nombreArchivo
+   */
+  public $nombreArchivo = "";
+
+  /**
+   * Estilos para el pdf
+   * @var string $hojaDeEstilo
+   */
+  public $hojaDeEstilo = "";
+
+  /**
+   * header para el pdf
+   * @var string $header
+   */
+  public $header;
+
+  /**
+   * @var \yii\web\Request $req
+   */
+  public $req;
+
+  /**
+   * @var \yii\web\Response $res
+   */
+  public $res;
+
+  //*
+  /* public function behaviors() {
+    $behavior = parent::behaviors();
+    $behavior["authenticator"] = [
+      "class" => CompositeAuth::className(),
+      "authMethods" => [
+        QueryParamAuth::className(),
+      ]
+    ];
+    return $behavior;
+  }  */// */
+
+  public function beforeAction($action) {
+    parent::beforeAction($action);
+
+    $basePath = \Yii::getAlias("@app");
+    $this->req = \Yii::$app->getRequest();
+    $this->res = \Yii::$app->getResponse();
+    $this->html = intval($this->req->get("html", 0)) === 1;
+
+    if ($this->html) {
+      $this->res->format = \yii\web\Response::FORMAT_HTML;
+    }
+
+    $this->descargar = intval($this->req->get("descargar", "")) === 1;
+    $this->marcaDeAgua = false; // intval($this->req->get("wm", 0)) === 1;
+    $this->hojaDeEstilo = file_get_contents("{$basePath}/web/css/pdf.css");
+
+    return true;
+  }
+
+  public static function crearPDF($nombreArchivo, $contenido, $configuracion, $descargar = true, $header = "", $footer = "", $marcaAgua = "") {
+    $basePath = \Yii::getAlias("@app");
+
+    $mpdf = new Mpdf($configuracion);
+    //$mpdf->WriteHTML($hojaDeEstilo, \Mpdf\HTMLParserMode::HEADER_CSS);
+    $mpdf->showWatermarkText = false;
+    $mpdf->watermarkTextAlpha = 0.30;
+    $mpdf->shrink_tables_to_fit = 0;
+    $mpdf->keep_table_proportions = true;
+    $mpdf->SetTitle($nombreArchivo);
+    $mpdf->SetDisplayMode('default');
+
+    $mpdf->SetHTMLHeader($header); //SetHTMLFooter($footer);
+
+    if ($footer !== "") {
+      $mpdf->SetHTMLFooter($footer); //SetHTMLFooter($footer);
+    } else {
+      $mpdf->SetFooter('Pag. {PAGENO} de {nbpg}');
+    }
+    if ($marcaAgua !== "") {
+      $mpdf->SetWatermarkText($marcaAgua);
+      $mpdf->showWatermarkText = true;
+    }
+    $mpdf->keep_table_proportions = TRUE;
+    $stylesheet = file_get_contents("{$basePath}/web/css/pdf/pdf.css");
+    $mpdf->WriteHTML($stylesheet, \Mpdf\HTMLParserMode::HEADER_CSS);
+
+    $mpdf->showImageErrors = false;
+    $mpdf->useSubstitutions = false;
+    $mpdf->simpleTables = false;
+    $mpdf->WriteHTML($contenido, \Mpdf\HTMLParserMode::HTML_BODY, ini_set("pcre.backtrack_limit", (strlen($contenido) * 5000)));
+    if ($descargar == true) {
+      return $mpdf->Output($nombreArchivo . ".pdf", \Mpdf\Output\Destination::STRING_RETURN);
+    } else {
+      $mpdf->Output($nombreArchivo . ".pdf", "I");
+      \Yii::$app->end();
+    }
+  }
+
+  public function exportarPdf($contenido) {
+    try {
+      // $config = array_merge($this->configuracion, ['format' => 'A4']);
+      $mpdf = new Mpdf($this->configuracion);
+      if (!empty($this->header)) {
+        $mpdf->SetHTMLHeader($this->header);
+      }
+      $mpdf->WriteHTML($this->hojaDeEstilo, \Mpdf\HTMLParserMode::HEADER_CSS);
+      $mpdf->SetWatermarkText($this->marcaDeAguaTexto);
+      $mpdf->watermark_font = 'DejaVuSansCondensed';
+      $mpdf->showWatermarkText = $this->marcaDeAgua;
+      $mpdf->watermarkTextAlpha = 0.30;
+      $mpdf->shrink_tables_to_fit = $this->encogerTablas;
+      $mpdf->keep_table_proportions = $this->mantenerProporcionTabla;
+      $mpdf->SetTitle($this->nombreArchivo);
+      $mpdf->SetDisplayMode('default');
+      $mpdf->SetFooter('Pag. {PAGENO} de {nbpg}');
+      $mpdf->showImageErrors = false;
+      $mpdf->useSubstitutions = false;
+      $mpdf->simpleTables = false;
+      $mpdf->WriteHTML($contenido, \Mpdf\HTMLParserMode::HTML_BODY);
+      $dest = $this->descargar ? "D" : "I";
+      if (strpos($this->nombreArchivo, '.pdf') === false) {
+        $this->nombreArchivo .= ".pdf";
+      }
+      header('Access-Control-Allow-Origin: *');
+      header('Access-Control-Expose-Headers: *');
+      $mpdf->Output($this->nombreArchivo, $dest);
+    } catch (\Exception $exception) {
+      throw $exception;
+    }
+    \Yii::$app->end();
+  }
+
+  public function afterAction($action, $result) {
+    if (!$this->html) {
+      $result = str_replace('disabled="disabled"', '', $result);
+      return $this->exportarPdf($result);
+    }
+    $this->marcaDeAgua = intval($this->req->get("wm", 1)) === 1;
+    $watermark = "background-image: url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' " .
+      "height='100px' width='100px'><text transform='translate(20, 100) rotate(-45)' fill='rgb(210,210,210)' " .
+      "font-size='18'>{$this->marcaDeAguaTexto}</text></svg>\");";
+    if (!$this->marcaDeAgua) {
+      $watermark = "";
+    }
+    $fondo = ".fondo-privado { background-color: rgb(141,216,169,0.7) !important; }";
+    $result = str_replace("<pagebreak>", "<br>", $result);
+    $result = "<style type=\"text/css\">{$this->hojaDeEstilo}\nbody{{$watermark}}\n{$fondo}</style>{$result}";
+    return $result;
+  }
+
+  /**
+   * funcion para generar cualquier Excel
+   */
+  public static function Excel($titulo = "Reporte", $pestania = "Reporte", $nombre = "Reporte", $etiquetas = [], $campos = [], $datos, $usarApuntador = true, $isArray = false, $respaldo = false) {
+    // Create new Spreadsheet object
+    $basePath = \Yii::getAlias("@app");
+
+    $spreadsheet = new Spreadsheet();
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getSecurity()->setLockWindows(false);
+    $spreadsheet->getSecurity()->setLockStructure(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSheet(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSort(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setInsertRows(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setFormatCells(false);
+
+    // Set document properties
+    $spreadsheet->getProperties()->setCreator('pbr')->setLastModifiedBy('pbr')->setTitle($titulo)
+      ->setDescription($titulo);
+
+    $spreadsheet->getActiveSheet()->setTitle($pestania);
+    $style_titulo = [
+      'font' => [
+        'bold' => true,
+        'size' => 13,
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+      ]
+    ];
+
+    $style_titulo_etiquestas = [
+      'font' => [
+        'bold' => true,
+        'size' => 11,
+        'color' => ['rgb' => '000000'],
+        'background' => ['rgb' => '4c5966'],
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER_CONTINUOUS,
+      ],
+      'fill' => [
+        'type' => \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_GRADIENT_LINEAR,
+        'rotation' => 90,
+        'startcolor' => ['argb' => '000000',],
+        'endcolor' => ['argb' => '000000',],
+      ],
+    ];
+
+    $spreadsheet->setActiveSheetIndex(0);
+    $i = $respaldo ? 1 : 7;
+    $ltrs = [];
+    $ltr2 = 65;
+    $ltr = 65;
+    foreach ($etiquetas as $v) {
+      if ($ltr > 90) {
+        $letra = "A" . chr($ltr2);
+      } else {
+        $letra = chr($ltr);
+      }
+      $ltrs[] = $letra;
+
+      $spreadsheet->getActiveSheet()->setCellValue($letra . $i, $v);
+      if ($ltr > 90) {
+        $ltr2++;
+      }
+      $ltr++;
+    }
+
+    // Add some data
+    $i++; //Es el renglón inicial
+    if ($usarApuntador) {
+      /* @var $datos ActiveQuery */
+      if (!$isArray) {
+        foreach ($datos->each() as $v) {
+          $l = 0;
+          foreach ($campos as $k => $a) {
+            $spreadsheet
+              ->getActiveSheet()
+              ->setCellValue($ltrs[$l] . $i, isset($v[$a]) ? $v[$a] : "")
+              ->getColumnDimension($ltrs[$l])
+              ->setAutoSize(true);
+            $l++;
+          }
+          $i++;
+        }
+      } else {
+        foreach ($datos as $v) {
+          $l = 0;
+          foreach ($campos as $k => $a) {
+            $spreadsheet
+              ->getActiveSheet()
+              ->setCellValue($ltrs[$l] . $i, isset($v[$a]) ? $v[$a] : "")
+              ->getColumnDimension($ltrs[$l])
+              ->setAutoSize(true);
+            $l++;
+          }
+          $i++;
+        }
+      }
+
+    } else {
+      foreach ($datos as $v) {
+        $l = 0;
+        foreach ($campos as $k => $a) {
+          $spreadsheet
+            ->getActiveSheet()
+            ->setCellValue($ltrs[$l] . $i, isset($v[$a]) ? $v[$a] : "")
+            ->getColumnDimension($ltrs[$l])
+            ->setAutoSize(true);
+          $l++;
+        }
+        $i++;
+      }
+
+    }
+    // Rename worksheet
+
+    // Set active sheet index to the first sheet, so Excel opens this as the first sheet
+    $spreadsheet->setActiveSheetIndex(0);
+
+    $ultima_letra = array_pop($ltrs);
+
+    if (!$respaldo) {
+      $spreadsheet->getActiveSheet()->mergeCells('A3:' . $ultima_letra . "3");
+      $spreadsheet->getActiveSheet()->getStyle("A3:" . $ultima_letra . "3")->applyFromArray($style_titulo);
+      $spreadsheet->getActiveSheet()->setCellValue('A3', $titulo);
+      //$spreadsheet->getActiveSheet()->setCellValue('A2', '=HIPERVINCULO("http://www.google.com/","Google")');
+
+      $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+      $objDrawing->setName('Logo');
+      $objDrawing->setDescription('Logo');
+      $objDrawing->setPath($basePath . '/web/img/logo-salud.png');
+      $objDrawing->setWidth(300);
+      $objDrawing->setCoordinates('A1');
+      $objDrawing->setWorksheet($spreadsheet->getActiveSheet());
+
+      $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+      $objDrawing->setName('Logo');
+      $objDrawing->setDescription('Logo');
+      $objDrawing->setPath($basePath . '/web/img/sa.png');
+      $objDrawing->setWidth(150);
+      $objDrawing->setCoordinates('M1');
+      $objDrawing->setWorksheet($spreadsheet->getActiveSheet());
+      $spreadsheet->getActiveSheet()->setCellValue('N3', " ");
+      $spreadsheet->getActiveSheet()->setCellValue('O3', " ");
+
+      //$spreadsheet->getActiveSheet()->getStyle("A7:" . $ultima_letra . "7")->applyFromArray($style_titulo_etiquestas);
+    }
+
+
+    // Redirect output to a client's web browser (Xlsx)
+    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+    header('Content-Disposition: attachment;filename="' . $nombre . '"');
+    header('Cache-Control: max-age=0');
+
+    $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
+    $writer->save('php://output');
+    exit;
+  }
+
+  public function excelMir($datos = []) {
+    // Create new Spreadsheet object
+    $nombre = "excels.xlsx";
+    $titulo = "Matriz de Indicadores para Resultados (2022)";
+    $pestania = "pestania";
+
+    $spreadsheet = new Spreadsheet();
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getSecurity()->setLockWindows(false);
+    $spreadsheet->getSecurity()->setLockStructure(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSheet(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setSort(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setInsertRows(false);
+    $spreadsheet->getActiveSheet()->getProtection()->setFormatCells(false);
+
+    // Set document properties
+    $spreadsheet->getProperties()->setCreator('pbr')->setLastModifiedBy('pbr')->setTitle($titulo)->setDescription($titulo);
+
+    $spreadsheet->getActiveSheet()->setTitle($pestania);
+    $style_titulo = [
+      'font' => [
+        'bold' => true,
+        'size' => 13,
+      ],
+      'alignment' => [
+        'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+      ]
+    ];
+
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getActiveSheet()->setCellValue("A8", "Dependencia y/o Entidad:");
+    $spreadsheet->getActiveSheet()->setCellValue("A9", "Programa Presupuestario:");//Eje De PED:
+    $spreadsheet->getActiveSheet()->setCellValue("A10", "Eje De PED:");//:
+    $spreadsheet->getActiveSheet()->setCellValue("A11", "Objetivo del PED:");
+    $spreadsheet->getActiveSheet()->setCellValue("A12", "Beneficiarios:");
+
+    // Rename worksheet
+    // Set active sheet index to the first sheet, so Excel opens this as the first sheet
+    $spreadsheet->setActiveSheetIndex(0);
+
+    $spreadsheet->getActiveSheet()->mergeCells('F4:K4');
+    $spreadsheet->getActiveSheet()->getStyle('F4:K4')->applyFromArray($style_titulo);
+    $spreadsheet->getActiveSheet()->setCellValue('F4', $titulo);
+    $losbenificiarios = "";
+    if (count($datos["benificiarios"]) > 0) {
+      foreach ($datos["benificiarios"] as $ben) {
+        $losbenificiarios .= "{$ben->beneficiario->nombre}, ";
+      }
+    }
+
+    $spreadsheet->setActiveSheetIndex(0);
+    $spreadsheet->getActiveSheet()->setCellValue("C8", $datos["dependencia"]->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C9", $datos["programa"]->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C10", $datos["eje"]->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C11", $datos["mir"]->objetivo->nombre);
+    $spreadsheet->getActiveSheet()->setCellValue("C12", $losbenificiarios);
+
+    $spreadsheet->getActiveSheet()->setCellValue("A14", "");
+    $spreadsheet->getActiveSheet()->setCellValue("B14", "Resumen Narrativo\n(Objetivos)");
+    $spreadsheet->getActiveSheet()->mergeCells('B14:B15');
+    $spreadsheet->getActiveSheet()->setCellValue("C14", "Indicadores");
+    $spreadsheet->getActiveSheet()->mergeCells('C14:E14');
+
+    $spreadsheet->getActiveSheet()->setCellValue("F14", "Programación");
+    $spreadsheet->getActiveSheet()->mergeCells('F14:J14');
+    $spreadsheet->getActiveSheet()->setCellValue("K14", "Meta % de Anua");
+    $spreadsheet->getActiveSheet()->setCellValue("L14", "% de Avance");
+    $spreadsheet->getActiveSheet()->setCellValue("M14", "Linea Base (año base)");
+    $spreadsheet->getActiveSheet()->setCellValue("N14", "Sentido");
+    $spreadsheet->getActiveSheet()->setCellValue("O14", "Frecuencia");
+    $spreadsheet->getActiveSheet()->setCellValue("P14", "Medios de Verificación\n(Fuentes)");
+    $spreadsheet->getActiveSheet()->setCellValue("Q14", "Supuestos");
+    $spreadsheet->getActiveSheet()->mergeCells('K14:K15');
+    $spreadsheet->getActiveSheet()->mergeCells('L14:L15');
+    $spreadsheet->getActiveSheet()->mergeCells('M14:M15');
+    $spreadsheet->getActiveSheet()->mergeCells('N14:N15');
+    $spreadsheet->getActiveSheet()->mergeCells('O14:O15');
+    $spreadsheet->getActiveSheet()->mergeCells('P14:P15');
+    $spreadsheet->getActiveSheet()->mergeCells('Q14:Q15');
+
+    $spreadsheet->getActiveSheet()->getStyle('A14:Q14')->applyFromArray($style_titulo);
+    $spreadsheet->getActiveSheet()->getStyle('A15:Q15')->applyFromArray($style_titulo);
+    $spreadsheet->getActiveSheet()->setCellValue("C15", "Nombre");
+    $spreadsheet->getActiveSheet()->setCellValue("D15", "Unidad de\nMedida");
+    $spreadsheet->getActiveSheet()->setCellValue("E15", "Fórmula");
+    $spreadsheet->getActiveSheet()->setCellValue("F15", "I");
+    $spreadsheet->getActiveSheet()->setCellValue("G15", "II");
+    $spreadsheet->getActiveSheet()->setCellValue("H15", "III");
+    $spreadsheet->getActiveSheet()->setCellValue("I15", "IV");
+    $spreadsheet->getActiveSheet()->setCellValue("J15", "Avance\nAcumulado");
+
+    $objDrawing = $this->cargarImagen($spreadsheet, '/web/img/logo-salud.png', 300, "A1");
+    $objDrawing = $this->cargarImagen($spreadsheet, '/web/img/sa.png', 150, "Q5");
+    $spreadsheet->getActiveSheet()->setCellValue('R3', " ");
+
+    $i = 16;
+    if ($datos["mir"] != null) {
+      $nuevoNiveles = [];
+      foreach ($datos["niveles"] as $nivel) {
+        if (!isset($nuevoNiveles[$nivel->nivel])) {
+          $nuevoNiveles[$nivel->nivel] = [];
+        }
+        $nuevoNiveles[$nivel->nivel][] = $nivel;
+      }
+
+      $ordenNivel = [
+        "FIN",
+        "PROPÓSITO",
+        "COMPONENTE",
+        "ACTIVIDAD"
+      ];
+
+      foreach ($ordenNivel as $k => $v) {
+        $inicio = $i;
+        $spreadsheet->getActiveSheet()->setCellValue("A" . $i, $v);
+        $fin = 0;
+        foreach ($nuevoNiveles[$v] as $nivel) {
+          $spreadsheet->getActiveSheet()->setCellValue("B" . $i, $nivel->resumen);
+          foreach ($nivel->matrizMIRIndicadores as $indicador) {
+            $spreadsheet->getActiveSheet()->setCellValue("C" . $i, $indicador->nombre);
+            $spreadsheet->getActiveSheet()->setCellValue("D" . $i, $indicador->unidadMedida->nombre);
+            $spreadsheet->getActiveSheet()->setCellValue("E" . $i, $indicador->numerador);
+            $spreadsheet->getActiveSheet()->setCellValue("E" . ($i + 1), $indicador->denominador);
+            $spreadsheet->getActiveSheet()->setCellValue("F" . $i, $indicador->numeradorT1);
+            $spreadsheet->getActiveSheet()->setCellValue("G" . $i, $indicador->numeradorT2);
+            $spreadsheet->getActiveSheet()->setCellValue("H" . $i, $indicador->numeradorT3);
+            $spreadsheet->getActiveSheet()->setCellValue("I" . $i, $indicador->numeradorT4);
+
+            $spreadsheet->getActiveSheet()->setCellValue("F" . ($i + 1), $indicador->denominadorT1);
+            $spreadsheet->getActiveSheet()->setCellValue("G" . ($i + 1), $indicador->denominadorT2);
+            $spreadsheet->getActiveSheet()->setCellValue("H" . ($i + 1), $indicador->denominadorT3);
+            $spreadsheet->getActiveSheet()->setCellValue("I" . ($i + 1), $indicador->denominadorT4);
+
+            $spreadsheet->getActiveSheet()->setCellValue("J" . $i, $indicador->avanceAcumulado);
+            $spreadsheet->getActiveSheet()->setCellValue("K" . $i, $indicador->metaAnual);
+            $spreadsheet->getActiveSheet()->setCellValue("L" . $i, $indicador->porcentajeAvance);
+            $spreadsheet->getActiveSheet()->setCellValue("M" . $i, $indicador->lineaBase);
+            $spreadsheet->getActiveSheet()->setCellValue("N" . $i, $indicador->sentido->valor);
+            $spreadsheet->getActiveSheet()->setCellValue("O" . $i, $indicador->frecuencia->nombre);
+            $spreadsheet->getActiveSheet()->setCellValue("P" . $i, $indicador->metodoVerificacion);
+            $spreadsheet->getActiveSheet()->setCellValue("Q" . $i, $indicador->supuestos);
+            $spreadsheet->getActiveSheet()->setCellValue("R" . $i, " ");
+            $i += 2;
+          }
+          //$spreadsheet->getActiveSheet()->mergeCells("B{$inicio}:B{$f}");
+        }
+        //$fin = $i+1;
+        $spreadsheet->getActiveSheet()->mergeCells("A{$inicio}:A17");
+      }
+    }
+    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+    header('Content-Disposition: attachment;filename="' . $nombre . '"');
+    header('Cache-Control: max-age=0');
+
+    $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
+    $writer->save('php://output');
+    exit;
+  }
+
+  function cargarImagen($spreadsheet, $imagen = "/web/img/logo-salud.png", $width = 300, $ubicacion = "A1") {
+    $basePath = \Yii::getAlias("@app");
+    $objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+    $objDrawing->setName('Logo');
+    $objDrawing->setDescription('Logo');
+    $objDrawing->setPath($basePath . $imagen);
+    $objDrawing->setWidth($width);
+    $objDrawing->setCoordinates($ubicacion);
+    $objDrawing->setWorksheet($spreadsheet->getActiveSheet());
+  }
+}

+ 37 - 0
modules/publico/Module.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace app\modules\publico;
+
+use Yii;
+
+/**
+ * v1 module definition class
+ */
+class Module extends \yii\base\Module {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $controllerNamespace = 'app\modules\publico\controllers';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    parent::init();
+    $response = Yii::$app->getResponse();
+    $headers = $response->getHeaders();
+    
+    $headers->set('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Headers', 'Content-Type,Accept,Authorization');
+    $headers->set('Access-Control-Allow-Origin', '*');
+    $headers->set('Access-Control-Request-Method', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Credentials', 'true');
+    $headers->set('Access-Control-Max-Age', 86400);
+    if (Yii::$app->getRequest()->isOptions) {
+      Yii::$app->end();
+    } // */
+    Yii::$app->getUser()->enableSession = false;
+  }
+
+}

+ 170 - 0
modules/publico/controllers/RecuperarContrasenaController.php

@@ -0,0 +1,170 @@
+<?php
+
+namespace app\modules\publico\controllers;
+
+use app\modules\mail\models\NotificacionCorreo;
+use common\data\Respuesta;
+use common\rest\JsonController;
+use DateTime;
+use DateTimeZone;
+use v1\models\RecuperarContrasena;
+use v1\models\Sesion;
+use v1\models\Usuario;
+use yii\db\Expression;
+use yii\web\Session;
+
+class RecuperarContrasenaController extends JsonController {
+
+	public $modelClass = 'v1\models\RecuperarContrasena';
+
+	public function actionIndex() {
+		$correo = trim($this->req->getBodyParam("correo", ""));
+		$usuario = null;
+		if ($correo !== "") {
+			$usuario = Usuario::find()->andWhere(["correo" => $correo])->one();
+		}
+
+		if ($usuario === null) {
+			return (new Respuesta())
+				->esError()
+				->mensaje('No se ha encontrado el Usuario, favor de verificar el correo.');
+		}
+		try {
+			$recuperarContrasena = new RecuperarContrasena();
+			$recuperarContrasena->uuid();
+			$recuperarContrasena->idUsuario = $usuario->id;
+			$recuperarContrasena->token = mt_rand(10000000, 99999999);
+			$recuperarContrasena->creado = new Expression('now()');
+
+			$recuperarContrasena->load($this->req->getBodyParams(), '');
+
+			if (!$recuperarContrasena->save()) {
+				return (new Respuesta())
+					->esError()
+					->mensaje('No fue posible crear el código de recuperación');
+			}
+
+			$tz = new DateTimeZone('America/Hermosillo');
+			$fechaNotificacion = new DateTime();
+			$fechaNotificacion->setTimezone($tz);
+			$fechaNotificacion = $fechaNotificacion->format('d/m/Y H:i a');
+
+			$parametros = [
+				"prioridad" => NotificacionCorreo::PRIORIDAD_1,
+				"asunto" => "Notificación SIISTAI Recuperar Contraseña: " . $fechaNotificacion,
+				"cuerpo" => $this->renderPartial('correo', ["datos" => $recuperarContrasena, "usuario" => $usuario]),
+				"receptores" => [$correo],
+				"adjuntos" => []
+			];
+			$resultado = NotificacionCorreo::enviarMultiple($parametros);
+
+			$recuperarContrasena->refresh();
+			return (new Respuesta())
+				->mensaje("Se ha enviado un correo con los datos necesarios para recuperar su contraseña.");
+		} catch (\Exception $e) {
+			return (new Respuesta())
+				->esError()
+				->mensaje($e->getMessage());
+		}
+	}
+
+	public function actionVerificar() {
+		$correo = trim($this->req->get("correo", ""));
+		$token = intval($this->req->get("token", 0));
+		$usuario = null;
+
+		if ($correo !== "") {
+			$usuario = Usuario::find()->andWhere(["correo" => $correo])->one();
+		}
+
+		if ($token !== 0) {
+			$encontrarToken = RecuperarContrasena::find()
+				->andWhere([
+					'idUsuario' => $usuario->id,
+					'token' => $token,
+				])
+				->andWhere(['is not', 'utilizado', null]);
+
+			if ($encontrarToken->exists()) {
+				return (new Respuesta())
+					->esError()
+					->mensaje('Este token ya fue utilizado');
+			}
+		}
+
+		if ($usuario !== null) {
+
+			$recuperarContrasena = RecuperarContrasena::find()
+				->andWhere([
+					"idUsuario" => $usuario->id,
+					"token" => $token,
+					'utilizado' => null
+				])
+				->one();
+
+			if ($recuperarContrasena !== null) {
+				return (new Respuesta())
+					->mensaje('Token válido');
+			} else {
+				return (new Respuesta())
+					->esError()
+					->mensaje('El Token parece no coincidir, favor de revisar');
+			}
+		} else {
+			return (new Respuesta())
+				->esError()
+				->mensaje('Ha ocurrido un error');
+		}
+	}
+
+	public function actionCambiar() {
+		$correo = trim($this->req->getBodyParam("correo", ""));
+		$token = intval($this->req->getBodyParam("token", null));
+		$pwd = trim($this->req->getBodyParam("pwd", ""));
+		$usuario = null;
+
+		if ($correo !== "") {
+			$usuario = Usuario::find()->andWhere(["correo" => $correo])->one();
+		}
+
+		/** @var \v1\models\Usuario $usuario */
+		if ($usuario !== null) {
+			$validarToken = RecuperarContrasena::find()
+				->andWhere([
+					'token' => $token,
+					'idUsuario' => $usuario->id
+				])
+				->one();
+
+			if ($validarToken !== null) {
+				$usuario->agregarClave($pwd);
+				$usuario->modificado = new Expression('now()');
+				if (!$usuario->save()) {
+					return (new Respuesta($usuario))
+						->mensaje("Ocurrió un error al guardar al recuperar su contraseña, favor de intentarlo de nuevo");
+				}
+
+				$validarToken->utilizado = new Expression('now()');
+				if (!$validarToken->save()) {
+					return (new Respuesta($usuario))
+						->mensaje("Ocurrió un error al guardar al recuperar su contraseña, favor de intentarlo de nuevo");
+				}
+
+				$sesion = Sesion::find()
+					->andWhere([
+						'correo' => $correo,
+						'eliminado' => null
+					])
+					->one();
+
+				$usuario->refresh();
+				return (new Respuesta($sesion))
+					->mensaje("Contraseña actualizada");
+			} else {
+				return (new Respuesta())
+					->esError()
+					->mensaje('Error al intentar cambiar su contraseña, favor de intentar de nuevo');
+			}
+		}
+	}
+}

+ 59 - 0
modules/publico/controllers/SolicitudAcuseController.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace app\modules\publico\controllers;
+
+use common\data\Respuesta;
+use common\rest\JsonController;
+use v1\models\SolicitudAcusePersonas;
+use yii\db\Expression;
+
+class SolicitudAcuseController extends JsonController {
+
+	public $modelClass = 'v1\models\SolicitudAcuse';
+
+	public function actionIndex() {
+		$token = trim($this->req->get("token", ""));
+
+		if ($token === '') {
+			return (new Respuesta())
+				->esError(401)
+				->mensaje("No se encontró el Acuse esperado.");
+		}
+
+		$query = $this->queryInicial
+			->innerJoin('SolicitudAcusePersonas', '{{SolicitudAcuse}}.id = {{SolicitudAcusePersonas}}.[[idSolicitud]]	')
+			->andWhere(['tokenRecepcion' => $token]);
+
+		return new Respuesta($query, $this->limite, $this->pagina, $this->ordenar);
+	}
+
+	public function actionConfirmar() {
+		$token = trim($this->req->getBodyParam("token", ""));
+		$acuse = null;
+
+		if ($token !== "") {
+			$acuse = SolicitudAcusePersonas::find()
+				->andWhere(['tokenRecepcion' => $token])
+				->one();
+		}
+
+		try {
+			if ($acuse !== null) {
+				$acuse->confirmado = new Expression('now()');
+				$acuse->load($this->req->getBodyParams(), '');
+				if (!$acuse->save()) {
+					return (new Respuesta())->esError()->mensaje("No se pudo confirmar");
+				}
+			}
+			$acuse->refresh();
+		} catch (\Exception $e) {
+			return (new Respuesta())
+				->esError()
+				->mensaje("Hubo un error en el servidor");
+		}
+
+
+		return (new Respuesta($acuse))
+			->mensaje("Se ha confirmado el Acuse.");
+	}
+}

+ 72 - 0
modules/publico/views/recuperar-contrasena/correo.php

@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @var \v1\models\RecuperarContrasena $datos
+ * @var \v1\models\Usuario $usuario
+ * 
+ */
+
+setlocale(LC_ALL, 'es_Es');
+$basePath = \Yii::getAlias('@app') . "/web/";
+?>
+
+<style type="text/css">
+	.cuerpo {
+		font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+		display: flex;
+		justify-content: center;
+		flex-direction: column;
+	}
+
+	.titulo {
+		margin: 1rem !important;
+		text-align: center;
+		width: 100%;
+	}
+
+	.visualizar-acuse {
+		align-self: center;
+		background-color: #863695;
+		border-radius: .5rem;
+		text-align: center;
+		color: white;
+		font-size: large;
+		font-weight: bolder;
+		text-decoration: none;
+		padding: 1rem;
+		width: 50%;
+	}
+
+	.informacion {
+		align-self: center;
+		text-align: center;
+		margin: 1rem;
+		font-weight: bold;
+	}
+
+	.fecha {
+		margin: .5rem;
+		align-self: center;
+		font-weight: bold;
+	}
+</style>
+
+<div style="font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;">
+	<h3>
+		Hola,<?= $usuario->nombre ?> Detectamos una petición para cambiar su contraseña
+	</h3>
+	<p>
+		En caso de que usted no haya hecho la petición favor de ingorar el mensaje
+	</p>
+	<h2>
+		<?= $datos->token ?>
+	</h2>
+	<p style="font-weight:bold; text-align:center;">
+		<?php $tz = new DateTimeZone('America/Hermosillo');
+		$fechaNotificacion = new DateTime();
+		$fechaNotificacion->setTimezone($tz);
+		$fechaNotificacion = $fechaNotificacion->format('d/m/Y H:i a');
+		echo $fechaNotificacion
+		?>
+	</p>
+</div>

+ 38 - 0
modules/v1/Module.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace v1;
+
+use Yii;
+
+/**
+ * v1 module definition class
+ */
+class Module extends \yii\base\Module {
+
+  /**
+   * {@inheritdoc}
+   */
+  public $controllerNamespace = 'v1\controllers';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    parent::init();
+    $response = Yii::$app->getResponse();
+    $headers = $response->getHeaders();
+
+    $headers->set('Access-Control-Allow-Methods', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Headers', 'Content-Type,Accept,Authorization,X-Requested-With');
+    $headers->set('Access-Control-Allow-Origin', '*');
+    $headers->set('Access-Control-Request-Method', 'POST, GET, DELETE, PUT, OPTIONS');
+    $headers->set('Access-Control-Allow-Credentials', 'true');
+    $headers->set('Access-Control-Max-Age', 86400);
+    if (Yii::$app->getRequest()->isOptions) {
+      Yii::$app->end();
+    } // */
+    Yii::$app->getUser()->enableSession = false;
+    Yii::$app->getUser()->identityClass = 'common\models\Usuario';
+  }
+
+}

+ 194 - 0
modules/v1/controllers/AclaracionController.php

@@ -0,0 +1,194 @@
+<?php
+
+namespace v1\controllers;
+
+use app\models\Folio;
+use common\data\Respuesta;
+use common\rest\AuthController;
+use v1\models\AclaracionMedia;
+use v1\models\BitacoraEstatus;
+use v1\models\Estatus;
+use v1\models\MediaBitacoraEstatus;
+use v1\models\Solicitud;
+use v1\models\SolicitudMedia;
+use v1\models\SujetoObligado;
+use yii\db\Expression;
+
+class AclaracionController extends AuthController {
+
+  public $modelClass = "v1\models\Aclaracion";
+
+  public function actionIndex() {
+    $id = trim($this->req->get("id", ""));
+    $idSolicitud = trim($this->req->get("idSolicitud", ""));
+    $buscar = trim($this->req->get("q", ""));
+
+    $query = $this->queryInicial
+      ->innerJoin('Solicitud', '{{Solicitud}}.id = {{Aclaracion}}.[[idSolicitud]]');
+
+    if ($id !== "") {
+      $query->andWhere(["{{Aclaracion}}.id" => $id]);
+    }
+
+    if ($idSolicitud !== "") {
+      $query->andWhere(["{{Aclaracion}}.[[idSolicitud]]" => $idSolicitud]);
+    }
+
+    if ($buscar) {
+      $query->andWhere([
+        "OR",
+        "f_unaccent({{Solicitud}}.[[leyenda]]) ilike f_unaccent(:q)",
+      ])->addParams([':q' => "%{$buscar}%"]);
+    }
+
+    return new Respuesta($query, $this->limite, $this->pagina, $this->ordenar);
+  }
+
+  public function actionGuardar() {
+    $id = trim($this->req->getBodyParam("id", ""));
+    $idSolicitud = trim($this->req->getBodyParam("idSolicitud", ""));
+    $valorAclaracion = intval($this->req->getBodyParam('valorAclaracion', ''));
+    $archivos = $this->req->getBodyParam("archivos", []);
+    $idSolicitaAclaracion = trim($this->req->getBodyParam("idSolicitaAclaracion", ""));
+    $idAclarador = trim($this->req->getBodyParam("idAclarador", ""));
+    $tipo = trim($this->req->getBodyParam("tipo", ""));
+
+    $usuario = $this->usuario;
+
+    $estatus = Estatus::find()
+      ->andWhere(['eliminado' => null]);
+
+    $modelo = null;
+    if ($id !== "") {
+      $modelo = $this->modelClass::findOne($id);
+    }
+
+    $tran = \Yii::$app->getDb()->beginTransaction();
+    try {
+      if ($modelo === null) {
+        $modelo = new $this->modelClass();
+        $modelo->uuid();
+        $modelo->creado = new Expression('now()');
+      } else {
+        $modelo->modificado = new Expression('now()');
+      }
+
+      $modelo->load($this->req->getBodyParams(), '');
+
+      if ($idSolicitaAclaracion !== "") {
+        $modelo->solicitado = new Expression('now()');
+      }
+
+      if ($idAclarador !== "") {
+        $modelo->aclarado = new Expression('now()');
+      }
+
+      if (!$modelo->save()) {
+        $tran->rollBack();
+        return (new Respuesta($modelo))
+          ->mensaje("Hubo un problema al guardar el registro de la solicitud");
+      }
+
+      $solicitud = Solicitud::findOne($idSolicitud);
+
+      $solicitud->aclaracion = $valorAclaracion;
+
+      $bitacora = new BitacoraEstatus();
+      $bitacora->uuid();
+      $bitacora->idSolicitud = $idSolicitud;
+      $bitacora->estatusInicial = $solicitud->idEstatus;
+      $bitacora->fechaCambio = new Expression('now()');
+      $bitacora->creado = new Expression('now()');
+      $bitacora->idUsuario = $usuario->id;
+
+      if ($idSolicitaAclaracion !== "") {
+        $estatus = Estatus::find()
+          ->andWhere(['nombre' => Estatus::ESTATUS_ACLARACION])
+          ->one();
+        $bitacora->comentario = $modelo->solicitudAclaracion;
+      }
+
+      if ($idAclarador !== "") {
+        $estatus = Estatus::find()
+          ->andWhere(['nombre' => Estatus::ESTATUS_ACLARADO])
+          ->one();
+        $bitacora->comentario = $modelo->aclaracion;
+      }
+
+      $bitacora->estatusFinal = $estatus->id;
+      if (!$bitacora->save()) {
+        $tran->rollBack();
+        return (new Respuesta($bitacora))
+          ->mensaje("Hubo un problema al guardar el registro de la solicitud");
+      }
+
+      $bitacora->refresh();
+
+      $solicitud->idEstatus = $estatus->id;
+
+      $solicitud->save();
+
+      foreach ($archivos as $archivo) {
+        $media = new AclaracionMedia();
+
+        $media->idMedia = $archivo['id'];
+        $media->idAclaracion = $modelo->id;
+        $media->tipo = $tipo;
+        $media->creado = new Expression('now()');
+
+        if (!$media->save()) {
+          $tran->rollBack();
+          return (new Respuesta($media))
+            ->mensaje("Hubo un problema al guardar el registro de la solicitud");
+        }
+
+        $mediaBitacora = new MediaBitacoraEstatus();
+
+        $mediaBitacora->uuid();
+        $mediaBitacora->idMedia = $archivo['id'];
+        $mediaBitacora->idBitacoraEstatus = $bitacora->id;
+        $mediaBitacora->creado = new Expression('now()');
+
+        if (!$mediaBitacora->save()) {
+          $tran->rollBack();
+          return (new Respuesta($mediaBitacora))
+            ->mensaje("Hubo un problema al guardar el registro de la solicitud");
+        }
+      }
+
+      $tran->commit();
+      $modelo->refresh();
+
+      return (new Respuesta($modelo))
+        ->mensaje("Registro de solicitud guardado con éxito.");
+    } catch (\Exception $e) {
+      $tran->rollBack();
+      return (new Respuesta())
+        ->esError($e)
+        ->mensaje("Hubo un error en el servidor");
+    }
+  }
+
+  public function actionEliminar() {
+    $id = intval($this->req->getBodyParam("id", null));
+    $modelo = null;
+
+    if ($id !== "") {
+      $modelo = $this->modelClass::findOne(["id" => $id]);
+    }
+    if ($modelo === null) {
+      return (new Respuesta())
+        ->esError()
+        ->mensaje("Registro de solicitud no encontrado");
+    }
+
+    $modelo->eliminado = new Expression('now()');
+    if (!$modelo->save()) {
+      return (new Respuesta($modelo))
+        ->mensaje("No se pudo eliminar el registro del solicitud");
+    }
+
+    return (new Respuesta())
+      ->mensaje("Registro de solicitud eliminado");
+  }
+}

+ 95 - 0
modules/v1/controllers/AclaracionMediaController.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace v1\controllers;
+
+use common\data\Respuesta;
+use common\rest\AuthController;
+use yii\db\Expression;
+
+class AclaracionMediaController extends AuthController {
+
+  public $modelClass = "v1\models\AclaracionMedia";
+
+  public function actionIndex() {
+    $id = intval($this->req->get("id", ""));
+    $idAclaracion = trim($this->req->get("idAclaracion", ""));
+    $buscar = trim($this->req->get("q", ""));
+
+    $query = $this->queryInicial;
+
+    if ($id > 0) {
+      $query->andWhere(["id" => $id]);
+    }
+
+    if ($idAclaracion !== '') {
+      $query->andWhere(['idAclaracion' => $idAclaracion]);
+    }
+
+    if ($buscar) {
+      $query->andWhere([
+        "OR",
+        "f_unaccent([[leyenda]]) ilike f_unaccent(:q)",
+      ])->addParams([':q' => "%{$buscar}%"]);
+    }
+
+    return new Respuesta($query, $this->limite, $this->pagina, $this->ordenar);
+  }
+
+  public function actionGuardar() {
+    $id = trim($this->req->getBodyParam("id", ""));
+
+    $modelo = null;
+    if ($id !== "") {
+      $modelo = $this->modelClass::findOne($id);
+    }
+
+    $tran = \Yii::$app->getDb()->beginTransaction();
+    try {
+      if ($modelo === null) {
+        $modelo = new $this->modelClass();
+        $modelo->creado = new Expression('now()');
+      } else {
+        $modelo->modificado = new Expression('now()');
+      }
+
+      if (!$modelo->save()) {
+        return (new Respuesta($modelo))
+          ->mensaje("Hubo un problema al guardar el registro del estatus");
+      }
+
+      $tran->commit();
+      $modelo->refresh();
+
+      return (new Respuesta($modelo))
+        ->mensaje("Registro de estatus guardado con éxito.");
+    } catch (\Exception $e) {
+      $tran->rollBack();
+      return (new Respuesta())
+        ->esError($e)
+        ->mensaje("Hubo un error en el servidor");
+    }
+  }
+
+  public function actionEliminar() {
+    $id = intval($this->req->getBodyParam("id", null));
+    $modelo = null;
+
+    if ($id !== "") {
+      $modelo = $this->modelClass::findOne(["id" => $id]);
+    }
+    if ($modelo === null) {
+      return (new Respuesta())
+        ->esError()
+        ->mensaje("Registro de estatus no encontrado");
+    }
+
+    $modelo->eliminado = new Expression('now()');
+    if (!$modelo->save()) {
+      return (new Respuesta($modelo))
+        ->mensaje("No se pudo eliminar el registro del estatus");
+    }
+
+    return (new Respuesta())
+      ->mensaje("Registro de estatus eliminado");
+  }
+}

+ 96 - 0
modules/v1/controllers/BitacoraEstatusController.php

@@ -0,0 +1,96 @@
+<?php
+
+namespace v1\controllers;
+
+use common\data\Respuesta;
+use common\rest\AuthController;
+use yii\db\Expression;
+
+class BitacoraEstatusController extends AuthController {
+
+	public $modelClass = "v1\models\BitacoraEstatus";
+
+	public function actionIndex() {
+		$id = trim($this->req->get("id", ""));
+		$idSolicitud = trim($this->req->get("idSolicitud", ""));
+		$buscar = trim($this->req->get("q", ""));
+
+		$query = $this->queryInicial;
+
+		if ($id !== "") {
+			$query->andWhere(["id" => $id]);
+		}
+
+		if ($idSolicitud !== "") {
+			$query->andWhere(["idSolicitud" => $idSolicitud]);
+		}
+
+		if ($buscar) {
+			// $query->andWhere([
+			// 	"OR",
+			// 	"f_unaccent([[nombre]]) ilike f_unaccent(:q)",
+			// ])->addParams([':q' => "%{$buscar}%"]);
+		}
+
+		return new Respuesta($query, $this->limite, $this->pagina, $this->ordenar);
+	}
+
+	public function actionGuardar() {
+		$id = trim($this->req->getBodyParam("id", ""));
+
+		$modelo = null;
+		if ($id !== "") {
+			$modelo = $this->modelClass::findOne($id);
+		}
+
+		$tran = \Yii::$app->getDb()->beginTransaction();
+		try {
+			if ($modelo === null) {
+				$modelo = new $this->modelClass();
+				$modelo->creado = new Expression('now()');
+			} else {
+				$modelo->modificado = new Expression('now()');
+			}
+
+			if (!$modelo->save()) {
+				return (new Respuesta($modelo))
+					->mensaje("Hubo un problema al guardar el registro del Estado");
+			}
+
+			$tran->commit();
+			$modelo->refresh();
+
+			return (new Respuesta($modelo))
+				->mensaje("Registro de Estado guardado con éxito.");
+		} catch (\Exception $e) {
+			$tran->rollBack();
+			return (new Respuesta())
+				->esError($e)
+				->mensaje("Hubo un error en el servidor");
+		}
+	}
+
+	public function actionEliminar() {
+		$id = intval($this->req->getBodyParam("id", null));
+		$modelo = null;
+
+		if ($id !== "") {
+			$modelo = $this->modelClass::findOne(["id" => $id]);
+		}
+		if ($modelo === null) {
+			return (new Respuesta())
+				->esError()
+				->mensaje("Registro de Estado no encontrado");
+		}
+
+		$modelo->eliminado = new Expression('now()');
+		if (!$modelo->save()) {
+			return (new Respuesta($modelo))
+				->mensaje("No se pudo eliminar el registro del Estado");
+		}
+
+		return (new Respuesta())
+			->mensaje("Registro de Estado eliminado");
+	}
+
+}

+ 115 - 0
modules/v1/controllers/CalendarioController.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace v1\controllers;
+
+use common\data\Respuesta;
+use common\rest\AuthController;
+use v1\models\Calendario;
+use yii\db\Expression;
+
+class CalendarioController extends AuthController {
+
+	// Establece la clase del modelo a utilizar
+	public $modelClass = "v1\models\Calendario";
+
+	// Acción para obtener una lista de calendarios
+	public function actionIndex() {
+		$id = trim($this->req->get("id", ""));
+		$fecha = trim($this->req->get("fecha", ""));
+
+		$query = $this->queryInicial;
+
+		// Filtrar por ID si se proporciona
+		if ($id !== "") {
+			$query->andWhere(["id" => $id]);
+		}
+
+		// Realizar una búsqueda en el año y fecaha del calendario
+		if ($fecha !== "") {
+			$query->andWhere(["fecha" => $fecha]);
+		}
+
+		// Devolver una respuesta con los resultados de la consulta
+		return new Respuesta($query, $this->limite, $this->pagina, $this->ordenar);
+	}
+
+	// Acción para guardar un calendario
+	public function actionGuardar() {
+		$id = trim($this->req->getBodyParam("id", ""));
+
+		$modelo = null;
+		if ($id !== "") {
+			$modelo = $this->modelClass::findOne($id);
+		}
+
+		$tran = \Yii::$app->getDb()->beginTransaction();
+		try {
+			if ($modelo === null) {
+				$modelo = new $this->modelClass();
+				$modelo->uuid();
+				$modelo->creado = new Expression('now()');
+			} else {
+				$modelo->modificado = new Expression('now()');
+			}
+
+			$modelo->load($this->req->getBodyParams(), "");
+
+			// Guardar el modelo en la base de datos
+			if (!$modelo->save()) {
+				return (new Respuesta($modelo))
+					->mensaje("Hubo un problema al guardar el registro de calendarios");
+			}
+
+			$tran->commit();
+			$modelo->refresh();
+
+			return (new Respuesta($modelo))
+				->mensaje("Registro de calendarios guardado con éxito.");
+		} catch (\Exception $e) {
+			$tran->rollBack();
+			return (new Respuesta())
+				->esError($e)
+				->mensaje("Hubo un error en el servidor");
+		}
+	}
+
+	// Acción para eliminar un calendario
+	public function actionEliminar() {
+		$id = intval($this->req->getBodyParam("id", null));
+		$modelo = null;
+
+		if ($id !== "") {
+			$modelo = $this->modelClass::findOne(["id" => $id]);
+		}
+		if ($modelo === null) {
+			return (new Respuesta())
+				->esError()
+				->mensaje("Registro de calendarios no encontrado");
+		}
+
+		$modelo->eliminado = new Expression('now()');
+		if (!$modelo->save()) {
+			return (new Respuesta($modelo))
+				->mensaje("No se pudo eliminar el registro de calendarios");
+		}
+
+		return (new Respuesta())
+			->mensaje("Registro de calendarios eliminado");
+	}
+
+	// Acción para calcular una fecha a partir de una fecha dada y una cantidad de días
+	public function actionCalcularFecha() {
+		$fechaInicio = trim($this->req->get('fecha', ''));
+		$dias = intval($this->req->get('dias', ''));
+
+		if ($fechaInicio === '') {
+			return (new Respuesta())
+				->esError()
+				->mensaje('Se necesita una fecha de inicio');
+		}
+
+		$fecha = Calendario::calcularDiasHabiles($fechaInicio, $dias);
+
+		return $fecha;
+	}
+}

+ 93 - 0
modules/v1/controllers/CategoriaController.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace v1\controllers;
+
+use common\data\Respuesta;
+use common\rest\AuthController;
+use yii\db\Expression;
+
+class CategoriaController extends AuthController {
+
+  public $modelClass = "v1\models\Categoria";
+
+  public function actionIndex() {
+    $id = trim($this->req->get("id", ""));
+    $buscar = trim($this->req->get("q", ""));
+
+    $query = $this->queryInicial;
+
+    if ($id !== "") {
+      $query->andWhere(["id" => $id]);
+    }
+
+    if ($buscar) {
+      $query->andWhere([
+        "OR",
+        "f_unaccent([[nombre]]) ilike f_unaccent(:q)",
+      ])->addParams([':q' => "%{$buscar}%"]);
+    }
+
+    return new Respuesta($query, $this->limite, $this->pagina, $this->ordenar);
+  }
+
+  public function actionGuardar() {
+    $id = trim($this->req->getBodyParam("id", ""));
+
+    $modelo = null;
+    if ($id !== "") {
+      $modelo = $this->modelClass::findOne($id);
+    }
+
+    $tran = \Yii::$app->getDb()->beginTransaction();
+    try {
+      if ($modelo === null) {
+        $modelo = new $this->modelClass();
+        $modelo -> uuid ();
+        $modelo->creado = new Expression('now()');
+      } else {
+        $modelo->modificado = new Expression('now()');
+      }
+
+      $modelo->load($this->req->getBodyParams(),'');
+
+      if (!$modelo->save()) {
+        return (new Respuesta($modelo))
+          ->mensaje("Hubo un problema al guardar el registro de la Categoría");
+      }
+
+      $tran->commit();
+      $modelo->refresh();
+
+      return (new Respuesta($modelo))
+        ->mensaje("Registro de Categoría guardado con éxito.");
+    } catch (\Exception $e) {
+      $tran->rollBack();
+      return (new Respuesta())
+        ->esError($e)
+        ->mensaje("Hubo un error en el servidor");
+    }
+  }
+
+  public function actionEliminar() {
+    $id = intval($this->req->getBodyParam("id", null));
+    $modelo = null;
+
+    if ($id !== "") {
+      $modelo = $this->modelClass::findOne(["id" => $id]);
+    }
+    if ($modelo === null) {
+      return (new Respuesta())
+        ->esError()
+        ->mensaje("Registro de Categoría no encontrado");
+    }
+
+    $modelo->eliminado = new Expression('now()');
+    if (!$modelo->save()) {
+      return (new Respuesta($modelo))
+        ->mensaje("No se pudo eliminar el registro del Categoría");
+    }
+
+    return (new Respuesta())
+      ->mensaje("Registro de Categoría eliminado");
+  }
+}

+ 0 - 0
modules/v1/controllers/ColeccionPermisoController.php


Some files were not shown because too many files changed in this diff