How to fix the error while saving the image in gallery? - android-studio

I was trying to save an image in a gallery via an app.
this is the error I got
java.lang.IllegalArgumentException: Couldn't find meta-data for a provider with authority com.example.android.fileprovider
I have a button which would capture an image from the camera by calling the below function.
**Code for saving the image **
val REQUEST_TAKE_PHOTO = 1
private fun dispatchTakePictureIntent() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
// Ensure that there's a camera activity to handle the intent
takePictureIntent.resolveActivity(packageManager)?.also {
// Create the File where the photo should go
val photoFile: File? = try {
createImageFile()
} catch (ex: IOException) {
// Error occurred while creating the File
Log.i("Exception","${ex.toString()}")
null
}
// Continue only if the File was successfully created
photoFile?.also {
val photoURI: Uri = FileProvider.getUriForFile(
this,
"com.example.android.fileprovider",
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)
}
}
}
}
lateinit var currentPhotoPath: String
#Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
).apply {
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = absolutePath
}
}
Now I in google docs I found out that we need to add some more lines in Manifest.xml file, so I added them, but still, there is an error
<?XML version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.intentexperiment">
<uses-feature android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SET_ALARM" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"></meta-data>
</provider>
</application>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path
android:name="image" android:path="Android/data/com.example.package.name/files/Pictures" />
</paths>
I don't understand where the error is.
EDIT
I have moved the
<path>
...
</path>
in an XML file and accordingly changed the android: resource attribute
but the error now is
java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.ClassNotFoundException: Didn't find class "android.support.v4.content.FileProvider" on path: DexPathList[[zip file "/data/app/com.example.intentexperiment-l0jZknwE-m3bgGza6RJAug==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.intentexperiment-l0jZknwE-m3bgGza6RJAug==/lib/arm64, /system/lib64]]
EDIT 2
after doing this
android:name="androidx.core.content.FileProvider"
still there is a error
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example.intentexperiment/files/Pictures/JPEG_20200425_194311_3114424866623330911.jpg
at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744)
at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
at com.example.intentexperiment.MainActivity.dispatchTakePictureIntent(MainActivity.kt:54)
at com.example.intentexperiment.MainActivity.access$dispatchTakePictureIntent(MainActivity.kt:22)
at com.example.intentexperiment.MainActivity$onCreate$1.onClick(MainActivity.kt:31)
at android.view.View.performClick(View.java:6600)
at android.view.View.performClickInternal(View.java:6577)
at android.view.View.access$3100(View.java:781)
at android.view.View$PerformClick.run(View.java:25912)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6923)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:870)

Related

Google maps stops the Android Studio application

I am working with google maps, calling the activity from a button, had a problem, the application stops when I try to open the map activity
My manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alwaysecure">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION."/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".MapsActivity"
android:label="#string/title_activity_maps"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-library android:name="org.apache.http.legacy"
android:required="false"/>
</application>
</manifest>
El activity del mapa:
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MapsActivity">
<fragment
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MapsActivity"
tools:layout_editor_absoluteX="-100dp"
tools:layout_editor_absoluteY="212dp" />
</fragment>
The activity that calls the map activity:
package com.example.alwaysecure;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
Button btn_login, btn_registrar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_login = (Button) findViewById(R.id.btn_login);
btn_registrar = (Button) findViewById(R.id.btn_register);
btn_login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(),MapsActivity.class);
startActivity(intent);
}
});
/*btn_registrar.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(),RegistrarActivity.class);
startActivity(intent);
}
});*/
}
}
The error:
07/18 03:04:25: Launching 'app' on Nexus 5 API 24.
$ adb shell am start -n "com.example.alwaysecure/com.example.alwaysecure.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Waiting for process to come online...
Connected to process 27619 on device 'Nexus_5_API_24 [emulator-5554]'.
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
W/System: ClassLoader referenced unknown path: /data/app/com.example.alwaysecure-2/lib/x86
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter androidx.vectordrawable.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
D/: HostConnection::get() New Host Connection established 0xa9876880, tid 27619
D/: HostConnection::get() New Host Connection established 0xa2733000, tid 27642
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
D/OpenGLRenderer: Swap behavior 0
D/EGL_emulation: eglCreateContext: 0xad4850c0: maj 2 min 0 rcv 2
D/EGL_emulation: eglMakeCurrent: 0xad4850c0: ver 2 0 (tinfo 0xad4835e0)
D/EGL_emulation: eglMakeCurrent: 0xad4850c0: ver 2 0 (tinfo 0xad4835e0)
I/zzbz: Making Creator dynamically
I/DynamiteModule: Considering local module com.google.android.gms.maps_dynamite:0 and remote module com.google.android.gms.maps_dynamite:203113001
Selected remote version of com.google.android.gms.maps_dynamite, version >= 203113001
V/DynamiteModule: Dynamite loader version >= 2, using loadModule2NoCrashUtils
W/System: ClassLoader referenced unknown path:
W/ResourceType: ResTable_typeSpec entry count inconsistent: given 67, previously 69
ResTable_typeSpec entry count inconsistent: given 2885, previously 2906
ResTable_typeSpec entry count inconsistent: given 471, previously 3453
W/System: ClassLoader referenced unknown path: /data/user_de/0/com.google.android.gms/app_chimera/m/0000001a/n/x86
I/Google Maps Android API: Google Play services client version: 12451000
I/Google Maps Android API: Google Play services package version: 212418022
I/Google Maps Android API: Google Play services maps renderer version(legacy): 203113001
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.alwaysecure, PID: 27619
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.alwaysecure/com.example.alwaysecure.MapsActivity}: android.view.InflateException: Binary XML file line #12: Binary XML file line #12: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: android.view.InflateException: Binary XML file line #12: Binary XML file line #12: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Caused by: java.lang.IllegalArgumentException: Binary XML file line #12: Duplicate id 0xffffffff, tag null, or parent id 0x7f0900d2 with another fragment for com.google.android.gms.maps.SupportMapFragment
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:116)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:319)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:298)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:777)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:414)
at android.app.Activity.setContentView(Activity.java:2414)
at androidx.activity.ComponentActivity.setContentView(ComponentActivity.java:380)
at com.example.alwaysecure.MapsActivity.onCreate(MapsActivity.java:28)
at android.app.Activity.performCreate(Activity.java:6662)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2599)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Process 27619 terminated.
The project was already finished, but due to a name change it got damaged and started showing this error, I'm doing it again from scratch but it keeps showing me this same error.

Class org.apache.oozie.action.hadoop.SparkMain not found

following are all oozie files which i have been using to run job. I have created folder on hdfs /test/jar and put workflow.xml and coordinator.xml file.
Properties File
nameNode=hdfs://host:8020
jobTracker=host:8050
queueName=default
oozie.use.system.lib.path=trueoozie.coord.application.path=${nameNode}/test/jar/coordinator.xml
oozie.action.sharelib.for.spark=spark2
start=2019-05-22T07:37Z
end=2019-05-22T07:40Z
freq=*/1 * * * *
zone=UTC
user.name=oozie
oozie.action.sharelib.for.spark.exclusion=oozie/jackson
#oozie.libpath=${nameNode}/user/oozie/share/lib
Coordinator File
<coordinator-app xmlns = "uri:oozie:coordinator:0.5" name = "test" frequency = "${freq}" start = "${start}" end = "${end}" timezone = "${zone}">
<controls>
<timeout>1</timeout>
</controls>
<action>
<workflow>
<app-path>${nameNode}/test/jar/workflow.xml</app-path>
</workflow>
</action>
</coordinator-app>
Workflow file
<workflow-app name="sample-wf" xmlns="uri:oozie:workflow:0.5">
<start to="test" />
<action name="test">
<spark xmlns="uri:oozie:spark-action:0.2">
<job-tracker>${jobTracker}</job-tracker>
<name-node>${nameNode}</name-node>
<master>yarn</master>
<mode>cluster</mode>
<name>Spark Example</name>
<class>com.spark.excel.mysql.executor.Executor</class>
<jar>${nameNode}/test/jar/com.spark.excel.mysql-0.1.jar</jar>
<spark-opts>--executor-memory 2G --num-executors 2</spark-opts>
</spark>
<ok to="end"/>
<error to="fail"/>
</action>
<kill name="fail">
<message>Workflow failed, error message [${wf:errorMessage(wf:lastErrorNode())}]</message>
</kill>
<end name="end" />
</workflow-app>
I have setup sharelib path also. Oozie is showing spark2 also through shareliblist and added oozie-sharelib-spark.jar file also in spark2.Ozzie job is submission and running also, but when it try to execute spark job then throughing error.
I had the same error. In my case I had to add in the properties file
oozie.use.system.libpath=true

Mule - Object To XML with JAXB

I'm using Mule 3.8 to get some JSON data which I turn to Java and then to XML files. Everything works until my File endpoint where it all ends in disaster:
Message : Could not find a transformer to transform
"SimpleDataType{type=java.io.ByteArrayOutputStream, mimeType='text/xml',
encoding='null'}" to "SimpleDataType{type=java.io.InputStream,
mimeType='*/*', encoding='null'}".
Payload : <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Header ....></Header>
Payload Type : java.io.ByteArrayOutputStream
..and all i get is dozens of empty .xml files..
I guess somehow I have to tranform my payload to something the file component could actually take and turn into a file, or is this something I have to do manually in a Java component?
Regards
EDIT - forgot the config
<flow name="Product">
<file:inbound-endpoint path="C:\temp\fileIn" responseTimeout="10000" doc:name="File"/>
<logger message="#[payload != null]" level="INFO" doc:name="Logger"/>
<json:json-to-object-transformer returnClass="java.util.List" encoding="UTF-8" doc:name="JSON to ObjectList"/>
<collection-splitter doc:name="Collection Splitter"/>
<logger message="#[payload]" level="INFO" doc:name="Logger"/>
<custom-transformer returnClass="se.131.Product.Header" encoding="UTF-8" class="se.131.Tranformer.Map2Product" doc:name="MapToProduct" mimeType="application/xml"/>
<mulexml:jaxb-object-to-xml-transformer name="myMarshaller" jaxbContext-ref="JAXB_Context" doc:name="JAXB Object to XML" encoding="UTF-8" mimeType="application/xml"/>
<logger message="#[payload]" level="INFO" doc:name="Logger"/>
<file:outbound-endpoint path="C:\temp\fileOut" responseTimeout="10000" doc:name="File" outputPattern="Product-#[function:dateStamp].xml" mimeType="text/xml"/>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<logger message="Oh no!!" level="INFO" doc:name="Logger"/>
</catch-exception-strategy>
</flow>
Try with placing <object-to-string-transformer> just before File outbound endpoint and check ...
The File endpoint expect String format of the payload to create the file

Active Sync Client Sync Message, status code 4 - ParseSyncKey - AirSyncPermanentException

I'm currently writing an active sync client and I've successfully managed to do a folder sync. I'm now attempting to sync the "Notes" folder (using the sync command) and it's giving me an error code 4, which Microsoft describes as "Protocol Error" -- I don't see anything wrong in the protocol that I'm sending, here is the sections from my log:
Folder sync RequestBody :
<?xml version="1.0" encoding="utf-8" ?>
<FolderSync xmlns="FolderHierarchy:">
<SyncKey>0</SyncKey>
</FolderSync>
FolderSync ResponseBody:
<?xml version="1.0" encoding="utf-8" ?>
<FolderSync xmlns="FolderHierarchy:">
<Status>1</Status>
<SyncKey>1</SyncKey>
<Changes>
<Count>12</Count>
<Add>
<ServerId>1</ServerId>
<ParentId>0</ParentId>
<DisplayName>Calendar</DisplayName>
<Type>8</Type>
</Add>
<Add>
<ServerId>2</ServerId>
<ParentId>0</ParentId>
<DisplayName>Contacts</DisplayName>
<Type>9</Type>
</Add>
<Add>
<ServerId>3</ServerId>
<ParentId>0</ParentId>
<DisplayName>Deleted Items</DisplayName>
<Type>4</Type>
</Add>
<Add>
<ServerId>4</ServerId>
<ParentId>0</ParentId>
<DisplayName>Drafts</DisplayName>
<Type>3</Type>
</Add>
<Add>
<ServerId>5</ServerId>
<ParentId>0</ParentId>
<DisplayName>Inbox</DisplayName>
<Type>2</Type>
</Add>
<Add>
<ServerId>6</ServerId>
<ParentId>0</ParentId>
<DisplayName>Journal</DisplayName>
<Type>11</Type>
</Add>
<Add>
<ServerId>7</ServerId>
<ParentId>0</ParentId>
<DisplayName>Junk E-Mail</DisplayName>
<Type>12</Type>
</Add>
<Add>
<ServerId>8</ServerId>
<ParentId>0</ParentId>
<DisplayName>Notes</DisplayName>
<Type>10</Type>
</Add>
<Add>
<ServerId>9</ServerId>
<ParentId>0</ParentId>
<DisplayName>Outbox</DisplayName>
<Type>6</Type>
</Add>
<Add>
<ServerId>10</ServerId>
<ParentId>0</ParentId>
<DisplayName>Sent Items</DisplayName>
<Type>5</Type>
</Add>
<Add>
<ServerId>11</ServerId>
<ParentId>0</ParentId>
<DisplayName>Tasks</DisplayName>
<Type>7</Type>
</Add>
<Add>
<ServerId>RI</ServerId>
<ParentId>0</ParentId>
<DisplayName>RecipientInfo</DisplayName>
<Type>19</Type>
</Add>
</Changes>
</FolderSync>
My Sync RequestBody :
<?xml version="1.0" encoding="utf-8" ?>
<Sync xmlns="AirSync:">
<Collections>
<Collection>
<SyncKey>0</SyncKey>
<CollectionId>8</CollectionId>
<DeletesAsMoves>1</DeletesAsMoves>
<GetChanges/>
</Collection>
</Collections>
</Sync>
Lists this exception:
SyncCommand_OnExecute_Exception :
Microsoft.Exchange.AirSync.AirSyncPermanentException
at Microsoft.Exchange.AirSync.SyncCommand.ParseSyncKey(SyncCollection collection)
at Microsoft.Exchange.AirSync.SyncCommand.SyncTheCollection(SyncCollection collection, Boolean createSubscription, Boolean tryNullSync)
at Microsoft.Exchange.AirSync.SyncCommand.OnExecute()
And provides the following ResponseBody :
<?xml version="1.0" encoding="utf-8" ?>
<Sync xmlns="AirSync:">
<Status>4</Status>
</Sync>
Any ideas why it is failing there? This is the first sync of the folder, so the SyncKey SHOULD be 0...
From http://msdn.microsoft.com/en-us/library/gg675447(v=exchg.80).aspx :
A Status element (section 2.2.3.162.16) value of 4 is returned if the GetChanges element is present and empty or set to 1 (TRUE) when the SyncKey element value is 0 (zero). No error is returned if the GetChanges element is absent or set to 0 (FALSE) when the SyncKey value is 0 (zero).
You have present and empty in your xml.
You only may set GetChanges to 1 or live it empty (which is by default = 1) in any consequential sync request, not in initial sync request.

Modifying a querystring using the IIS URL Rewrite Module

I'm in the process of migrating some Sharepoint sites from one farm to another farm. It's a little more complicated, but for simplicity sake...
What I'd like to maintain is old URLs that people have for these sites, documents, etc. and the IIS URL Rewrite Module seems like a good way to go.
Here's an idea of what the structure is:
_______________________ _______________________
|oldfarm.company.com**| |newfarm.company.com**|
|oldsitecollection** | |newsitecollection** |
|subsitename | |subsitename |
|... | |... |
|_____________________| |_____________________|
** = changes, everything else remains the same, URLwise.
On the "newfarm" I have extended the web application to respond to "oldfarm.company.com", and that web application has a URL Redirect Rule that redirects http://oldfarm.company.com/oldsitecollection/... to http://newfarm.company.com/newsitecollection/...
That works great for the vast majority of what I'm trying to do.
What I'm having difficulty with is rewriting QUERYSTRING values. Sharepoint's Office Document Viewers contain path information in the QUERYSTRING and that's what I need to change.
Here is an original URL sample:
http://oldfarm.company.com/oldsitecollection/subsitename/_layouts/WordViewer.aspx?id=/oldsitecollection/subsitename/doclib/doc.docx&Source=http%3A%2F%2Foldfarm%2Ecompany%2Ecom%2Foldsitecollection%2Fsubsitename%2Fdoclib%2FForms%2FAllItems%2Easpx&DefaultItemOpen=1
Here is the URL after the redirect (and where I'm stuck):
http://newfarm.company.com/newsitecollection/subsitename/_layouts/WordViewer.aspx?id=/oldsitecollection/subsitename/doclib/doc.docx&Source=http%3A%2F%2Foldfarm%2Ecompany%2Ecom%2Foldsitecollection%2Fsubsitename%2Fdoclib%2FForms%2FAllItems%2Easpx&DefaultItemOpen=1
Here is what I need the URL to look like:
http://newfarm.company.com/newsitecollection/subsitename/_layouts/WordViewer.aspx?id=/newsitecollection/subsitename/doclib/doc.docx&Source=http%3A%2F%2Fnewfarm%2Ecompany%2Ecom%2Fnewsitecollection%2Fsubsitename%2Fdoclib%2FForms%2FAllItems%2Easpx&DefaultItemOpen=1
I have tried using Rewrite Maps because these are not dynamic substitutions, but I cannot get them to modify the QUERYSTRING.
Here's an example of the rewrite rule I'm working on:
<rewrite>
<rewriteMaps>
<rewriteMap name="WordViewer">
<add key="id=/oldsitecollection" value="id=/newsitecollection" />
</rewriteMap>
</rewriteMaps>
<rules>
<rule name="Rewrite rule1 for WordViewer">
<match url=".*WordViewer.aspx" />
<conditions>
<add input="{WordViewer:{QUERY_STRING}}" pattern="(.+)" />
</conditions>
<action type="Rewrite" url="{C:1}" appendQueryString="false" />
</rule>
</rules>
</rewrite>
To answer my own question, what worked for me was to create my own custom rewrite provider.
The provider I created was a simple find/replace provider that looked like this:
public class FindReplaceProvider : IRewriteProvider, IProviderDescriptor
{
public string Find { get; private set; }
public string Replace { get; private set; }
public void Initialize(IDictionary<string, string> settings, IRewriteContext rewriteContext)
{
string tmpFind, tmpReplace;
if (!settings.TryGetValue("Find", out tmpFind) || string.IsNullOrEmpty(tmpFind))
throw new ArgumentException("FindReplaceProvider setting 'Find' is required and cannot be empty");
if (!settings.TryGetValue("Replace", out tmpReplace))
throw new ArgumentException("FindReplaceProvider setting 'Replace' is required and cannot be null");
if (!string.IsNullOrEmpty(tmpFind))
Find = tmpFind;
else
throw new ArgumentException("FindReplaceProvider parameter 'Find' cannot be empty");
if (!string.IsNullOrEmpty(tmpReplace))
Replace = tmpReplace;
else
Replace = String.Empty;
}
public string Rewrite(string value)
{
return Regex.Replace(value, Find, Replace, RegexOptions.IgnoreCase);
}
public IEnumerable<SettingDescriptor> GetSettings()
{
yield return new SettingDescriptor("Find", "String to find");
yield return new SettingDescriptor("Replace", "String to replace");
}
}
And my rewrite rules end up looking like this:
<rewrite>
<providers>
<provider name="OfficeWebAppsReplaceId" type="MyFindReplaceProvider">
<settings>
<add key="Find" value="id=/oldsitecollection" />
<add key="Replace" value="id=/newsitecollection" />
</settings>
</provider>
<provider name="OfficeWebAppsReplaceSource" type="MyFindReplaceProvider">
<settings>
<add key="Find" value="http%3A%2F%2Foldfarm%2Ecompany%2Ecom%2Foldsitecollection%2" />
<add key="Replace" value="http%3A%2F%2Fnewfarm%2Ecompany%2Ecom%2Fnewsitecollection%2" />
</settings>
</provider>
</providers>
<rules>
<rule name="OfficeWebAppsQuerystringRedirect" stopProcessing="true">
<match url=".*(WordViewer.aspx|WordEditor.aspx|xlviewer.aspx|PowerPoint.aspx)$" />
<conditions logicalGrouping="MatchAny">
<add input="{QUERY_STRING}" pattern=".*id=/oldsitecollection.+" />
<add input="{QUERY_STRING}" pattern=".*Source=http%3A%2F%2Foldfarm%2Ecompany%2Ecom%2Foldsitecollection%2F.+" />
</conditions>
<action type="Redirect" url="{R:0}?{OfficeWebAppsReplaceId:{OfficeWebAppsReplaceSource:{C:0}}}" appendQueryString="false" redirectType="Temporary" />
</rule>
</rules>
</rewrite>

Resources